/*****************************************************************************
- * 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;
}