From c2c388cfc3258d237493df7e4efdfe2562abe78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Fri, 24 Jul 2009 22:50:06 +0200 Subject: Fix deadlocks caused by input_vdr's discontinuity handling. In trickspeed mode, input_vdr's metronom generates a discontinuity for each video frame. These generated discontinuities could interfere with discontinuities generated by the demuxer. As a result the metronom deadlocked while waiting for a matching discontinuity indication. So care must be taken to not mix internal and external discontinuities. --- src/vdr/input_vdr.c | 142 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 125 insertions(+), 17 deletions(-) diff --git a/src/vdr/input_vdr.c b/src/vdr/input_vdr.c index bd656f3dd..273c2bc87 100644 --- a/src/vdr/input_vdr.c +++ b/src/vdr/input_vdr.c @@ -124,12 +124,16 @@ struct vdr_input_plugin_s uint8_t osd_supports_argb_layer; uint8_t audio_channels; - uint8_t trick_speed_mode; uint8_t mute_mode; uint8_t volume_mode; int last_volume; vdr_frame_size_changed_data_t frame_size; + uint8_t trick_speed_mode; + uint8_t trick_speed_mode_blocked; + pthread_mutex_t trick_speed_mode_lock; + pthread_cond_t trick_speed_mode_cond; + pthread_t rpc_thread; int rpc_thread_shutdown; pthread_mutex_t rpc_thread_shutdown_lock; @@ -812,7 +816,14 @@ static off_t vdr_execute_rpc_command(vdr_input_plugin_t *this) if (this->trick_speed_mode != data->on) { + pthread_mutex_lock(&this->trick_speed_mode_lock); + + while (this->trick_speed_mode_blocked) + pthread_cond_wait(&this->trick_speed_mode_cond, &this->trick_speed_mode_lock); + this->trick_speed_mode = data->on; + + pthread_mutex_unlock(&this->trick_speed_mode_lock); _x_demux_seek(this->stream, 0, 0, 0); @@ -1801,6 +1812,9 @@ static void vdr_plugin_dispose(input_plugin_t *this_gen) pthread_cond_destroy(&this->metronom_thread_request_cond); pthread_cond_destroy(&this->metronom_thread_reply_cond); + pthread_mutex_destroy(&this->trick_speed_mode_lock); + pthread_cond_destroy(&this->trick_speed_mode_cond); + pthread_mutex_destroy(&this->find_sync_point_lock); pthread_mutex_destroy(&this->adjust_zoom_lock); @@ -2089,6 +2103,8 @@ static int vdr_plugin_open_socket_mrl(input_plugin_t *this_gen) return 1; } +static void vdr_metronom_handle_audio_discontinuity_impl(metronom_t *self, int type, int64_t disc_off); + static void *vdr_metronom_thread_loop(void *arg) { vdr_input_plugin_t *this = (vdr_input_plugin_t *)arg; @@ -2104,7 +2120,7 @@ static void *vdr_metronom_thread_loop(void *arg) if (this->metronom_thread_request == -1) run = 0; else - this->metronom.metronom.handle_audio_discontinuity(&this->metronom.metronom, DISC_ABSOLUTE, this->metronom_thread_request); + vdr_metronom_handle_audio_discontinuity_impl(&this->metronom.metronom, DISC_ABSOLUTE, this->metronom_thread_request); this->metronom_thread_request = 0; this->metronom_thread_reply = 1; @@ -2391,7 +2407,7 @@ fprintf(stderr, "B =============================================\n"); } } -static void vdr_metronom_handle_audio_discontinuity(metronom_t *self, int type, int64_t disc_off) +static void vdr_metronom_handle_audio_discontinuity_impl(metronom_t *self, int type, int64_t disc_off) { vdr_metronom_t *this = (vdr_metronom_t *)self; int64_t vpts_offset = vdr_vpts_offset_queue_change_begin(this->input, type); @@ -2401,7 +2417,48 @@ static void vdr_metronom_handle_audio_discontinuity(metronom_t *self, int type, vdr_vpts_offset_queue_change_end(this->input, type, disc_off, vpts_offset); } -static void vdr_metronom_handle_video_discontinuity(metronom_t *self, int type, int64_t disc_off) +static void vdr_metronom_handle_audio_discontinuity(metronom_t *self, int type, int64_t disc_off) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + + pthread_mutex_lock(&this->input->trick_speed_mode_lock); + + if (this->input->trick_speed_mode_blocked & 0x04) /* must not enter while video is leaving */ + pthread_cond_wait(&this->input->trick_speed_mode_cond, &this->input->trick_speed_mode_lock); + + this->input->trick_speed_mode_blocked |= 0x02; /* audio is in */ + + if (!this->input->trick_speed_mode) + { + pthread_mutex_unlock(&this->input->trick_speed_mode_lock); + + vdr_metronom_handle_audio_discontinuity_impl(self, type, disc_off); + + pthread_mutex_lock(&this->input->trick_speed_mode_lock); + } + else + { + if (this->input->trick_speed_mode_blocked != 0x03) /* wait for audio and video in */ + { + pthread_cond_wait(&this->input->trick_speed_mode_cond, &this->input->trick_speed_mode_lock); + this->input->trick_speed_mode_blocked &= ~0x04; /* video left already */ + } + else + { + this->input->trick_speed_mode_blocked |= 0x04; /* audio is leaving */ + pthread_cond_broadcast(&this->input->trick_speed_mode_cond); + } + } + + this->input->trick_speed_mode_blocked &= ~0x02; /* audio is out */ + + if (!this->input->trick_speed_mode_blocked) + pthread_cond_broadcast(&this->input->trick_speed_mode_cond); + + pthread_mutex_unlock(&this->input->trick_speed_mode_lock); +} + +static void vdr_metronom_handle_video_discontinuity_impl(metronom_t *self, int type, int64_t disc_off) { vdr_metronom_t *this = (vdr_metronom_t *)self; int64_t vpts_offset = vdr_vpts_offset_queue_change_begin(this->input, type); @@ -2411,28 +2468,76 @@ static void vdr_metronom_handle_video_discontinuity(metronom_t *self, int type, vdr_vpts_offset_queue_change_end(this->input, type, disc_off, vpts_offset); } +static void vdr_metronom_handle_video_discontinuity(metronom_t *self, int type, int64_t disc_off) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + + pthread_mutex_lock(&this->input->trick_speed_mode_lock); + + if (this->input->trick_speed_mode_blocked & 0x04) /* must not enter while audio is leaving */ + pthread_cond_wait(&this->input->trick_speed_mode_cond, &this->input->trick_speed_mode_lock); + + this->input->trick_speed_mode_blocked |= 0x01; /* video is in */ + + if (!this->input->trick_speed_mode) + { + pthread_mutex_unlock(&this->input->trick_speed_mode_lock); + + vdr_metronom_handle_video_discontinuity_impl(self, type, disc_off); + + pthread_mutex_lock(&this->input->trick_speed_mode_lock); + } + else + { + if (this->input->trick_speed_mode_blocked != 0x03) /* wait for audio and video in */ + { + pthread_cond_wait(&this->input->trick_speed_mode_cond, &this->input->trick_speed_mode_lock); + this->input->trick_speed_mode_blocked &= ~0x04; /* audio left already */ + } + else + { + this->input->trick_speed_mode_blocked |= 0x04; /* video is leaving */ + pthread_cond_broadcast(&this->input->trick_speed_mode_cond); + } + } + + this->input->trick_speed_mode_blocked &= ~0x01; /* video is out */ + + if (!this->input->trick_speed_mode_blocked) + pthread_cond_broadcast(&this->input->trick_speed_mode_cond); + + pthread_mutex_unlock(&this->input->trick_speed_mode_lock); +} + static void vdr_metronom_got_video_frame(metronom_t *self, vo_frame_t *frame) { vdr_metronom_t *this = (vdr_metronom_t *)self; - if (this->input->trick_speed_mode && frame->pts) + if (frame->pts) { - pthread_mutex_lock(&this->input->metronom_thread_call_lock); + pthread_mutex_lock(&this->input->trick_speed_mode_lock); + + if (this->input->trick_speed_mode) + { + pthread_mutex_lock(&this->input->metronom_thread_call_lock); + + pthread_mutex_lock(&this->input->metronom_thread_lock); + this->input->metronom_thread_request = frame->pts; + this->input->metronom_thread_reply = 0; + pthread_cond_broadcast(&this->input->metronom_thread_request_cond); + pthread_mutex_unlock(&this->input->metronom_thread_lock); - pthread_mutex_lock(&this->input->metronom_thread_lock); - this->input->metronom_thread_request = frame->pts; - this->input->metronom_thread_reply = 0; - pthread_cond_broadcast(&this->input->metronom_thread_request_cond); - pthread_mutex_unlock(&this->input->metronom_thread_lock); + vdr_metronom_handle_video_discontinuity_impl(self, DISC_ABSOLUTE, frame->pts); - vdr_metronom_handle_video_discontinuity(self, DISC_ABSOLUTE, frame->pts); + pthread_mutex_lock(&this->input->metronom_thread_lock); + if (!this->input->metronom_thread_reply) + pthread_cond_wait(&this->input->metronom_thread_reply_cond, &this->input->metronom_thread_lock); + pthread_mutex_unlock(&this->input->metronom_thread_lock); - pthread_mutex_lock(&this->input->metronom_thread_lock); - if (!this->input->metronom_thread_reply) - pthread_cond_wait(&this->input->metronom_thread_reply_cond, &this->input->metronom_thread_lock); - pthread_mutex_unlock(&this->input->metronom_thread_lock); + pthread_mutex_unlock(&this->input->metronom_thread_call_lock); + } - pthread_mutex_unlock(&this->input->metronom_thread_call_lock); + pthread_mutex_unlock(&this->input->trick_speed_mode_lock); } this->stream_metronom->got_video_frame(this->stream_metronom, frame); @@ -2565,6 +2670,9 @@ static input_plugin_t *vdr_class_get_instance(input_class_t *cls_gen, xine_strea pthread_mutex_init(&this->rpc_thread_shutdown_lock, 0); pthread_cond_init(&this->rpc_thread_shutdown_cond, 0); + pthread_mutex_init(&this->trick_speed_mode_lock, 0); + pthread_cond_init(&this->trick_speed_mode_cond, 0); + pthread_mutex_init(&this->metronom_thread_lock, 0); pthread_cond_init(&this->metronom_thread_request_cond, 0); pthread_cond_init(&this->metronom_thread_reply_cond, 0); -- cgit v1.2.3