Fix deadlock issue on Hovis

This commit is contained in:
bossiel 2015-05-26 20:05:45 +00:00
parent 1948ff4480
commit f7ca47efc6
4 changed files with 69 additions and 67 deletions

View File

@ -1679,15 +1679,15 @@ int tdav_session_av_set_ro(tdav_session_av_t* self, const struct tsdp_header_M_s
const tmedia_codec_t* tdav_session_av_get_best_neg_codec(const tdav_session_av_t* self)
{
const tsk_list_item_t* item;
if(!self){
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return tsk_null;
}
tsk_list_foreach(item, TMEDIA_SESSION(self)->neg_codecs){
tsk_list_foreach(item, TMEDIA_SESSION(self)->neg_codecs) {
// exclude DTMF, RED and ULPFEC
if(!TDAV_IS_DTMF_CODEC(item->data) && !TDAV_IS_ULPFEC_CODEC(item->data) && !TDAV_IS_RED_CODEC(item->data)
&& TMEDIA_CODEC(item->data)->plugin && TMEDIA_CODEC(item->data)->plugin->encode && TMEDIA_CODEC(item->data)->plugin->decode){
if (!TDAV_IS_DTMF_CODEC(item->data) && !TDAV_IS_ULPFEC_CODEC(item->data) && !TDAV_IS_RED_CODEC(item->data)
&& TMEDIA_CODEC(item->data)->plugin && TMEDIA_CODEC(item->data)->plugin->encode && TMEDIA_CODEC(item->data)->plugin->decode) {
return TMEDIA_CODEC(item->data);
}
}

View File

@ -369,10 +369,10 @@ static int tdav_session_video_producer_enc_cb(const void* callback_data, const v
// get best negotiated codec if not already done
// the encoder codec could be null when session is renegotiated without re-starting (e.g. hold/resume)
if(!video->encoder.codec){
if (!video->encoder.codec) {
const tmedia_codec_t* codec;
tsk_safeobj_lock(base);
if(!(codec = tdav_session_av_get_best_neg_codec(base))){
if (!(codec = tdav_session_av_get_best_neg_codec(base))) {
TSK_DEBUG_ERROR("No codec matched");
tsk_safeobj_unlock(base);
return -2;
@ -381,25 +381,33 @@ static int tdav_session_video_producer_enc_cb(const void* callback_data, const v
tsk_safeobj_unlock(base);
}
if(base->rtp_manager){
if (base->rtp_manager) {
//static int __rotation_counter = 0;
/* encode */
tsk_size_t out_size = 0;
tmedia_codec_t* codec_encoder = tsk_null;
if(!base->rtp_manager->is_started){
if (!base->rtp_manager->is_started) {
TSK_DEBUG_ERROR("Not started");
return 0;
goto bail;
}
// take a reference to the encoder to make sure it'll not be destroyed while we're using it
codec_encoder = tsk_object_ref(video->encoder.codec);
if (!codec_encoder) {
TSK_DEBUG_ERROR("The encoder is null");
goto bail;
}
#define PRODUCER_OUTPUT_FIXSIZE (base->producer->video.chroma != tmedia_chroma_mjpeg) // whether the output data has a fixed size/length
#define PRODUCER_OUTPUT_RAW (base->producer->encoder.codec_id == tmedia_codec_id_none) // Otherwise, frames from the producer are already encoded
#define PRODUCER_SIZE_CHANGED ((video->conv.producerWidth && video->conv.producerWidth != base->producer->video.width) || (video->conv.producerHeight && video->conv.producerHeight != base->producer->video.height) \
|| (video->conv.xProducerSize && (video->conv.xProducerSize != size && PRODUCER_OUTPUT_FIXSIZE)))
#define ENCODED_NEED_FLIP (TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.flip)
#define ENCODED_NEED_RESIZE (base->producer->video.width != TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.width || base->producer->video.height != TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.height)
#define ENCODED_NEED_FLIP (TMEDIA_CODEC_VIDEO(codec_encoder)->out.flip)
#define ENCODED_NEED_RESIZE (base->producer->video.width != TMEDIA_CODEC_VIDEO(codec_encoder)->out.width || base->producer->video.height != TMEDIA_CODEC_VIDEO(codec_encoder)->out.height)
#define PRODUCED_FRAME_NEED_ROTATION (base->producer->video.rotation != 0)
#define PRODUCED_FRAME_NEED_MIRROR (base->producer->video.mirror != tsk_false)
#define PRODUCED_FRAME_NEED_CHROMA_CONVERSION (base->producer->video.chroma != TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.chroma)
#define PRODUCED_FRAME_NEED_CHROMA_CONVERSION (base->producer->video.chroma != TMEDIA_CODEC_VIDEO(codec_encoder)->out.chroma)
// Video codecs only accept YUV420P buffers ==> do conversion if needed or producer doesn't have the right size
if (PRODUCER_OUTPUT_RAW && (PRODUCED_FRAME_NEED_CHROMA_CONVERSION || PRODUCER_SIZE_CHANGED || ENCODED_NEED_FLIP || ENCODED_NEED_RESIZE ||PRODUCED_FRAME_NEED_ROTATION || PRODUCED_FRAME_NEED_MIRROR)) {
// Create video converter if not already done or producer size have changed
@ -410,8 +418,8 @@ static int tdav_session_video_producer_enc_cb(const void* callback_data, const v
video->conv.xProducerSize = size;
TSK_DEBUG_INFO("producer size = (%d, %d)", (int)base->producer->video.width, (int)base->producer->video.height);
if(!(video->conv.toYUV420 = tmedia_converter_video_create(base->producer->video.width, base->producer->video.height, base->producer->video.chroma, TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.width, TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.height,
TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.chroma))){
if (!(video->conv.toYUV420 = tmedia_converter_video_create(base->producer->video.width, base->producer->video.height, base->producer->video.chroma, TMEDIA_CODEC_VIDEO(codec_encoder)->out.width, TMEDIA_CODEC_VIDEO(codec_encoder)->out.height,
TMEDIA_CODEC_VIDEO(codec_encoder)->out.chroma))){
TSK_DEBUG_ERROR("Failed to create video converter");
ret = -5;
goto bail;
@ -433,22 +441,22 @@ static int tdav_session_video_producer_enc_cb(const void* callback_data, const v
tmedia_pvt_int32,
"rotation",
(void*)&base->producer->video.rotation);
if(!param){
if (!param) {
TSK_DEBUG_ERROR("Failed to create a media parameter");
return -1;
}
video->encoder.rotation = base->producer->video.rotation; // update rotation to avoid calling the function several times
ret = tmedia_codec_set(video->encoder.codec, param);
ret = tmedia_codec_set(codec_encoder, param);
TSK_OBJECT_SAFE_FREE(param);
// (ret != 0) -> not supported by the codec -> to be done by the converter
video->encoder.scale_rotated_frames = (ret != 0);
}
// update one-shot parameters
tmedia_converter_video_set(video->conv.toYUV420, base->producer->video.rotation, TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.flip, base->producer->video.mirror, video->encoder.scale_rotated_frames);
tmedia_converter_video_set(video->conv.toYUV420, base->producer->video.rotation, TMEDIA_CODEC_VIDEO(codec_encoder)->out.flip, base->producer->video.mirror, video->encoder.scale_rotated_frames);
yuv420p_size = tmedia_converter_video_process(video->conv.toYUV420, buffer, size, &video->encoder.conv_buffer, &video->encoder.conv_buffer_size);
if(!yuv420p_size || !video->encoder.conv_buffer){
if (!yuv420p_size || !video->encoder.conv_buffer) {
TSK_DEBUG_ERROR("Failed to convert XXX buffer to YUV42P");
ret = -6;
goto bail;
@ -457,26 +465,26 @@ static int tdav_session_video_producer_enc_cb(const void* callback_data, const v
// Encode data
tsk_mutex_lock(video->encoder.h_mutex);
if(video->encoder.codec){ // codec is destroyed by stop() which use same mutex
if(video->encoder.conv_buffer && yuv420p_size){
if (video->started && codec_encoder->opened) { // stop() function locks the encoder mutex before changing "started"
if (video->encoder.conv_buffer && yuv420p_size) {
/* producer doesn't support yuv42p */
out_size = video->encoder.codec->plugin->encode(video->encoder.codec, video->encoder.conv_buffer, yuv420p_size, &video->encoder.buffer, &video->encoder.buffer_size);
out_size = codec_encoder->plugin->encode(codec_encoder, video->encoder.conv_buffer, yuv420p_size, &video->encoder.buffer, &video->encoder.buffer_size);
}
else{
else {
/* producer supports yuv42p */
out_size = video->encoder.codec->plugin->encode(video->encoder.codec, buffer, size, &video->encoder.buffer, &video->encoder.buffer_size);
out_size = codec_encoder->plugin->encode(codec_encoder, buffer, size, &video->encoder.buffer, &video->encoder.buffer_size);
}
}
tsk_mutex_unlock(video->encoder.h_mutex);
if(out_size){
if (out_size) {
/* Never called, see tdav_session_video_raw_cb() */
trtp_manager_send_rtp(base->rtp_manager, video->encoder.buffer, out_size, 6006, tsk_true, tsk_true);
}
bail: ;
bail:
TSK_OBJECT_SAFE_FREE(codec_encoder);
}
else{
else {
TSK_DEBUG_ERROR("Invalid parameter");
ret = -1;
}
@ -1102,8 +1110,11 @@ static int tdav_session_video_stop(tmedia_session_t* self)
video = (tdav_session_video_t*)self;
base = (tdav_session_av_t*)self;
// must be here to make sure not other thread will lock the encoder once we have done it
// must be here to make sure no other thread will lock the encoder once we have done it
tsk_mutex_lock(video->encoder.h_mutex); // encoder thread will check "started" var right after the lock is passed
video->started = tsk_false;
tsk_mutex_unlock(video->encoder.h_mutex);
// at this step we're sure that encode() will no longer be called which means we can safely close the codec
if (video->jb) {
ret = tdav_video_jb_stop(video->jb);
@ -1113,9 +1124,12 @@ static int tdav_session_video_stop(tmedia_session_t* self)
tsk_list_clear_items(video->avpf.packets);
tsk_list_unlock(video->avpf.packets);
// the encoder must be locked before stopping the session as such action will close all codecs
tsk_mutex_lock(video->encoder.h_mutex);
// tdav_session_av_stop() : stop producer and consumer, close encoder and all other codecs, stop rtpManager...
// no need to lock the encoder to avoid using a closed codec (see above)
// no need to lock the decoder as the rtpManager will be stop before closing the codec
// lock-free stop() may avoid deadlock issue (cannot reproduce it myself) on Hovis
ret = tdav_session_av_stop(base);
tsk_mutex_lock(video->encoder.h_mutex);
TSK_OBJECT_SAFE_FREE(video->encoder.codec);
tsk_mutex_unlock(video->encoder.h_mutex);
TSK_OBJECT_SAFE_FREE(video->decoder.codec);
@ -1324,12 +1338,12 @@ static int _tdav_session_video_init(tdav_session_video_t *p_self, tmedia_type_t
TSK_DEBUG_ERROR("Failed to create encode mutex");
return -4;
}
if (!(p_self->avpf.packets = tsk_list_create())) {
if (!p_self->avpf.packets && !(p_self->avpf.packets = tsk_list_create())) {
TSK_DEBUG_ERROR("Failed to create list");
return -2;
}
if (p_self->jb_enabled) {
if(!(p_self->jb = tdav_video_jb_create())) {
if (!p_self->jb && !(p_self->jb = tdav_video_jb_create())) {
TSK_DEBUG_ERROR("Failed to create jitter buffer");
return -3;
}

View File

@ -279,35 +279,24 @@ bail:
tsk_size_t tnet_transport_sendto(const tnet_transport_handle_t *handle, tnet_fd_t from, const struct sockaddr *to, const void* buf, tsk_size_t size)
{
tnet_transport_t *transport = (tnet_transport_t*)handle;
WSABUF wsaBuffer;
DWORD numberOfBytesSent = 0;
int ret = -1;
int numberOfBytesSent = 0;
if (!transport){
if (!transport) {
TSK_DEBUG_ERROR("Invalid server handle.");
return ret;
goto bail;
}
if (!TNET_SOCKET_TYPE_IS_DGRAM(transport->master->type)){
TSK_DEBUG_ERROR("In order to use WSASendTo you must use an udp transport.");
return ret;
if (!TNET_SOCKET_TYPE_IS_DGRAM(transport->master->type)) {
TSK_DEBUG_ERROR("In order to use sendto() you must use an udp transport.");
goto bail;
}
wsaBuffer.buf = (CHAR*)buf;
wsaBuffer.len = (ULONG)size;
if ((ret = WSASendTo(from, &wsaBuffer, 1, &numberOfBytesSent, 0, to, tnet_get_sockaddr_size(to), 0, 0)) == SOCKET_ERROR){
if ((ret = WSAGetLastError()) == WSA_IO_PENDING){
TSK_DEBUG_INFO("WSA_IO_PENDING error for WSASendTo SSESSION");
ret = 0;
}
else{
TNET_PRINT_LAST_ERROR("WSASendTo have failed.");
return ret;
}
if ((numberOfBytesSent = tnet_sockfd_sendto(from, to, buf, size)) <= 0) {
TNET_PRINT_LAST_ERROR("sendto have failed.");
goto bail;
}
else ret = 0;
bail:
return numberOfBytesSent;
}

View File

@ -1674,17 +1674,17 @@ int tnet_sockfd_sendto(tnet_fd_t fd, const struct sockaddr *to, const void* buf,
tsk_size_t sent = 0;
int ret = -1;
if (fd == TNET_INVALID_FD){
if (fd == TNET_INVALID_FD) {
TSK_DEBUG_ERROR("Using invalid FD to send data.");
goto bail;
}
if (!buf || !size){
if (!buf || !size) {
TSK_DEBUG_ERROR("Using invalid BUFFER.");
ret = -2;
goto bail;
}
while (sent < size){
while (sent < size) {
int try_guard = 10;
#if TNET_UNDER_WINDOWS
WSABUF wsaBuffer;
@ -1693,31 +1693,30 @@ int tnet_sockfd_sendto(tnet_fd_t fd, const struct sockaddr *to, const void* buf,
wsaBuffer.len = (ULONG)(size - sent);
try_again:
ret = WSASendTo(fd, &wsaBuffer, 1, &numberOfBytesSent, 0, to, tnet_get_sockaddr_size(to), 0, 0); // returns zero if succeed
if (ret == 0){
if (ret == 0) {
ret = numberOfBytesSent;
}
#else
try_again:
ret = sendto(fd, (((const uint8_t*)buf) + sent), (size - sent), 0, to, tnet_get_sockaddr_size(to)); // returns number of sent bytes if succeed
#endif
if (ret <= 0){
if(tnet_geterrno() == TNET_ERROR_WOULDBLOCK){
if (ret <= 0) {
if (tnet_geterrno() == TNET_ERROR_WOULDBLOCK) {
TSK_DEBUG_INFO("SendUdp() - WouldBlock. Retrying...");
if(try_guard--){
if (try_guard--) {
tsk_thread_sleep(10);
goto try_again;
}
}
else{
}
}
else {
TNET_PRINT_LAST_ERROR("sendto() failed");
}
goto bail;
}
else{
}
else {
sent += ret;
}
}
}
bail:
return (int)((size == sent) ? sent : ret);