[PATCH][RFC] Run deinterlacer and decoder in separate threads

Petri Hintukainen <[email protected]> Tue, 15 May 2012 15:13:15 +0300
Newsgroups gmane.comp.video.xine.devel
Message-ID <1337083995.29602.11.camel@ph-NF310>
Hello,

Attached patch runs deinterlacer (tvtime plugin) and video decoder in
separate threads.

It could be further improved by splitting deinterlacing to multiple
threads, for example:
 - pipelining: color space conversion, pulldown detection,
deinterlacing, chroma filter, ...
 - processing frames in slices

Usage:
xine --post tvtime:threads=1

Not deeply tested ...


- Petri

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and 
threat landscape has changed and how IT managers can respond. Discussions 
will include endpoint security, mobile security and the latest in malware 
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/

_______________________________________________
xine-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xine-devel
tvtime_thread.diff (text/x-patch, 7.3 KB)
diff --git a/src/post/deinterlace/xine_plugin.c b/src/post/deinterlace/xine_plugin.c
--- a/src/post/deinterlace/xine_plugin.c
+++ b/src/post/deinterlace/xine_plugin.c
@@ -31,6 +31,8 @@
 #define LOG
 */
 
+#define XINE_ENGINE_INTERNAL  /* non-blocking ticket release */
+
 #include <xine/xine_internal.h>
 #include <xine/post.h>
 #include <xine/xineutils.h>
@@ -79,6 +81,7 @@ typedef struct deinterlace_parameters_s 
   int use_progressive_frame_flag;
   int chroma_filter;
   int cheap_mode;
+  int threads;
 
 } deinterlace_parameters_t;
 
@@ -104,6 +107,8 @@ PARAM_ITEM( POST_PARAM_TYPE_BOOL, chroma
             "apply chroma filter after deinterlacing" )
 PARAM_ITEM( POST_PARAM_TYPE_BOOL, cheap_mode, NULL, 0, 1, 0,
             "skip image format conversion - cheaper but not 100% correct" )
+PARAM_ITEM( POST_PARAM_TYPE_INT, threads, NULL, 0, 1, 0,
+            "use multiple threads" )
 END_PARAM_DESCR( param_descr )
 
 
@@ -136,6 +141,16 @@ struct post_plugin_deinterlace_s {
   vo_frame_t        *recent_frame[NUM_RECENT_FRAMES];
 
   pthread_mutex_t    lock;
+
+  uint8_t            threads;
+  uint8_t            thread_created;
+  pthread_t          worker_thread;
+  pthread_mutex_t    thread_lock;
+  pthread_cond_t     thread_complete; /* thred can acceprt input, thread completed frame, thread finished */
+  pthread_cond_t     thread_start;    /* frame queued for thread */
+  vo_frame_t        *thread_frame;    /* incoming frame for worker thread */
+  xine_stream_t     *thread_stream;
+  int                thread_result;   /* result of last draw */
 };
 
 
@@ -144,10 +159,34 @@ typedef struct post_class_deinterlace_s 
   deinterlace_parameters_t init_param;
 } post_class_deinterlace_t;
 
+static void deinterlace_thread_stop(post_plugin_deinterlace_t *this)
+{
+  if (this->thread_created) {
+
+    pthread_mutex_lock(&this->thread_lock);
+    this->thread_created = 0;
+    pthread_cond_signal(&this->thread_start);
+    pthread_mutex_unlock(&this->thread_lock);
+
+    pthread_join(this->worker_thread, NULL);
+
+    if (this->thread_frame) {
+      this->thread_frame->free(this->thread_frame);
+      this->thread_frame = NULL;
+    }
+
+    pthread_cond_destroy(&this->thread_complete);
+    pthread_cond_destroy(&this->thread_start);
+    pthread_mutex_destroy(&this->thread_lock);
+  }
+}
+
 static void _flush_frames(post_plugin_deinterlace_t *this)
 {
   int i;
 
+  deinterlace_thread_stop(this);
+
   for( i = 0; i < NUM_RECENT_FRAMES; i++ ) {
     if( this->recent_frame[i] ) {
       this->recent_frame[i]->free(this->recent_frame[i]);
@@ -178,6 +217,7 @@ static int set_parameters (xine_post_t *
   this->use_progressive_frame_flag = param->use_progressive_frame_flag;
   this->chroma_filter = param->chroma_filter;
   this->cheap_mode = param->cheap_mode;
+  this->threads = param->threads;
 
   this->tvtime_changed++;
 
@@ -199,6 +239,7 @@ static int get_parameters (xine_post_t *
   param->use_progressive_frame_flag = this->use_progressive_frame_flag;
   param->chroma_filter = this->chroma_filter;
   param->cheap_mode = this->cheap_mode;
+  param->threads = this->threads;
 
   return 1;
 }
@@ -258,6 +299,8 @@ static char * get_static_help (void) {
            "systems to try deinterlace algorithms, in a tradeoff between quality "
            "and cpu usage.\n"
            "\n"
+           "  Threads: run decoder and deinterlacer in sepatate threads\n"
+           "\n"
            "* Uses several algorithms from tvtime and dscaler projects.\n"
            "Deinterlacing methods: (Not all methods are available for all platforms)\n"
            "\n"
@@ -366,6 +409,7 @@ static void *deinterlace_init_plugin(xin
   class->init_param.use_progressive_frame_flag = 1;
   class->init_param.chroma_filter              = 0;
   class->init_param.cheap_mode                 = 0;
+  class->init_param.threads                    = 0;
 
   return &class->class;
 }
@@ -435,6 +479,7 @@ static void deinterlace_dispose(post_plu
   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)this_gen;
 
   if (_x_post_dispose(this_gen)) {
+    deinterlace_thread_stop(this);
     _flush_frames(this);
     pthread_mutex_destroy(&this->lock);
     free(this->tvtime);
@@ -681,7 +738,8 @@ static int deinterlace_build_output_fiel
   return skip;
 }
 
-static int deinterlace_draw(vo_frame_t *frame, xine_stream_t *stream)
+
+static int deinterlace_draw_int(vo_frame_t *frame, xine_stream_t *stream)
 {
   post_video_port_t *port = (post_video_port_t *)frame->port;
   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)port->post;
@@ -924,3 +982,103 @@ static int deinterlace_draw(vo_frame_t *
 
   return skip;
 }
+
+static void *deinterlace_thread(void *p)
+{
+  post_plugin_deinterlace_t *this = p;
+  xine_stream_t *stream;
+  vo_frame_t *frame;
+  int result;
+
+  pthread_mutex_lock(&this->thread_lock);
+
+  while (this->thread_created) {
+
+    if (!this->thread_frame) {
+      pthread_cond_wait(&this->thread_start, &this->thread_lock);
+    }
+
+    if (this->thread_frame) {
+      /* got frame */
+      stream = this->thread_stream;
+      frame  = this->thread_frame;
+      this->thread_frame = NULL;
+
+      /* next frame can be queued now */
+      pthread_cond_signal(&this->thread_complete); /* free queue slot */
+      pthread_mutex_unlock(&this->thread_lock);
+
+      /* deinterlace */
+
+      //_x_post_lock(&this->post);
+      this->post.running_ticket->acquire(this->post.running_ticket, 1);
+
+      result = deinterlace_draw_int(frame, stream);
+      frame->free(frame);
+
+      //_x_post_unlock(&this->post);
+      this->post.running_ticket->release_nonblocking(this->post.running_ticket, 1);
+      //_x_post_rewire(post);
+
+      /* store result */
+      pthread_mutex_lock(&this->thread_lock);
+      this->thread_result = result;
+      pthread_cond_signal(&this->thread_complete); /* frame complete */
+    }
+  }
+
+  pthread_cond_signal(&this->thread_complete);  /* thread finished */
+  pthread_mutex_unlock(&this->thread_lock);
+
+  return NULL;
+}
+
+static void deinterlace_thread_start(post_plugin_deinterlace_t *this)
+{
+  if (this->threads && !this->thread_created) {
+    pthread_mutex_init(&this->thread_lock, NULL);
+    pthread_cond_init(&this->thread_complete, NULL);
+    pthread_cond_init(&this->thread_start, NULL);
+
+    pthread_mutex_lock(&this->thread_lock);
+    pthread_create(&this->worker_thread, NULL, deinterlace_thread, this);
+    this->thread_created = 1;
+    this->thread_result = 0;
+    pthread_mutex_unlock(&this->thread_lock);
+  }
+}
+
+static int deinterlace_draw(vo_frame_t *frame, xine_stream_t *stream)
+{
+  post_video_port_t *port = (post_video_port_t *)frame->port;
+  post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)port->post;
+
+  if (this->threads) {
+    int result = 0;
+
+    deinterlace_thread_start(this);
+
+    pthread_mutex_lock(&this->thread_lock);
+
+    while (this->thread_frame && this->threads) {
+      pthread_cond_wait(&this->thread_complete, &this->thread_lock);
+    }
+
+    if (this->threads) {
+      result = this->thread_result;
+      this->thread_stream = stream;
+      this->thread_frame  = frame;
+      frame->lock(frame);
+      pthread_cond_signal(&this->thread_start);
+
+      pthread_mutex_unlock(&this->thread_lock);
+      return result;
+    }
+
+    pthread_mutex_unlock(&this->thread_lock);
+  }
+
+  deinterlace_thread_stop(this);
+
+  return deinterlace_draw_int(frame, stream);
+}