]> git.sesse.net Git - vlc/blob - src/ac3_spdif/ac3_spdif.c
fa531afd1a96c52c44da9336c04b44ca704c33b2
[vlc] / src / ac3_spdif / ac3_spdif.c
1 /*****************************************************************************
2  * ac3_spdif.c: ac3 pass-through to external decoder with enabled soundcard
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: ac3_spdif.c,v 1.12 2001/09/30 20:25:13 bozo Exp $
6  *
7  * Authors: Stéphane Borel <stef@via.ecp.fr>
8  *          Juha Yrjola <jyrjola@cc.hut.fi>
9  *          German Gomez Garcia <german@piraos.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include "defs.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>                                              /* memcpy() */
34 #include <fcntl.h>
35
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #include "config.h"
41 #include "common.h"
42 #include "threads.h"
43 #include "mtime.h"
44
45 #include "intf_msg.h"                        /* intf_DbgMsg(), intf_ErrMsg() */
46
47 #include "stream_control.h"
48 #include "input_ext-dec.h"
49
50 #include "audio_output.h"
51
52 #include "ac3_spdif.h"
53 #include "ac3_iec958.h"
54
55 #define FRAME_NB 8
56
57 /****************************************************************************
58  * Local Prototypes
59  ****************************************************************************/
60 static int  InitThread       ( ac3_spdif_thread_t * );
61 static void RunThread        ( ac3_spdif_thread_t * );
62 static void ErrorThread      ( ac3_spdif_thread_t * );
63 static void EndThread        ( ac3_spdif_thread_t * );
64 static void BitstreamCallback( bit_stream_t *, boolean_t );
65
66 /****************************************************************************
67  * spdif_CreateThread: initialize the spdif thread
68  ****************************************************************************/
69 vlc_thread_t spdif_CreateThread( adec_config_t * p_config )
70 {
71     ac3_spdif_thread_t *   p_spdif;
72
73     intf_DbgMsg( "spdif debug: creating ac3 pass-through thread" );
74
75     /* Allocate the memory needed to store the thread's structure */
76     p_spdif = malloc( sizeof(ac3_spdif_thread_t) );
77
78     if( p_spdif == NULL )
79     {
80         intf_ErrMsg ( "spdif error: not enough memory "
81                       "for spdif_CreateThread() to create the new thread");
82         return 0;
83     }
84     
85     /* Temporary buffer to store ac3 frames to be transformed */
86     p_spdif->p_ac3 = malloc( SPDIF_FRAME_SIZE );
87
88     if( p_spdif->p_ac3 == NULL )
89     {
90         free( p_spdif->p_ac3 );
91         return 0;
92     }
93
94     /*
95      * Initialize the thread properties
96      */
97     p_spdif->p_config = p_config;
98     p_spdif->p_fifo = p_config->decoder_config.p_decoder_fifo;
99
100     p_spdif->p_aout_fifo = NULL;
101
102     /* Spawn the ac3 to spdif thread */
103     if (vlc_thread_create(&p_spdif->thread_id, "spdif", 
104                 (vlc_thread_func_t)RunThread, (void *)p_spdif))
105     {
106         intf_ErrMsg( "spdif error: can't spawn spdif thread" );
107         free( p_spdif->p_ac3 );
108         free( p_spdif );
109         return 0;
110     }
111
112     intf_DbgMsg( "spdif debug: spdif thread (%p) created", p_spdif );
113
114     return p_spdif->thread_id;
115 }
116
117 /*
118  * Local functions
119  */
120
121 /****************************************************************************
122  * InitThread: initialize thread data and create output fifo
123  ****************************************************************************/
124 static int InitThread( ac3_spdif_thread_t * p_spdif )
125 {
126     boolean_t b_sync = 0;
127
128     p_spdif->p_config->decoder_config.pf_init_bit_stream(
129             &p_spdif->bit_stream,
130             p_spdif->p_config->decoder_config.p_decoder_fifo,
131             BitstreamCallback, (void*)p_spdif );
132
133     /* Creating the audio output fifo */
134     p_spdif->p_aout_fifo = aout_CreateFifo( AOUT_ADEC_SPDIF_FIFO, 1, 48000, 0,
135                                             SPDIF_FRAME_SIZE, NULL );
136
137     if( p_spdif->p_aout_fifo == NULL )
138     {
139         return -1;
140     }
141
142     intf_WarnMsg( 3, "spdif: aout fifo #%d created",
143                      p_spdif->p_aout_fifo->i_fifo );
144
145     /* Sync word */
146     p_spdif->p_ac3[0] = 0x0b;
147     p_spdif->p_ac3[1] = 0x77;
148
149     /* Find syncword */
150     while( !b_sync )
151     {
152         while( GetBits( &p_spdif->bit_stream, 8 ) != 0x0b );
153         p_spdif->i_real_pts = p_spdif->i_pts;
154         p_spdif->i_pts = 0;
155         b_sync = ( ShowBits( &p_spdif->bit_stream, 8 ) == 0x77 );
156     }
157     RemoveBits( &p_spdif->bit_stream, 8 );
158
159     /* Check stream properties */
160     if( ac3_iec958_parse_syncinfo( p_spdif ) < 0 )
161     {
162         intf_ErrMsg( "spdif error: stream not valid");
163
164         aout_DestroyFifo( p_spdif->p_aout_fifo );
165         return -1;
166     }
167
168     /* Check that we can handle the rate 
169      * FIXME: we should check that we have the same rate for all fifos 
170      * but all rates should be supported by the decoder (32, 44.1, 48) */
171     if( p_spdif->ac3_info.i_sample_rate != 48000 )
172     {
173         intf_ErrMsg( "spdif error: Only 48000 Hz streams supported");
174
175         aout_DestroyFifo( p_spdif->p_aout_fifo );
176         return -1;
177     }
178     p_spdif->p_aout_fifo->l_rate = p_spdif->ac3_info.i_sample_rate;
179
180     GetChunk( &p_spdif->bit_stream, p_spdif->p_ac3 + sizeof(sync_frame_t),
181         p_spdif->ac3_info.i_frame_size - sizeof(sync_frame_t) );
182
183     return 0;
184 }
185
186 /****************************************************************************
187  * RunThread: loop that reads ac3 ES and transform it to
188  * an spdif compliant stream.
189  ****************************************************************************/
190 static void RunThread( ac3_spdif_thread_t * p_spdif )
191 {
192     mtime_t     i_frame_time;
193     boolean_t   b_sync;
194     /* PTS of the current frame */
195     mtime_t     i_current_pts = 0;
196
197     /* Initializing the spdif decoder thread */
198     if( InitThread( p_spdif ) )
199     {
200          p_spdif->p_fifo->b_error = 1;
201     }
202
203     /* Compute the theorical duration of an ac3 frame */
204     i_frame_time = 1000000 * AC3_FRAME_SIZE /
205                              p_spdif->ac3_info.i_sample_rate;
206
207     while( !p_spdif->p_fifo->b_die && !p_spdif->p_fifo->b_error )
208     {
209         /* Handle the dates */
210         if( p_spdif->i_real_pts )
211         {
212             if(i_current_pts + i_frame_time != p_spdif->i_real_pts)
213             {
214                 intf_WarnMsg( 2, "spdif warning: date discontinuity (%d)",
215                               p_spdif->i_real_pts - i_current_pts -
216                               i_frame_time );
217             }
218             i_current_pts = p_spdif->i_real_pts;
219             p_spdif->i_real_pts = 0;
220         }
221         else
222         {
223             i_current_pts += i_frame_time;
224         }
225
226         /* if we're late here the output won't have to play the frame */
227         if( i_current_pts > mdate() )
228         {
229             p_spdif->p_aout_fifo->date[p_spdif->p_aout_fifo->l_end_frame] =
230                 i_current_pts;
231     
232             /* Write in the first free packet of aout fifo */
233             p_spdif->p_iec = ((u8*)(p_spdif->p_aout_fifo->buffer) + 
234                 (p_spdif->p_aout_fifo->l_end_frame * SPDIF_FRAME_SIZE ));
235     
236             /* Build burst to be sent to hardware decoder */
237             ac3_iec958_build_burst( p_spdif );
238     
239             vlc_mutex_lock (&p_spdif->p_aout_fifo->data_lock);
240             p_spdif->p_aout_fifo->l_end_frame = 
241                     (p_spdif->p_aout_fifo->l_end_frame + 1 ) & AOUT_FIFO_SIZE;
242             vlc_mutex_unlock (&p_spdif->p_aout_fifo->data_lock);
243         }
244
245         /* Find syncword again in case of stream discontinuity */
246         /* Here we have p_spdif->i_pts == 0
247          * Therefore a non-zero value after a call to GetBits() means the PES
248          * has changed. */
249         b_sync = 0;
250         while( !b_sync )
251         {
252             while( GetBits( &p_spdif->bit_stream, 8 ) != 0x0b );
253             p_spdif->i_real_pts = p_spdif->i_pts;
254             p_spdif->i_pts = 0;
255             b_sync = ( ShowBits( &p_spdif->bit_stream, 8 ) == 0x77 );
256         }
257         RemoveBits( &p_spdif->bit_stream, 8 );
258
259         /* Read data from bitstream */
260         GetChunk( &p_spdif->bit_stream, p_spdif->p_ac3 + 2,
261                   p_spdif->ac3_info.i_frame_size - 2 );
262     }
263
264     /* If b_error is set, the ac3 spdif thread enters the error loop */
265     if( p_spdif->p_fifo->b_error )
266     {
267         ErrorThread( p_spdif );
268     }
269
270     /* End of the ac3 decoder thread */
271     EndThread( p_spdif );
272
273     return;
274 }
275
276 /*****************************************************************************
277  * ErrorThread : ac3 spdif's RunThread() error loop
278  *****************************************************************************/
279 static void ErrorThread( ac3_spdif_thread_t * p_spdif )
280 {
281     /* We take the lock, because we are going to read/write the start/end
282      * indexes of the decoder fifo */
283     vlc_mutex_lock (&p_spdif->p_fifo->data_lock);
284
285     /* Wait until a `die' order is sent */
286     while( !p_spdif->p_fifo->b_die )
287     {
288         /* Trash all received PES packets */
289         while( !DECODER_FIFO_ISEMPTY( *p_spdif->p_fifo ) )
290         {
291             p_spdif->p_fifo->pf_delete_pes(p_spdif->p_fifo->p_packets_mgt,
292                     DECODER_FIFO_START( *p_spdif->p_fifo ) );
293             DECODER_FIFO_INCSTART( *p_spdif->p_fifo );
294         }
295
296         /* Waiting for the input thread to put new PES packets in the fifo */
297         vlc_cond_wait( &p_spdif->p_fifo->data_wait,
298                        &p_spdif->p_fifo->data_lock );
299     }
300
301     /* We can release the lock before leaving */
302     vlc_mutex_unlock( &p_spdif->p_fifo->data_lock );
303 }
304
305 /*****************************************************************************
306  * EndThread : ac3 spdif thread destruction
307  *****************************************************************************/
308 static void EndThread( ac3_spdif_thread_t * p_spdif )
309 {
310     intf_DbgMsg( "spdif debug: destroying thread %p", p_spdif );
311
312     /* If the audio output fifo was created, we destroy it */
313     if( p_spdif->p_aout_fifo != NULL )
314     {
315         aout_DestroyFifo( p_spdif->p_aout_fifo );
316
317         /* Make sure the output thread leaves the NextFrame() function */
318         vlc_mutex_lock( &(p_spdif->p_aout_fifo->data_lock ) );
319         vlc_cond_signal( &(p_spdif->p_aout_fifo->data_wait ) );
320         vlc_mutex_unlock( &(p_spdif->p_aout_fifo->data_lock ) );
321         
322     }
323
324     /* Destroy descriptor */
325     free( p_spdif->p_config );
326     free( p_spdif->p_ac3 );
327     free( p_spdif );
328
329     intf_DbgMsg ("spdif debug: thread %p destroyed", p_spdif );
330 }
331
332 /*****************************************************************************
333  * BitstreamCallback: Import parameters from the new data/PES packet
334  *****************************************************************************
335  * This function is called by input's NextDataPacket.
336  *****************************************************************************/
337 static void BitstreamCallback ( bit_stream_t * p_bit_stream,
338                                         boolean_t b_new_pes)
339 {
340     ac3_spdif_thread_t *    p_spdif;
341
342     if( b_new_pes )
343     {
344         p_spdif = (ac3_spdif_thread_t *)p_bit_stream->p_callback_arg;
345
346         p_bit_stream->p_byte += 3;
347
348         p_spdif->i_pts =
349             DECODER_FIFO_START( *p_bit_stream->p_decoder_fifo )->i_pts;
350         DECODER_FIFO_START( *p_bit_stream->p_decoder_fifo )->i_pts = 0;
351     }
352 }