changeset 15449:a7966365a197

improve playback with mplayer -tv immediatemode=0
author henry
date Sat, 14 May 2005 05:42:37 +0000
parents 324046793f7c
children 0202bf87c830
files libmpdemux/tvi_v4l2.c
diffstat 1 files changed, 73 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/libmpdemux/tvi_v4l2.c	Sat May 14 01:14:58 2005 +0000
+++ b/libmpdemux/tvi_v4l2.c	Sat May 14 05:42:37 2005 +0000
@@ -118,10 +118,16 @@
     volatile int                shutdown;
 
     double                      audio_secs_per_block;
+    long long                   audio_usecs_per_block;
     long long                   audio_skew_total;
     long long                   audio_skew_delta_total;
     long			audio_recv_blocks_total;
     long			audio_sent_blocks_total;
+    pthread_mutex_t             audio_mutex;
+    int                         audio_insert_null_samples;
+    volatile long               audio_null_blocks_inserted;
+    volatile long long          dropped_frames_timeshift;
+    long long                   dropped_frames_compensated;
 } priv_t;
 
 #include "tvi_def.h"
@@ -144,8 +150,8 @@
     This may be an useful translation table for some others:
 
     IMGFMT_RGB8  == V4L2_PIX_FMT_RGB332
-    IMGFMT_RGB15 == V4L2_PIX_FMT_RGB555
-    IMGFMT_RGB16 == V4L2_PIX_FMT_RGB565
+    IMGFMT_BGR15 == V4L2_PIX_FMT_RGB555
+    IMGFMT_BGR16 == V4L2_PIX_FMT_RGB565
     IMGFMT_RGB24 == V4L2_PIX_FMT_RGB24
     IMGFMT_RGB32 == V4L2_PIX_FMT_RGB32
     IMGFMT_BGR24 == V4L2_PIX_FMT_BGR24
@@ -841,6 +847,7 @@
     if (!tv_param_noaudio && !tv_param_immediate) {
 	pthread_join(priv->audio_grabber_thread, NULL);
 	pthread_mutex_destroy(&priv->skew_mutex);
+	pthread_mutex_destroy(&priv->audio_mutex);
     }
 
     if (priv->input.audioset) {
@@ -1168,6 +1175,7 @@
 	priv->audio_secs_per_block = (double)priv->audio_in.blocksize/(priv->audio_in.samplerate
 								    *priv->audio_in.channels
 								    *priv->audio_in.bytes_per_sample);
+	priv->audio_usecs_per_block = 1e6*priv->audio_secs_per_block;
 	priv->audio_head = 0;
 	priv->audio_tail = 0;
 	priv->audio_cnt = 0;
@@ -1177,6 +1185,13 @@
 	priv->audio_skew_delta_total = 0;
 	priv->audio_recv_blocks_total = 0;
 	priv->audio_sent_blocks_total = 0;
+	priv->audio_null_blocks_inserted = 0;
+	priv->audio_insert_null_samples = 0;
+	priv->dropped_frames_timeshift = 0;
+	priv->dropped_frames_compensated = 0;
+
+	pthread_mutex_init(&priv->skew_mutex, NULL);
+	pthread_mutex_init(&priv->audio_mutex, NULL);
     }
 
     /* setup video parameters */
@@ -1210,6 +1225,8 @@
 	return 0;
     }
 
+    pthread_mutex_init(&priv->video_buffer_mutex, NULL);
+
     priv->video_head = 0;
     priv->video_tail = 0;
     priv->video_cnt = 0;
@@ -1318,7 +1335,7 @@
 static void *video_grabber(void *data)
 {
     priv_t *priv = (priv_t*)data;
-    long long skew, prev_skew, xskew, interval, prev_interval;
+    long long skew, prev_skew, xskew, interval, prev_interval, delta;
     int i;
     int framesize = priv->format.fmt.pix.height*priv->format.fmt.pix.width*
 	pixfmt2depth(priv->format.fmt.pix.pixelformat)/8;
@@ -1341,7 +1358,6 @@
     priv->streamon = 1;
 
     if (!tv_param_noaudio) {
-	pthread_mutex_init(&priv->skew_mutex, NULL);
 	pthread_create(&priv->audio_grabber_thread, NULL, audio_grabber, priv);
     }
 
@@ -1424,6 +1440,7 @@
 //	fprintf(stderr, "idx = %d, ts = %lf\n", buf.index, (double)(priv->curr_frame) / 1e6);
 
 	interval = priv->curr_frame - priv->first_frame;
+	delta = interval - prev_interval;
 
 	if (!priv->immediate_mode) {
 	    // interpolate the skew in time
@@ -1431,17 +1448,17 @@
 	    xskew = priv->audio_skew + (interval - priv->audio_skew_measure_time)*priv->audio_skew_factor;
 	    pthread_mutex_unlock(&priv->skew_mutex);
  	    // correct extreme skew changes to avoid (especially) moving backwards in time
-	    if (xskew - prev_skew > (interval - prev_interval)*MAX_SKEW_DELTA) {
-		skew = prev_skew + (interval - prev_interval)*MAX_SKEW_DELTA;
-	    } else if (xskew - prev_skew < -(interval - prev_interval)*MAX_SKEW_DELTA) {
-		skew = prev_skew - (interval - prev_interval)*MAX_SKEW_DELTA;
+	    if (xskew - prev_skew > delta*MAX_SKEW_DELTA) {
+		skew = prev_skew + delta*MAX_SKEW_DELTA;
+	    } else if (xskew - prev_skew < -delta*MAX_SKEW_DELTA) {
+		skew = prev_skew - delta*MAX_SKEW_DELTA;
 	    } else {
 		skew = xskew;
 	    }
 	}
 
 	mp_msg(MSGT_TV, MSGL_DBG3, "\nfps = %lf, interval = %lf, a_skew = %f, corr_skew = %f\n",
-	       (interval != prev_interval) ? (double)1e6/(interval - prev_interval) : -1,
+	       delta ? (double)1e6/delta : -1,
 	       (double)1e-6*interval, (double)1e-6*xskew, (double)1e-6*skew);
 	mp_msg(MSGT_TV, MSGL_DBG3, "vcnt = %d, acnt = %d\n", priv->video_cnt, priv->audio_cnt);
 
@@ -1469,6 +1486,11 @@
 	if (priv->video_cnt == priv->video_buffer_size_current) {
 	    if (!priv->immediate_mode) {
 		mp_msg(MSGT_TV, MSGL_ERR, "\nvideo buffer full - dropping frame\n");
+		if (priv->audio_insert_null_samples) {
+		    pthread_mutex_lock(&priv->audio_mutex);
+		    priv->dropped_frames_timeshift += delta;
+		    pthread_mutex_unlock(&priv->audio_mutex);
+		}
 	    }
 	} else {
 	    if (priv->immediate_mode) {
@@ -1478,8 +1500,16 @@
 		// negative skew => there are more audio samples, increase interval
 		// positive skew => less samples, shorten the interval
 		priv->video_timebuffer[priv->video_tail] = interval - skew;
+		if (priv->audio_insert_null_samples && priv->video_timebuffer[priv->video_tail] > 0) {
+		    pthread_mutex_lock(&priv->audio_mutex);
+		    priv->video_timebuffer[priv->video_tail] += 
+			(priv->audio_null_blocks_inserted
+			 - priv->dropped_frames_timeshift/priv->audio_usecs_per_block)
+			*priv->audio_usecs_per_block;
+		    pthread_mutex_unlock(&priv->audio_mutex);
+		}
 	    }
-		
+
 	    copy_frame(priv, priv->video_ringbuffer[priv->video_tail], priv->map[buf.index].addr);
 	    priv->video_tail = (priv->video_tail+1)%priv->video_buffer_size_current;
 	    priv->video_cnt++;
@@ -1585,7 +1615,7 @@
 	current_time = (long long)1e6*tv.tv_sec + tv.tv_usec - priv->audio_start_time;
 
 	if (priv->audio_recv_blocks_total < priv->aud_skew_cnt*2) {
-	    start_time_avg += (long long)1e6*tv.tv_sec + tv.tv_usec - 1e6*priv->audio_secs_per_block*priv->audio_recv_blocks_total;
+	    start_time_avg += (long long)1e6*tv.tv_sec + tv.tv_usec - priv->audio_usecs_per_block*priv->audio_recv_blocks_total;
 	    priv->audio_start_time = start_time_avg/(priv->audio_recv_blocks_total+1);
 	}
 
@@ -1595,7 +1625,7 @@
 	// put the current skew into the ring buffer
 	priv->audio_skew_total -= priv->audio_skew_buffer[audio_skew_ptr];
 	priv->audio_skew_buffer[audio_skew_ptr] = current_time
-	    - 1e6*priv->audio_secs_per_block*priv->audio_recv_blocks_total;
+	    - priv->audio_usecs_per_block*priv->audio_recv_blocks_total;
 	priv->audio_skew_total += priv->audio_skew_buffer[audio_skew_ptr];
 
 	pthread_mutex_lock(&priv->skew_mutex);
@@ -1643,6 +1673,7 @@
 	
 //	fprintf(stderr, "audio_skew = %lf, delta = %lf\n", (double)priv->audio_skew/1e6, (double)priv->audio_skew_delta_total/1e6);
 
+	pthread_mutex_lock(&priv->audio_mutex);
 	if ((priv->audio_tail+1) % priv->audio_buffer_size == priv->audio_head) {
 	    mp_msg(MSGT_TV, MSGL_ERR, "\ntoo bad - dropping audio frame !\n");
 	    priv->audio_drop++;
@@ -1650,6 +1681,7 @@
 	    priv->audio_tail = (priv->audio_tail+1) % priv->audio_buffer_size;
 	    priv->audio_cnt++;
 	}
+	pthread_mutex_unlock(&priv->audio_mutex);
     }
     return NULL;
 }
@@ -1659,25 +1691,49 @@
     mp_dbg(MSGT_TV, MSGL_DBG2, "grab_audio_frame(priv=%p, buffer=%p, len=%d)\n",
 	priv, buffer, len);
 
-    if (priv->first) {
-        pthread_create(&priv->video_grabber_thread, NULL, video_grabber, priv);
-        priv->first = 0;
+    // hack: if grab_audio_frame is called first, it means we are used by mplayer
+    // => switch to the mode which outputs audio immediately, even if
+    // it should be silence
+    if (priv->first) priv->audio_insert_null_samples = 1;
+
+    pthread_mutex_lock(&priv->audio_mutex);
+    while (priv->audio_insert_null_samples
+	   && priv->dropped_frames_timeshift - priv->dropped_frames_compensated >= priv->audio_usecs_per_block) {
+	// some frames were dropped - drop the corresponding number of audio blocks
+	if (priv->audio_drop) {
+	    priv->audio_drop--;
+	} else {
+	    if (priv->audio_head == priv->audio_tail) break;
+	    priv->audio_head = (priv->audio_head+1) % priv->audio_buffer_size;
+	}
+	priv->dropped_frames_compensated += priv->audio_usecs_per_block;
     }
 
     // compensate for dropped audio frames
     if (priv->audio_drop && (priv->audio_head == priv->audio_tail)) {
 	priv->audio_drop--;
-	priv->audio_sent_blocks_total++;
 	memset(buffer, 0, len);
-	return (double)priv->audio_sent_blocks_total*priv->audio_secs_per_block;
+	goto out;
     }
 
+    if (priv->audio_insert_null_samples && (priv->audio_head == priv->audio_tail)) {
+	// return silence to avoid desync and stuttering
+	memset(buffer, 0, len);
+	priv->audio_null_blocks_inserted++;
+	goto out;
+    }
+
+    pthread_mutex_unlock(&priv->audio_mutex);
     while (priv->audio_head == priv->audio_tail) {
+	// this is mencoder => just wait until some audio is available
 	usleep(10000);
     }
+//    pthread_mutex_lock(&priv->audio_mutex);
     memcpy(buffer, priv->audio_ringbuffer+priv->audio_head*priv->audio_in.blocksize, len);
     priv->audio_head = (priv->audio_head+1) % priv->audio_buffer_size;
     priv->audio_cnt--;
+out:
+    pthread_mutex_unlock(&priv->audio_mutex);
     priv->audio_sent_blocks_total++;
     return (double)priv->audio_sent_blocks_total*priv->audio_secs_per_block;
 }