]> git.sesse.net Git - vlc/blobdiff - src/audio_output/aout_spdif.c
* ./include/vlc_threads.h, ./src/misc/threads.c: improved the cond_wait
[vlc] / src / audio_output / aout_spdif.c
index 5b55943234ba576fe5f85f656db95327c0c63668..79c69a694f1088ed8749b84c4cda73e7f54a76d2 100644 (file)
@@ -1,8 +1,8 @@
 /*****************************************************************************
- * aout_spdif: ac3 passthrough output
+ * aout_spdif.c: AC3 passthrough output
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
- * $Id: aout_spdif.c,v 1.12 2001/06/09 17:01:22 stef Exp $
+ * $Id: aout_spdif.c,v 1.29 2002/06/01 12:32:01 sam Exp $
  *
  * Authors: Michel Kaempf <maxx@via.ecp.fr>
  *          Stéphane Borel <stef@via.ecp.fr>
 /*****************************************************************************
  * Preamble
  *****************************************************************************/
-#include "defs.h"
-
-#include <stdio.h>                                           /* "intf_msg.h" */
 #include <stdlib.h>                            /* calloc(), malloc(), free() */
 #include <string.h>                                              /* memset() */
 
-#include "config.h"
-#include "common.h"
-#include "threads.h"
-#include "mtime.h"                             /* mtime_t, mdate(), msleep() */
+#include <vlc/vlc.h>
 
-#include "intf_msg.h"                        /* intf_DbgMsg(), intf_ErrMsg() */
+#ifdef HAVE_UNISTD_H
+#   include <unistd.h>
+#endif
 
 #include "audio_output.h"
-#include "aout_common.h"
+#include "aout_spdif.h"
+
+/****************************************************************************
+ * iec958_build_burst: builds an iec958/spdif frame
+ ****************************************************************************/
+void iec958_build_burst( u8 * p_buf, aout_fifo_t * p_fifo )
+{
+    const u8 p_sync[6] = { 0x72, 0xF8, 0x1F, 0x4E, 0x01, 0x00 };
+    int      i_length  = p_fifo->i_frame_size;
+#ifndef HAVE_SWAB
+    /* Skip the first byte if i_length is odd */
+    u16 * p_in  = (u16 *)( p_fifo->buffer + ( i_length & 0x1 ) );
+    u16 * p_out = (u16 *)p_buf;
+#endif
+
+    /* Add the spdif headers */
+    memcpy( p_buf, p_sync, 6 );
+    p_buf[6] = ( i_length * 8 ) & 0xFF;
+    p_buf[7] = ( ( i_length * 8 ) >> 8 ) & 0xFF;
+
+#ifdef HAVE_SWAB
+    swab( p_fifo->buffer + p_fifo->i_start_frame * i_length,
+          p_buf + 8, i_length );
+#else
+    /* i_length should be even */
+    i_length &= ~0x1;
+
+    while( i_length )
+    {
+        *p_out = ( (*p_in & 0x00ff) << 16 ) | ( (*p_in & 0xff00) >> 16 );
+        p_in++;
+        p_out++;
+        i_length -= 2;
+    }
+#endif
+
+    /* Add zeroes to complete the spdif frame,
+     * they will be ignored by the decoder */
+    memset( p_buf + 8 + i_length, 0, SPDIF_FRAME_SIZE - 8 - i_length );
+}
 
-#define BLANK_FRAME_MAX 1000
-#define SLEEP_TIME      16000
 
 /*****************************************************************************
  * aout_SpdifThread: audio output thread that sends raw spdif data
  * This output thread is quite specific as it can only handle one fifo now.
  *
  * Note: spdif can demux up to 8 ac3 streams, and can even take
- * care of time stamps (cf ac3 spec).
+ * care of time stamps (cf ac3 spec) but I'm not sure all decoders
+ * implement it.
  *****************************************************************************/
 void aout_SpdifThread( aout_thread_t * p_aout )
 {
-    u8          pi_spdif_blank [9] =
-                    { 0x72, 0xf8, 0x1f, 0x4e, 0x01, 0x00, 0x08, 0x00, 0x77 };
-    u8          pi_blank[SPDIF_FRAME_SIZE];
     int         i_fifo;
-    int         i_frame;
-    int         i_blank;
-    mtime_t     mplay;
-    mtime_t     mdelta;
-    mtime_t     mlast = 0;
-
-    /* get a blank frame ready */
-    memset( pi_blank, 0, sizeof(pi_blank) );
-    memcpy( pi_blank, pi_spdif_blank, sizeof(pi_spdif_blank) );
-   
-    intf_WarnMsg( 3, "aout info: starting spdif output loop" );
-
-    /* variable used to compute the nnumber of blank frames since the
-     * last significant frame */
-    i_blank = 0;
-    mdelta = 0;
-
-    /* Compute the theorical duration of an ac3 frame */
+    mtime_t     m_frame_time = 0;
+    mtime_t     m_play;
+    mtime_t     m_old;
 
     while( !p_aout->b_die )
     {
-        /* variable to check that we send data to the decoder
-         * once per loop at least */
-        i_frame = 0;
+        i_fifo = 0;
+        /* Find spdif fifo */
+        while( ( p_aout->fifo[i_fifo].i_format != AOUT_FIFO_SPDIF ) &&
+               ( i_fifo < AOUT_MAX_FIFOS ) && !p_aout->b_die )
+        {
+            i_fifo++;
+        }
+
+        m_old = 0;
 
-        /* FIXME: find a way to handle the locks here */
-        for( i_fifo = 0 ; i_fifo < AOUT_MAX_FIFOS ; i_fifo++ )
+        while( !p_aout->b_die && 
+               !p_aout->fifo[i_fifo].b_die )
         {
-            /* the loop read each fifo so that we can change the stream
-             * on the fly but mulitplexing is not handled yet so
-             * the sound will be broken is more than one fifo has data */ 
-            /* TODO: write the muliplexer :) */
-            if( p_aout->fifo[i_fifo].i_type == AOUT_ADEC_SPDIF_FIFO )
+            vlc_mutex_lock( &p_aout->fifo[i_fifo].data_lock );
+            if( !AOUT_FIFO_ISEMPTY( p_aout->fifo[i_fifo] ) )
             {
-                vlc_mutex_lock( &p_aout->fifo[i_fifo].data_lock );
-                if( p_aout->fifo[i_fifo].b_die )
-                {
-                    vlc_mutex_unlock( &p_aout->fifo[i_fifo].data_lock );
-                    aout_FreeFifo( &p_aout->fifo[i_fifo] );
-                }
-                else if( !AOUT_FIFO_ISEMPTY( p_aout->fifo[i_fifo] ) )
-                {
-                    mplay = p_aout->fifo[i_fifo].date[p_aout->fifo[i_fifo].
-                                l_start_frame];
-                    mdelta = mplay - mdate();
+                /* Copy data from fifo to buffer to release the
+                 * lock earlier */
+                iec958_build_burst( p_aout->buffer,
+                                    &p_aout->fifo[i_fifo] );
+
+                m_play = p_aout->fifo[i_fifo].date[p_aout->fifo[i_fifo].
+                             i_start_frame];
+
+                p_aout->fifo[i_fifo].i_start_frame =
+                    (p_aout->fifo[i_fifo].i_start_frame + 1 )
+                    & AOUT_FIFO_SIZE;
+
+                /* Compute the theorical duration of an ac3 frame */
+                m_frame_time = 1000000 * AC3_FRAME_SIZE
+                                       / p_aout->fifo[i_fifo].i_rate;
+
+                vlc_mutex_unlock( &p_aout->fifo[i_fifo].data_lock );
 
-                    if( mdelta < ( 3 * SLEEP_TIME ) )
+                /* Play spdif frame to the external decoder 
+                 * the kernel driver will sleep until the
+                 * dsp buffer is empty enough to accept the data */
+                if( m_play > ( mdate() - m_frame_time ) )
+                {
+                    /* check continuity */
+                    if( (m_play - m_old) != m_frame_time )
                     {
-                        intf_WarnMsg( 12, "spdif out (%d):"
-                                          "playing frame %lld (%lld)",
-                                           i_fifo,
-                                           mdelta,
-                                           mplay-mlast );
-                        mlast = mplay;
-                        /* play spdif frame to the external decoder */
-                        p_aout->pf_play( p_aout,
-                                     ( (byte_t *)p_aout->fifo[i_fifo].buffer
-                                         + p_aout->fifo[i_fifo].l_start_frame
-                                            * SPDIF_FRAME_SIZE ),
-                                     p_aout->fifo[i_fifo].l_frame_size );
-
-                        p_aout->fifo[i_fifo].l_start_frame = 
-                            (p_aout->fifo[i_fifo].l_start_frame + 1 )
-                            & AOUT_FIFO_SIZE;
-                        
-                        i_frame++;
-                        i_blank = 0;
+                        mwait( m_play - m_frame_time );
                     }
                     else
                     {
-                        intf_WarnMsg( 12, "spdif out (%d): early frame %lld", 
-                                            i_fifo, mdelta );
+                        mwait( m_play - 2 * m_frame_time );
                     }
-                    vlc_mutex_unlock( &p_aout->fifo[i_fifo].data_lock );
-                }
-                else
-                {
-                    vlc_mutex_unlock( &p_aout->fifo[i_fifo].data_lock );
-                }
-            }
-        }
+                    m_old = m_play;
+                    
+                    p_aout->pf_getbufinfo( p_aout, 0 );
 
-        if( i_frame )
-        {
-            if( mdelta > 0 )
-            {
-                /* we leave some time for aout fifo to fill and not to stress
-                 * the external decoder too much */
-                msleep( mdelta + SLEEP_TIME );
+                    p_aout->pf_play( p_aout, (byte_t *)p_aout->buffer,
+                                     SPDIF_FRAME_SIZE );
+                }
             }
-            else if( mdelta > -SLEEP_TIME )
+            else
             {
-                msleep( SLEEP_TIME );
-            }
-        }
-        else
-        {
-            /* insert blank frame for stream continuity to
-             * the external decoder */
-            intf_WarnMsg( 6, "spdif warning: blank frame" );
-            p_aout->pf_play( p_aout, pi_blank, SPDIF_FRAME_SIZE );
+                msg_Warn( p_aout, "empty spdif fifo" );
+                while( AOUT_FIFO_ISEMPTY( p_aout->fifo[i_fifo] ) &&
+                       !p_aout->b_die && 
+                       !p_aout->fifo[i_fifo].b_die )
 
-            /* we kill the output if we don't have any stream */
-            if( ++i_blank > BLANK_FRAME_MAX )
-            {
-                p_aout->b_die = 1;
+                {
+                    vlc_mutex_unlock( &p_aout->fifo[i_fifo].data_lock );
+                    msleep( m_frame_time );
+                    vlc_mutex_lock( &p_aout->fifo[i_fifo].data_lock );
+                }
+                vlc_mutex_unlock( &p_aout->fifo[i_fifo].data_lock );
             }
         }
-    }
-    
-    intf_WarnMsg( 3, "aout info: exiting spdif loop" );
-    vlc_mutex_lock( &p_aout->fifos_lock );
 
-    for ( i_fifo = 0; i_fifo < AOUT_MAX_FIFOS; i_fifo++ )
-    {
+        vlc_mutex_lock( &p_aout->fifos_lock );
         aout_FreeFifo( &p_aout->fifo[i_fifo] );
+        vlc_mutex_unlock( &p_aout->fifos_lock );
     }
 
-    vlc_mutex_unlock( &p_aout->fifos_lock );
-
     return;
 }