1 /*****************************************************************************
2 * tospdif.c : encapsulates A/52 and DTS frames into S/PDIF packets
3 *****************************************************************************
4 * Copyright (C) 2002, 2006-2016 VLC authors and VideoLAN
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * Stéphane Borel <stef@via.ecp.fr>
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU Lesser General Public License as published by
15 * the Free Software Foundation; either version 2.1 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public License
24 * along with this program; if not, write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
38 #include <vlc_filter.h>
40 #include "../packetizer/a52.h"
41 #include "../packetizer/dts_header.h"
43 static int Open( vlc_object_t
* );
44 static void Close( vlc_object_t
* );
47 set_category( CAT_AUDIO
)
48 set_subcategory( SUBCAT_AUDIO_MISC
)
49 set_description( N_("Audio filter for A/52/DTS->S/PDIF encapsulation") )
50 set_capability( "audio converter", 10 )
51 set_callbacks( Open
, Close
)
63 unsigned int i_nb_blocks_substream0
;
67 unsigned int i_frame_count
;
72 #define SPDIF_HEADER_SIZE 8
74 #define IEC61937_AC3 0x01
75 #define IEC61937_EAC3 0x15
76 #define IEC61937_TRUEHD 0x16
77 #define IEC61937_DTS1 0x0B
78 #define IEC61937_DTS2 0x0C
79 #define IEC61937_DTS3 0x0D
81 #define SPDIF_MORE_DATA 1
82 #define SPDIF_SUCCESS VLC_SUCCESS
83 #define SPDIF_ERROR VLC_EGENERIC
85 static bool is_big_endian( filter_t
*p_filter
, block_t
*p_in_buf
)
87 switch( p_filter
->fmt_in
.audio
.i_format
)
92 case VLC_CODEC_TRUEHD
:
95 return p_in_buf
->p_buffer
[0] == 0x1F
96 || p_in_buf
->p_buffer
[0] == 0x7F;
98 vlc_assert_unreachable();
102 static inline void write_16( filter_t
*p_filter
, void *p_buf
, uint16_t i_val
)
104 if( p_filter
->fmt_out
.audio
.i_format
== VLC_CODEC_SPDIFB
)
105 SetWBE( p_buf
, i_val
);
107 SetWLE( p_buf
, i_val
);
110 static void write_padding( filter_t
*p_filter
, size_t i_size
)
112 filter_sys_t
*p_sys
= p_filter
->p_sys
;
113 assert( p_sys
->p_out_buf
!= NULL
);
115 assert( p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
>= i_size
);
117 uint8_t *p_out
= &p_sys
->p_out_buf
->p_buffer
[p_sys
->i_out_offset
];
118 memset( p_out
, 0, i_size
);
119 p_sys
->i_out_offset
+= i_size
;
122 static void write_data( filter_t
*p_filter
, const void *p_buf
, size_t i_size
,
123 bool b_input_big_endian
)
125 filter_sys_t
*p_sys
= p_filter
->p_sys
;
126 assert( p_sys
->p_out_buf
!= NULL
);
128 bool b_output_big_endian
=
129 p_filter
->fmt_out
.audio
.i_format
== VLC_CODEC_SPDIFB
;
130 uint8_t *p_out
= &p_sys
->p_out_buf
->p_buffer
[p_sys
->i_out_offset
];
131 const uint8_t *p_in
= p_buf
;
133 assert( p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
>= i_size
);
135 if( b_input_big_endian
!= b_output_big_endian
)
136 swab( p_in
, p_out
, i_size
& ~1 );
138 memcpy( p_out
, p_in
, i_size
& ~1 );
139 p_sys
->i_out_offset
+= ( i_size
& ~1 );
143 assert( p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
>= 2 );
144 p_out
+= ( i_size
& ~1 );
145 write_16( p_filter
, p_out
, p_in
[i_size
- 1] << 8 );
146 p_sys
->i_out_offset
+= 2;
150 static void write_buffer( filter_t
*p_filter
, block_t
*p_in_buf
)
152 write_data( p_filter
, p_in_buf
->p_buffer
, p_in_buf
->i_buffer
,
153 is_big_endian( p_filter
, p_in_buf
) );
154 p_filter
->p_sys
->p_out_buf
->i_length
+= p_in_buf
->i_length
;
157 static int write_init( filter_t
*p_filter
, block_t
*p_in_buf
,
158 size_t i_out_size
, unsigned i_nb_samples
)
160 filter_sys_t
*p_sys
= p_filter
->p_sys
;
162 assert( p_sys
->p_out_buf
== NULL
);
163 assert( i_out_size
> SPDIF_HEADER_SIZE
&& ( i_out_size
& 3 ) == 0 );
165 p_sys
->p_out_buf
= block_Alloc( i_out_size
);
166 if( !p_sys
->p_out_buf
)
168 p_sys
->p_out_buf
->i_dts
= p_in_buf
->i_dts
;
169 p_sys
->p_out_buf
->i_pts
= p_in_buf
->i_pts
;
170 p_sys
->p_out_buf
->i_nb_samples
= i_nb_samples
;
172 p_sys
->i_out_offset
= SPDIF_HEADER_SIZE
; /* Place for the S/PDIF header */
176 static void write_finalize( filter_t
*p_filter
, uint16_t i_data_type
,
177 uint8_t i_length_mul
)
179 filter_sys_t
*p_sys
= p_filter
->p_sys
;
180 assert( p_sys
->p_out_buf
!= NULL
);
181 uint8_t *p_out
= p_sys
->p_out_buf
->p_buffer
;
183 assert( p_sys
->i_out_offset
> SPDIF_HEADER_SIZE
);
184 assert( i_data_type
!= 0 );
185 assert( i_length_mul
== 1 || i_length_mul
== 8 );
188 write_16( p_filter
, &p_out
[0], 0xf872 ); /* syncword 1 */
189 write_16( p_filter
, &p_out
[2], 0x4e1f ); /* syncword 2 */
190 write_16( p_filter
, &p_out
[4], i_data_type
); /* data type */
191 /* length in bits or bytes */
192 write_16( p_filter
, &p_out
[6], ( p_sys
->i_out_offset
- SPDIF_HEADER_SIZE
)
196 if( p_sys
->i_out_offset
< p_sys
->p_out_buf
->i_buffer
)
197 write_padding( p_filter
,
198 p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
);
201 static int write_buffer_ac3( filter_t
*p_filter
, block_t
*p_in_buf
)
203 if( unlikely( p_in_buf
->i_buffer
< 6
204 || p_in_buf
->i_buffer
> A52_FRAME_NB
* 4
205 || p_in_buf
->i_nb_samples
!= A52_FRAME_NB
) )
207 /* Input is not correctly packetizer. Try to parse the buffer in order
208 * to get the mandatory informations to play AC3 over S/PDIF */
209 vlc_a52_header_t a52
;
210 if( vlc_a52_header_Parse( &a52
, p_in_buf
->p_buffer
, p_in_buf
->i_buffer
)
211 != VLC_SUCCESS
|| a52
.b_eac3
)
213 p_in_buf
->i_buffer
= a52
.i_size
;
214 p_in_buf
->i_nb_samples
= a52
.i_samples
;
217 if( write_init( p_filter
, p_in_buf
, A52_FRAME_NB
* 4, A52_FRAME_NB
) )
219 write_buffer( p_filter
, p_in_buf
);
220 write_finalize( p_filter
, IEC61937_AC3
|
221 ( ( p_in_buf
->p_buffer
[5] & 0x7 ) << 8 ) /* bsmod */,
224 return SPDIF_SUCCESS
;
227 static int write_buffer_eac3( filter_t
*p_filter
, block_t
*p_in_buf
)
229 filter_sys_t
*p_sys
= p_filter
->p_sys
;
231 vlc_a52_header_t a52
= { };
232 if( vlc_a52_header_Parse( &a52
, p_in_buf
->p_buffer
, p_in_buf
->i_buffer
)
236 p_in_buf
->i_buffer
= a52
.i_size
;
237 p_in_buf
->i_nb_samples
= a52
.i_samples
;
239 if( !p_sys
->p_out_buf
240 && write_init( p_filter
, p_in_buf
, AOUT_SPDIF_SIZE
* 4, AOUT_SPDIF_SIZE
) )
242 if( p_in_buf
->i_buffer
> p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
)
245 write_buffer( p_filter
, p_in_buf
);
249 if( ( a52
.eac3
.strmtyp
== EAC3_STRMTYP_INDEPENDENT
250 || a52
.eac3
.strmtyp
== EAC3_STRMTYP_AC3_CONVERT
)
251 && a52
.i_blocks_per_sync_frame
!= 6 )
253 /* cf. Annex E 2.3.1.2 of AC3 spec */
254 if( a52
.eac3
.i_substreamid
== 0 )
255 p_sys
->spec
.eac3
.i_nb_blocks_substream0
256 += a52
.i_blocks_per_sync_frame
;
258 if( p_sys
->spec
.eac3
.i_nb_blocks_substream0
!= 6 )
259 return SPDIF_MORE_DATA
;
261 p_sys
->spec
.eac3
.i_nb_blocks_substream0
= 0;
263 write_finalize( p_filter
, IEC61937_EAC3
, 1 /* in bytes */ );
264 return SPDIF_SUCCESS
;
267 return SPDIF_MORE_DATA
;
271 /* Adapted from libavformat/spdifenc.c:
272 * It seems Dolby TrueHD frames have to be encapsulated in MAT frames before
273 * they can be encapsulated in IEC 61937.
274 * Here we encapsulate 24 TrueHD frames in a single MAT frame, padding them
275 * to achieve constant rate.
276 * The actual format of a MAT frame is unknown, but the below seems to work.
277 * However, it seems it is not actually necessary for the 24 TrueHD frames to
278 * be in an exact alignment with the MAT frame
280 static int write_buffer_truehd( filter_t
*p_filter
, block_t
*p_in_buf
)
282 #define TRUEHD_FRAME_OFFSET 2560
284 filter_sys_t
*p_sys
= p_filter
->p_sys
;
286 if( !p_sys
->p_out_buf
287 && write_init( p_filter
, p_in_buf
, 61440, 61440 / 16 ) )
291 if( p_sys
->spec
.truehd
.i_frame_count
== 0 )
293 static const char p_mat_start_code
[20] = {
294 0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00,
295 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, 0x49, 0x80, 0x77, 0xE0
297 write_data( p_filter
, p_mat_start_code
, 20, true );
298 /* We need to include the S/PDIF header in the first MAT frame */
299 i_padding
= TRUEHD_FRAME_OFFSET
- p_in_buf
->i_buffer
- 20
302 else if( p_sys
->spec
.truehd
.i_frame_count
== 11 )
304 /* The middle mat code need to be at the ((2560 * 12) - 4) offset */
305 i_padding
= TRUEHD_FRAME_OFFSET
- p_in_buf
->i_buffer
- 4;
307 else if( p_sys
->spec
.truehd
.i_frame_count
== 12 )
309 static const char p_mat_middle_code
[12] = {
310 0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA,
311 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0
313 write_data( p_filter
, p_mat_middle_code
, 12, true );
314 i_padding
= TRUEHD_FRAME_OFFSET
- p_in_buf
->i_buffer
- ( 12 - 4 );
316 else if( p_sys
->spec
.truehd
.i_frame_count
== 23 )
318 static const char p_mat_end_code
[16] = {
319 0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00,
320 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11
323 /* The end mat code need to be at the ((2560 * 24) - 24) offset */
324 i_padding
= TRUEHD_FRAME_OFFSET
- p_in_buf
->i_buffer
- 24;
326 if( i_padding
< 0 || p_in_buf
->i_buffer
+ i_padding
>
327 p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
)
330 write_buffer( p_filter
, p_in_buf
);
331 write_padding( p_filter
, i_padding
);
332 write_data( p_filter
, p_mat_end_code
, 16, true );
333 write_finalize( p_filter
, IEC61937_TRUEHD
, 1 /* in bytes */ );
334 p_sys
->spec
.truehd
.i_frame_count
= 0;
335 return SPDIF_SUCCESS
;
338 i_padding
= TRUEHD_FRAME_OFFSET
- p_in_buf
->i_buffer
;
340 if( i_padding
< 0 || p_in_buf
->i_buffer
+ i_padding
>
341 p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
)
344 write_buffer( p_filter
, p_in_buf
);
345 write_padding( p_filter
, i_padding
);
346 p_sys
->spec
.truehd
.i_frame_count
++;
347 return SPDIF_MORE_DATA
;
350 static int write_buffer_dts( filter_t
*p_filter
, block_t
*p_in_buf
)
352 uint16_t i_data_type
;
353 if( p_in_buf
->i_nb_samples
== 0 )
355 /* Input is not correctly packetizer. Try to parse the buffer in order
356 * to get the mandatory informations to play DTS over S/PDIF */
357 vlc_dts_header_t header
;
358 if( vlc_dts_header_Parse( &header
, p_in_buf
->p_buffer
,
359 p_in_buf
->i_buffer
) != VLC_SUCCESS
)
361 p_in_buf
->i_nb_samples
= header
.i_frame_length
;
362 p_in_buf
->i_buffer
= header
.i_frame_size
;
364 switch( p_in_buf
->i_nb_samples
)
367 i_data_type
= IEC61937_DTS1
;
370 i_data_type
= IEC61937_DTS2
;
373 i_data_type
= IEC61937_DTS3
;
376 msg_Err( p_filter
, "Frame size %d not supported",
377 p_in_buf
->i_nb_samples
);
381 if( p_in_buf
->i_buffer
> p_in_buf
->i_nb_samples
* 4
382 || write_init( p_filter
, p_in_buf
, p_in_buf
->i_nb_samples
* 4,
383 p_in_buf
->i_nb_samples
) )
385 write_buffer( p_filter
, p_in_buf
);
386 write_finalize( p_filter
, i_data_type
, 8 /* in bits */ );
387 return SPDIF_SUCCESS
;
390 static void Flush( filter_t
*p_filter
)
392 filter_sys_t
*p_sys
= p_filter
->p_sys
;
394 if( p_sys
->p_out_buf
!= NULL
)
396 block_Release( p_sys
->p_out_buf
);
397 p_sys
->p_out_buf
= NULL
;
399 memset( &p_sys
->spec
, 0, sizeof( p_sys
->spec
) );
402 static block_t
*DoWork( filter_t
*p_filter
, block_t
*p_in_buf
)
404 filter_sys_t
*p_sys
= p_filter
->p_sys
;
405 block_t
*p_out_buf
= NULL
;
408 switch( p_filter
->fmt_in
.audio
.i_format
)
411 i_ret
= write_buffer_ac3( p_filter
, p_in_buf
);
414 i_ret
= write_buffer_eac3( p_filter
, p_in_buf
);
417 case VLC_CODEC_TRUEHD
:
418 i_ret
= write_buffer_truehd( p_filter
, p_in_buf
);
421 i_ret
= write_buffer_dts( p_filter
, p_in_buf
);
424 vlc_assert_unreachable();
430 assert( p_sys
->p_out_buf
->i_buffer
== p_sys
->i_out_offset
);
431 p_out_buf
= p_sys
->p_out_buf
;
432 p_sys
->p_out_buf
= NULL
;
434 case SPDIF_MORE_DATA
:
441 block_Release( p_in_buf
);
445 static int Open( vlc_object_t
*p_this
)
447 filter_t
*p_filter
= (filter_t
*)p_this
;
450 if( ( p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_DTS
&&
451 p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_A52
&&
452 p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_EAC3
&&
453 p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_MLP
&&
454 p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_TRUEHD
) ||
455 ( p_filter
->fmt_out
.audio
.i_format
!= VLC_CODEC_SPDIFL
&&
456 p_filter
->fmt_out
.audio
.i_format
!= VLC_CODEC_SPDIFB
) )
459 p_sys
= p_filter
->p_sys
= malloc( sizeof(filter_sys_t
) );
460 if( unlikely( p_sys
== NULL
) )
462 p_sys
->p_out_buf
= NULL
;
464 memset( &p_sys
->spec
, 0, sizeof( p_sys
->spec
) );
466 p_filter
->pf_audio_filter
= DoWork
;
467 p_filter
->pf_flush
= Flush
;
472 static void Close( vlc_object_t
*p_this
)
474 filter_t
*p_filter
= (filter_t
*)p_this
;
477 free( p_filter
->p_sys
);