diff --git a/veejay-current/veejay-server/libel/avhelper.c b/veejay-current/veejay-server/libel/avhelper.c index 45e35863..b29a144c 100644 --- a/veejay-current/veejay-server/libel/avhelper.c +++ b/veejay-current/veejay-server/libel/avhelper.c @@ -318,6 +318,29 @@ int avhelper_recv_frame_packet( void *decoder ) return -1; // error } +int avhelper_decode_video_buffer( void *ptr, uint8_t *data, int len ) +{ + int got_picture = 0; + el_decoder_t * e = (el_decoder_t*) ptr; + + avcodec_decode_video( e->codec_ctx, e->frames[e->frame_index], &got_picture, data, len ); + + //avhelper_frame_unref(e->frames[e->frame_index]); + + if(got_picture) { + e->frameinfo[e->frame_index] = 1; /* we have a full picture at this index */ + e->frame_index = (e->frame_index + 1) % 2; /* use next available buffer */ + e->frameinfo[e->frame_index] = 0; /* and clear the information */ + return 1; + } + else { + e->frameinfo[e->frame_index] = 0; + } + + return 0; +} + + int avhelper_recv_decode( void *decoder, int *got_picture ) { el_decoder_t *x = (el_decoder_t*) decoder; @@ -501,7 +524,7 @@ further: veejay_memset( &(x->packets[0]), 0, sizeof(AVPacket)); AVFrame *f = avhelper_alloc_frame(); x->output = yuv_yuv_template( NULL,NULL,NULL, wid, hei, dst_pixfmt ); - x->spvf = ( (double) x->codec_ctx->framerate.den ) / ( (double) x->codec_ctx->framerate.num); + x->spvf = ( (double) x->codec_ctx->framerate.den ) / ( (double) x->codec_ctx->framerate.num); int got_picture = 0; while(1) { diff --git a/veejay-current/veejay-server/libel/avhelper.h b/veejay-current/veejay-server/libel/avhelper.h index 492a151a..fdf739e4 100644 --- a/veejay-current/veejay-server/libel/avhelper.h +++ b/veejay-current/veejay-server/libel/avhelper.h @@ -66,6 +66,8 @@ int avhelper_recv_decode( void *decoder, int *got_picture ); int avhelper_recv_frame_packet( void *decoder ); +int avhelper_decode_video_buffer( void *ptr, uint8_t *data, int len ); + double avhelper_get_spvf( void *decoder ); #endif diff --git a/veejay-current/veejay-server/libel/vj-avcodec.c b/veejay-current/veejay-server/libel/vj-avcodec.c index 5ef4b5c4..d5ba3ba7 100644 --- a/veejay-current/veejay-server/libel/vj-avcodec.c +++ b/veejay-current/veejay-server/libel/vj-avcodec.c @@ -415,11 +415,9 @@ int vj_avcodec_init( int pixel_format, int verbose) char *av_log_setting = getenv("VEEJAY_AV_LOG"); if(av_log_setting != NULL) { - int enabled = atoi(av_log_setting); - if(enabled) { - veejay_msg(VEEJAY_MSG_DEBUG, "FFMpeg Log is enabled"); - av_log_set_level(AV_LOG_VERBOSE); - } + int level = atoi(av_log_setting); + veejay_msg(VEEJAY_MSG_DEBUG, "ffmpeg/libav log level set to %d", level); + av_log_set_level(level); } #if LIBAVCODEC_BUILD < 5400 diff --git a/veejay-current/veejay-server/libstream/vj-avformat.c b/veejay-current/veejay-server/libstream/vj-avformat.c index f667034e..966f5155 100644 --- a/veejay-current/veejay-server/libstream/vj-avformat.c +++ b/veejay-current/veejay-server/libstream/vj-avformat.c @@ -55,6 +55,10 @@ static int lock(threaded_t *t) { return pthread_mutex_lock( &(t->mutex )); } +static int trylock(threaded_t *t) +{ + return pthread_mutex_trylock( &(t->mutex )); +} static int unlock(threaded_t *t) { @@ -67,6 +71,9 @@ static int eval_state(threaded_t *t, vj_tag *tag) lock(t); if(t->state == STATE_ERROR || t->decoder == NULL) { + + //FIXME: this part can take some time, but we must let other functions not block due to having the sema up here + // if(t->decoder) { avhelper_close_decoder(t->decoder); t->decoder = NULL; @@ -140,6 +147,7 @@ static void *reader_thread(void *data) if( error ) { lock(t); t->state = STATE_ERROR; + t->have_frame = 0; unlock(t); } } @@ -168,9 +176,12 @@ NETTHREADEXIT: void avformat_thread_set_state(vj_tag *tag, int new_state) { threaded_t *t = (threaded_t*) tag->priv; - lock(t); - t->state = new_state; - unlock(t); + if(trylock(t) == 0 ) { + t->state = new_state; + unlock(t); + } else { + veejay_msg(VEEJAY_MSG_WARNING,"Failed to set state, thread is busy"); //FIXME thread that initially takes a long startup time, locks main thread from controls + } } void *avformat_thread_allocate(VJFrame *frame) @@ -183,9 +194,14 @@ int avformat_thread_get_frame( vj_tag *tag, VJFrame *dst ) { threaded_t *t = (threaded_t*) tag->priv; - lock(t); + lock(t); //FIXME thread that initially takes a long startup time, locks main thread from controls + + if( !(t->state==STATE_RUNNING) || t->have_frame == 0 ) { + unlock(t); + return 1; + } VJFrame *src = avhelper_get_decoded_video(t->decoder); - if(t->scaler == NULL) { + if(t->scaler == NULL) { sws_template sws_templ; memset( &sws_templ, 0, sizeof(sws_template)); sws_templ.flags = yuv_which_scaler(); @@ -217,7 +233,7 @@ int avformat_thread_start(vj_tag *tag, VJFrame *info) p_err = pthread_create( &(t->thread), NULL, &reader_thread, (void*) tag ); if( p_err ==0) { - veejay_msg(VEEJAY_MSG_INFO, "Created thread for stream [%s]",tag->source_name ); + veejay_msg(VEEJAY_MSG_INFO, "Created new input stream %d for [%s]",tag->id, tag->source_name ); } return 1; diff --git a/veejay-current/veejay-server/libstream/vj-net.c b/veejay-current/veejay-server/libstream/vj-net.c index cfecb76c..6fca311f 100644 --- a/veejay-current/veejay-server/libstream/vj-net.c +++ b/veejay-current/veejay-server/libstream/vj-net.c @@ -40,8 +40,8 @@ typedef struct { pthread_mutex_t mutex; pthread_t thread; - int state; int have_frame; + int state; vj_client *v; VJFrame *info; VJFrame *dest; // there is no full range YUV + alpha in PIX_FMT family @@ -50,6 +50,8 @@ typedef struct #define STATE_INACTIVE 0 #define STATE_RUNNING 1 +#define STATE_QUIT 2 +#define STATE_ERROR 4 static int lock(threaded_t *t) { @@ -61,6 +63,69 @@ static int unlock(threaded_t *t) return pthread_mutex_unlock( &(t->mutex )); } +static int eval_state(threaded_t *t, vj_tag *tag) +{ + int ret = 0; + lock(t); + + if(t->state == STATE_ERROR || t->v == NULL) { + if(t->v == NULL) { + t->v = vj_client_alloc_stream(t->info); + } + veejay_msg(VEEJAY_MSG_INFO, " ... Waiting for network stream to become ready [%s]",tag->source_name); + int success = vj_client_connect_dat( t->v, tag->source_name,tag->video_channel ); + if(success <= 0) { + t->state = STATE_ERROR; + if(t->v) { + vj_client_close(t->v); + vj_client_free(t->v); + t->v = NULL; + } + } + else { + veejay_msg(VEEJAY_MSG_INFO, "Connecton established with %s:%d",tag->source_name, tag->video_channel + 5); + t->state = STATE_RUNNING; + } + } + + ret = t->state; + + unlock(t); + return ret; +} + +static int eval_state_mcast(threaded_t *t, vj_tag *tag) //FIXME: split this up in seperate source files, use modular structure +{ + int ret = 0; + lock(t); + + if(t->state == STATE_ERROR || t->v == NULL) { + if(t->v == NULL) { + t->v = vj_client_alloc_stream(t->info); + } + veejay_msg(VEEJAY_MSG_INFO, " ... Waiting for mcast stream to become ready [%s]",tag->source_name); + int success = vj_client_connect( t->v, NULL, tag->source_name, tag->video_channel ); + if(success <= 0) { + t->state = STATE_ERROR; + if(t->v) { + vj_client_close(t->v); + vj_client_free(t->v); + t->v = NULL; + } + } + else { + veejay_msg(VEEJAY_MSG_INFO, "Connecton established with %s:%d",tag->source_name, tag->video_channel + 5); + t->state = STATE_RUNNING; + } + } + + ret = t->state; + + unlock(t); + return ret; +} + + #define MS_TO_NANO(a) (a *= 1000000) static void net_delay(long ms, long sec ) { @@ -85,114 +150,88 @@ static void *reader_thread(void *data) int retrieve = 0; int success = 0; - t->v = vj_client_alloc_stream(t->info); - if(t->v == NULL) { - return NULL; - } - snprintf(buf,sizeof(buf), "%03d:%d;", VIMS_GET_FRAME, my_screen_id); - success = vj_client_connect_dat( t->v, tag->source_name,tag->video_channel ); - - if( success > 0 ) { - veejay_msg(VEEJAY_MSG_INFO, "Connecton established with %s:%d",tag->source_name, tag->video_channel + 5); - } - else - { - veejay_msg(0, "Unable to connect to %s: %d", tag->source_name, tag->video_channel+5); - goto NETTHREADEXIT; - } - lock(t); - t->state = STATE_RUNNING; - unlock(t); + t->state = STATE_ERROR; for( ;; ) { int error = 0; int res = 0; int ret = 0; - - if( retrieve == 0 && t->have_frame == 0 ) { - ret = vj_client_send( t->v, V_CMD,(unsigned char*) buf ); - if( ret <= 0 ) { - error = 1; - } - else { - retrieve = 1; - } - } - if(!error && retrieve == 1 ) { - res = vj_client_poll(t->v, V_CMD ); - if( res ) { - if(vj_client_link_can_read( t->v, V_CMD ) ) { - retrieve = 2; - } - } - else if ( res < 0 ) { - error = 1; - } else if ( res == 0 ) { + int state_eval = eval_state(t, tag); + + switch(state_eval) { + case STATE_INACTIVE: net_delay(10,0); - continue; - } - } - - if(!error && retrieve == 2) { - int frame_len = vj_client_read_frame_hdr( t->v ); - if( frame_len <= 0 ) { - error = 1; - } - - if( error == 0 ) { - lock(t); - ret = vj_client_read_frame_data( t->v, frame_len ); - unlock(t); - } - - if(ret) { - t->have_frame = 1; retrieve = 0; - } + success = 0; + continue; + break; + case STATE_RUNNING: + if( retrieve == 0 ) { + ret = vj_client_send( t->v, V_CMD,(unsigned char*) buf ); + if( ret <= 0 ) { + error = 1; + } + else { + retrieve = 1; /* queried a frame, try to fetch it */ + } + } + if(!error && retrieve == 1 ) { + res = vj_client_poll(t->v, V_CMD ); + if( res ) { + if(vj_client_link_can_read( t->v, V_CMD ) ) { + retrieve = 2; /* data waiting at socket */ + } + } + else if ( res < 0 ) { + error = 1; + } else if ( res == 0 ) { + net_delay(10,0); + continue; + } + } + if(!error && retrieve == 2) { + int frame_len = vj_client_read_frame_hdr( t->v ); + if( frame_len <= 0 ) { + error = 1; + } - if( ret <= 0 ) { - veejay_msg(VEEJAY_MSG_DEBUG,"Error reading video frame from %s:%d",tag->source_name,tag->video_channel ); - error = 1; - } - } -NETTHREADRETRY: + if( error == 0 ) { + lock(t); + ret = vj_client_read_frame_data( t->v, frame_len ); + if(ret) { + t->have_frame = 1; + } + unlock(t); + } - if( error ) - { - vj_client_close(t->v); + if(ret) { + retrieve = 0; + } - veejay_msg(VEEJAY_MSG_INFO, " ZZzzzzz ... waiting for Link %s:%d to become ready", tag->source_name, tag->video_channel ); - net_delay( 0, 5 ); + if( ret <= 0 ) { + veejay_msg(VEEJAY_MSG_DEBUG,"Error reading video frame from %s:%d",tag->source_name,tag->video_channel ); + error = 1; + } + } + if( error ) { + lock(t); + t->state = STATE_ERROR; + t->have_frame = 0; + unlock(t); + retrieve = 0; - success = vj_client_connect_dat( t->v, tag->source_name,tag->video_channel ); - - if( t->state == 0 ) - { - veejay_msg(VEEJAY_MSG_INFO, "Network thread with %s: %d was told to exit",tag->source_name,tag->video_channel+5); - goto NETTHREADEXIT; - } + } - if( success <= 0 ) - { - goto NETTHREADRETRY; - } - else - { - veejay_msg(VEEJAY_MSG_INFO, "Connecton re-established with %s:%d",tag->source_name,tag->video_channel + 5); - } - - retrieve = 0; + break; + case STATE_QUIT: + { + goto NETTHREADEXIT; + } } - - if( t->state == 0 ) - { - veejay_msg(VEEJAY_MSG_INFO, "Network thread with %s: %d was told to exit",tag->source_name,tag->video_channel+5); - goto NETTHREADEXIT; - } } NETTHREADEXIT: @@ -205,6 +244,8 @@ NETTHREADEXIT: veejay_msg(VEEJAY_MSG_INFO, "Network thread with %s: %d has exited",tag->source_name,tag->video_channel+5); + pthread_exit(NULL); + return NULL; } @@ -214,72 +255,35 @@ static void *mcast_reader_thread(void *data) vj_tag *tag = (vj_tag*) data; threaded_t *t = tag->priv; int success = 0; - - t->v = vj_client_alloc_stream(t->info); - if(t->v == NULL) { - return NULL; - } - - success = vj_client_connect( t->v, NULL, tag->source_name, tag->video_channel ); - - if( success > 0 ) { - veejay_msg(VEEJAY_MSG_INFO, "Multicast connecton established with %s:%d",tag->source_name, tag->video_channel + 5); - } - else - { - veejay_msg(0, "Unable to connect to %s: %d", tag->source_name, tag->video_channel+5); - goto NETTHREADEXIT; - } - - lock(t); - t->state = STATE_RUNNING; - unlock(t); - const int padded = 256; int max_len = padded + RUP8( 1920 * 1080 * 3 ); for( ;; ) { int error = 0; - - if( vj_client_read_mcast_data( t->v, max_len ) < 0 ) { - error = 1; - } - - if(error == 0) { - t->have_frame = 1; - } + + int state_eval = eval_state_mcast(t, tag); -NETTHREADRETRY: + switch(state_eval) { + case STATE_INACTIVE: + net_delay(10,0); + continue; + break; + case STATE_RUNNING: + lock(t); + if( vj_client_read_mcast_data( t->v, max_len ) < 0 ) { + error = 1; + } + t->have_frame = 1; + if( error ) { + t->state = STATE_ERROR; + t->have_frame = 0; + } + unlock(t); - if( error ) - { - vj_client_close(t->v); - - veejay_msg(VEEJAY_MSG_INFO, " ZZzzzzz ... waiting for multicast server %s:%d to become ready", tag->source_name, tag->video_channel ); - net_delay( 0, 5 ); - - success = vj_client_connect( t->v,NULL,tag->source_name,tag->video_channel ); - - if( t->state == 0 ) - { - veejay_msg(VEEJAY_MSG_INFO, "Multicast receiver %s: %d was told to stop",tag->source_name,tag->video_channel+5); + break; + case STATE_QUIT: goto NETTHREADEXIT; - } - - if( success <= 0 ) - { - goto NETTHREADRETRY; - } - else - { - veejay_msg(VEEJAY_MSG_INFO, "Multicast receiver has re-established with %s:%d",tag->source_name,tag->video_channel + 5); - } - } - - if( t->state == 0 ) - { - veejay_msg(VEEJAY_MSG_INFO, "Multicast receiver %s: %d was told to stop",tag->source_name,tag->video_channel+5); - goto NETTHREADEXIT; + break; } } @@ -306,16 +310,12 @@ int net_thread_get_frame( vj_tag *tag, VJFrame *dst ) { threaded_t *t = (threaded_t*) tag->priv; - int state = 0; - /* frame ready ? */ lock(t); - state = t->state; - if( state == 0 || t->have_frame == 0 ) { + if(!(t->state == STATE_RUNNING) || t->have_frame == 0) { unlock(t); - return 1; // not active or no frame - } - + return 1; + } VJFrame *src = avhelper_get_decoded_video(t->v->decoder); if(t->scaler == NULL) { @@ -326,9 +326,6 @@ int net_thread_get_frame( vj_tag *tag, VJFrame *dst ) } yuv_convert_and_scale( t->scaler, src,dst ); - - t->have_frame = 0; // frame is consumed - unlock(t); return 1; @@ -346,7 +343,6 @@ int net_thread_start(vj_tag *tag, VJFrame *info) pthread_mutex_init( &(t->mutex), NULL ); - t->have_frame = 0; t->info = info; @@ -358,7 +354,8 @@ int net_thread_start(vj_tag *tag, VJFrame *info) } if( p_err ==0) { - veejay_msg(VEEJAY_MSG_INFO, "Created new %s threaded stream to veejay host %s port %d", + veejay_msg(VEEJAY_MSG_INFO, "Created new input stream [%d] (%s) to veejay host %s port %d", + tag->id, tag->source_type == VJ_TAG_TYPE_MCAST ? "multicast" : "unicast", tag->source_name,tag->video_channel); } diff --git a/veejay-current/veejay-server/libvjnet/vj-client.c b/veejay-current/veejay-server/libvjnet/vj-client.c index 63958cc2..1bc341c2 100644 --- a/veejay-current/veejay-server/libvjnet/vj-client.c +++ b/veejay-current/veejay-server/libvjnet/vj-client.c @@ -311,7 +311,7 @@ int vj_client_read_frame_data( vj_client *v, int datalen) return n; } - return avhelper_decode_video( v->decoder, v->space, n ); + return avhelper_decode_video_buffer( v->decoder, v->space, n ); } int vj_client_read_no_wait(vj_client *v, int sock_type, uint8_t *dst, int bytes )