diff --git a/include/mediastreamer2/msfilter.h b/include/mediastreamer2/msfilter.h index f015b82..2fcaa04 100644 --- a/include/mediastreamer2/msfilter.h +++ b/include/mediastreamer2/msfilter.h @@ -74,6 +74,7 @@ typedef struct _MSFilterMethod MSFilterMethod; enum _MSFilterCategory{ MS_FILTER_OTHER, + MS_FILTER_DISPLAY, MS_FILTER_ENCODER, MS_FILTER_DECODER }; @@ -214,6 +215,20 @@ MSFilterDesc * ms_filter_get_encoder(const char *mime); MSFilterDesc * ms_filter_get_decoder(const char *mime); /** + * Retrieve displays according to display name. + * + * Internal supported codecs: + * MSVideoOut, MSDrawDibDisplay; + * Existing Public plugins: + * MSFbOut + * + * @param mime A string indicating the display. + * + * Returns: a MSFilterDesc if successfull, NULL otherwise. + */ +MSFilterDesc * ms_filter_get_display(const char *name); + +/** * Create encoder filter according to codec name. * * Internal supported codecs: @@ -242,6 +257,19 @@ MSFilter * ms_filter_create_encoder(const char *mime); MSFilter * ms_filter_create_decoder(const char *mime); /** + * Create display filter according to display name. + * + * Internal supported codecs: + * MSVideoOut, MSDrawDibDisplay; + * Existing Public plugins: + * MSFbOut + * + * @param mime A string indicating the display. + * + * Returns: a MSFilter if successfull, NULL otherwise. + */ +MSFilter * ms_filter_create_display(const char *name); +/** * Check if a encode or decode filter exists for a codec name. * * Internal supported codecs: diff --git a/src/drawdib-display.c b/src/drawdib-display.c index 1e1f87b..48dc7fb 100644 --- a/src/drawdib-display.c +++ b/src/drawdib-display.c @@ -693,7 +693,7 @@ MSFilterDesc ms_dd_display_desc={ .id=MS_DRAWDIB_DISPLAY_ID, .name="MSDrawDibDisplay", .text=N_("A video display based on windows DrawDib api"), - .category=MS_FILTER_OTHER, + .category=MS_FILTER_DISPLAY, .ninputs=2, .noutputs=0, .init=dd_display_init, diff --git a/src/msfilter.c b/src/msfilter.c index f4b6be4..43a39ab 100644 --- a/src/msfilter.c +++ b/src/msfilter.c @@ -202,10 +202,54 @@ void ms_filter_destroy(MSFilter *f){ ms_free(f); } +#ifdef DEBUG +static long filter_get_cur_time(void *unused) +{ +#if defined(_WIN32_WCE) + DWORD timemillis = GetTickCount(); + return timemillis; +#elif defined(WIN32) + return timeGetTime() ; +#elif defined(__MACH__) && defined(__GNUC__) && (__GNUC__ >= 3) + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec*1000LL) + (tv.tv_usec/1000LL); +#elif defined(__MACH__) + struct timespec ts; + struct timeb time_val; + + ftime (&time_val); + ts.tv_sec = time_val.time; + ts.tv_nsec = time_val.millitm * 1000000; + return (ts.tv_sec*1000LL) + (ts.tv_nsec/1000000LL); +#else + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC,&ts)<0){ + fprintf(stderr, "clock_gettime() doesn't work: %s",strerror(errno)); + } + return (ts.tv_sec*1000LL) + (ts.tv_nsec/1000000LL); +#endif +} +#endif void ms_filter_process(MSFilter *f){ ms_debug("Executing process of filter %s:%p",f->desc->name,f); +#ifdef DEBUG + long start,stop; + start = filter_get_cur_time(NULL); +#endif f->desc->process(f); +#ifdef DEBUG + stop = filter_get_cur_time(NULL); + if(stop-start > 10) + { + ms_warning("%s take too much time:%ldms\n",f->desc->name,stop-start); + } + else + { + ms_debug("%s take:%ldms\n",f->desc->name,stop-start); + } +#endif } void ms_filter_preprocess(MSFilter *f, struct _MSTicker *t){ @@ -307,4 +351,20 @@ int ms_connection_helper_unlink(MSConnectionHelper *h, MSFilter *f, int inpin, i return err; } +MSFilterDesc * ms_filter_get_display(const char *name){ + MSList *elem; + for (elem=desc_list;elem!=NULL;elem=ms_list_next(elem)){ + MSFilterDesc *desc=(MSFilterDesc*)elem->data; + if (desc->category==MS_FILTER_DISPLAY && + strcasecmp(desc->name,name)==0){ + return desc; + } + } + return NULL; +} +MSFilter * ms_filter_create_display(const char *name){ + MSFilterDesc *desc=ms_filter_get_display(name); + if (desc!=NULL) return ms_filter_new_from_desc(desc); + return NULL; +} diff --git a/src/msv4l2.c b/src/msv4l2.c index a5e2a58..08ae5ae 100644 --- a/src/msv4l2.c +++ b/src/msv4l2.c @@ -54,6 +54,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. typedef struct V4l2State{ int fd; +#ifdef V4L2_THREADED + ms_thread_t thread; + bool_t thread_run; + queue_t rq; + ms_mutex_t mutex; +#endif char *dev; char *mmapdbuf; int msize;/*mmapped size*/ @@ -366,16 +372,77 @@ static void msv4l2_init(MSFilter *f){ s->fps=15; s->configured=FALSE; f->data=s; +#ifdef V4L2_THREADED + s->thread_run = TRUE; + qinit(&s->rq); +#endif } static void msv4l2_uninit(MSFilter *f){ V4l2State *s=(V4l2State*)f->data; ms_free(s->dev); ms_free(s); +#ifdef V4L2_THREADED + ms_mutex_destroy(&s->mutex); +#endif } +#ifdef V4L2_THREADED +static void *msv4l2_thread(void *ptr){ + V4l2State *s=(V4l2State*)ptr; + int err=-1; + ms_message("msv4l2_thread starting"); + if (s->fd!=-1) + { + ms_warning("msv4l2 file descriptor already openned fd:%d",s->fd); + goto exit; + } + if( msv4l2_open(s)!=0){ + ms_warning("msv4l2 could not be openned"); + goto close; + } + if (!s->configured && msv4l2_configure(s)!=0){ + ms_warning("msv4l2 could not be configured"); + goto close; + } + if (msv4l2_do_mmap(s)!=0) + { + ms_warning("msv4l2 could not do mmap"); + goto close; + } + ms_message("V4L2 video capture started."); + while(s->thread_run) + { + mblk_t *m; + if (s->fd!=-1){ + mblk_t *m; + m=v4lv2_grab_image(s); + if (m){ + mblk_t *om=dupb(m); + mblk_set_marker_info(om,(s->pix_fmt==MS_MJPEG)); + ms_mutex_lock(&s->mutex); + putq(&s->rq,om); + ms_mutex_unlock(&s->mutex); + } + } + } + ms_message("thread:%d",s->thread_run); +munmap: + msv4l2_do_munmap(s); +close: + msv4l2_close(s); +exit: + ms_message("msv4l2_thread exited."); + s->fd = -1; + ms_thread_exit(NULL); +} +#endif + static void msv4l2_preprocess(MSFilter *f){ V4l2State *s=(V4l2State*)f->data; +#ifdef V4L2_THREADED + ms_thread_create(&s->thread,NULL,msv4l2_thread,s); +#else if (s->fd==-1 && msv4l2_open(s)!=0) { return; } @@ -388,10 +455,40 @@ static void msv4l2_preprocess(MSFilter *f){ msv4l2_close(s); } s->start_time=f->ticker->time; +#endif } static void msv4l2_process(MSFilter *f){ V4l2State *s=(V4l2State*)f->data; +#ifdef V4L2_THREADED + uint32_t timestamp; + int cur_frame; + if (s->frame_count==-1){ + s->start_time=f->ticker->time; + s->frame_count=0; + } + cur_frame=((f->ticker->time-s->start_time)*s->fps/1000.0); + + if (cur_frame>=s->frame_count){ + mblk_t *om=NULL; + ms_mutex_lock(&s->mutex); + /*keep the most recent frame if several frames have been captured */ + if (s->fd!=-1){ + om=getq(&s->rq); + } + ms_mutex_unlock(&s->mutex); + if (om!=NULL){ + timestamp=f->ticker->time*90;/* rtp uses a 90000 Hz clockrate for video*/ + mblk_set_timestamp_info(om,timestamp); + mblk_set_marker_info(om,TRUE); + ms_queue_put(f->outputs[0],om); + /*ms_message("picture sent");*/ + s->frame_count++; + } + }else{ + flushq(&s->rq,0); + } +#else uint32_t elapsed; if (s->fd!=-1){ @@ -408,14 +505,24 @@ static void msv4l2_process(MSFilter *f){ } } } +#endif } static void msv4l2_postprocess(MSFilter *f){ V4l2State *s=(V4l2State*)f->data; +#ifdef V4L2_THREADED + s->thread_run = FALSE; + if(ms_thread_join(s->thread,NULL)) + ms_warning("msv4l2 thread was already stopped"); + else + ms_message("msv4l2 thread has joined."); + flushq(&s->rq,0); +#else if (s->fd!=-1){ msv4l2_do_munmap(s); msv4l2_close(s); } +#endif } static int msv4l2_set_fps(MSFilter *f, void *arg){ @@ -442,6 +549,9 @@ static int msv4l2_get_pixfmt(MSFilter *f, void *arg){ if (msv4l2_open(s)==0){ msv4l2_configure(s); *(MSPixFmt*)arg=s->pix_fmt; +#ifdef V4L2_THREADED + msv4l2_close(s); +#endif return 0; }else return -1; } diff --git a/src/videoout.c b/src/videoout.c index 54ba397..245b651 100644 --- a/src/videoout.c +++ b/src/videoout.c @@ -1837,7 +1837,7 @@ MSFilterDesc ms_video_out_desc={ .id=MS_VIDEO_OUT_ID, .name="MSVideoOut", .text=N_("A generic video display"), - .category=MS_FILTER_OTHER, + .category=MS_FILTER_DISPLAY, .ninputs=2, .noutputs=0, .init=video_out_init, diff --git a/src/videostream.c b/src/videostream.c index 7ccd5bd..2336048 100644 --- a/src/videostream.c +++ b/src/videostream.c @@ -32,6 +32,16 @@ extern RtpSession * create_duplex_rtpsession( int locport, bool_t ipv6); #define MAX_RTP_SIZE UDP_MAX_SIZE +#ifndef WIN32 + const char *display_name="MSVideoOut"; +#else + const char *display_name="MSDrawDibDisplay"; +#endif + +void video_stream_set_display_filter_name(const char *mime){ + display_name=mime; +} + /* this code is not part of the library itself, it is part of the mediastream program */ void video_stream_free (VideoStream * stream) { @@ -266,11 +276,7 @@ int video_stream_start (VideoStream *stream, RtpProfile *profile, const char *re stream->output=ms_filter_new(MS_EXT_DISPLAY_ID); ms_filter_set_notify_callback (stream->output,ext_display_cb,stream); }else{ -#ifndef WIN32 - stream->output=ms_filter_new(MS_VIDEO_OUT_ID); -#else - stream->output=ms_filter_new(MS_DRAWDIB_DISPLAY_ID); -#endif + stream->output = ms_filter_create_display(display_name); } stream->sizeconv=ms_filter_new(MS_SIZE_CONV_ID); @@ -399,13 +405,7 @@ VideoStream * video_preview_start(MSWebCam *device, MSVideoSize disp_size){ /* creates the filters */ stream->source = ms_web_cam_create_reader(device); -#ifndef WIN32 - stream->output = ms_filter_new(MS_VIDEO_OUT_ID); -#else - stream->output = ms_filter_new(MS_DRAWDIB_DISPLAY_ID); -#endif - - + stream->output = ms_filter_create_display(display_name); /* configure the filters */ ms_filter_call_method(stream->source,MS_FILTER_SET_VIDEO_SIZE,&vsize); if (ms_filter_get_id(stream->source)!=MS_STATIC_IMAGE_ID) @@ -579,11 +579,7 @@ int video_stream_recv_only_start (VideoStream *stream, RtpProfile *profile, cons ms_error("videostream.c: No codecs available for payload %i:%s.",payload,pt->mime_type); return -1; } -#ifndef WIN32 - stream->output=ms_filter_new(MS_VIDEO_OUT_ID); -#else - stream->output=ms_filter_new(MS_DRAWDIB_DISPLAY_ID); -#endif + stream->output = ms_filter_create_display(display_name); /*force the decoder to output YUV420P */ format=MS_YUV420P; /*ask the size-converter to always output CIF */