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
6 * Authors: Christophe Massiot <massiot@via.ecp.fr>
7 * Stéphane Borel <stef@via.ecp.fr>
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2.1 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
39 #include "../../packetizer/a52.h"
40 #include "../../packetizer/dts_header.h"
42 static int Open( vlc_object_t
* );
45 set_category( CAT_AUDIO
)
46 set_subcategory( SUBCAT_AUDIO_AFILTER
)
47 set_description( N_("Audio filter for A/52/DTS->S/PDIF encapsulation") )
48 set_capability( "audio converter", 10 )
61 unsigned int i_nb_blocks
;
65 unsigned int i_frame_count
;
74 #define SPDIF_HEADER_SIZE 8
76 #define IEC61937_AC3 0x01
77 #define IEC61937_EAC3 0x15
78 #define IEC61937_TRUEHD 0x16
79 #define IEC61937_DTS1 0x0B
80 #define IEC61937_DTS2 0x0C
81 #define IEC61937_DTS3 0x0D
82 #define IEC61937_DTSHD 0x11
84 #define SPDIF_MORE_DATA 1
85 #define SPDIF_SUCCESS VLC_SUCCESS
86 #define SPDIF_ERROR VLC_EGENERIC
88 static bool is_big_endian( filter_t
*p_filter
, block_t
*p_in_buf
)
90 switch( p_filter
->fmt_in
.audio
.i_format
)
95 case VLC_CODEC_TRUEHD
:
99 return p_in_buf
->p_buffer
[0] == 0x1F
100 || p_in_buf
->p_buffer
[0] == 0x7F;
102 vlc_assert_unreachable();
106 static void set_16( filter_t
*p_filter
, void *p_buf
, uint16_t i_val
)
108 if( p_filter
->fmt_out
.audio
.i_format
== VLC_CODEC_SPDIFB
)
109 SetWBE( p_buf
, i_val
);
111 SetWLE( p_buf
, i_val
);
114 static void write_16( filter_t
*p_filter
, uint16_t i_val
)
116 filter_sys_t
*p_sys
= p_filter
->p_sys
;
117 assert( p_sys
->p_out_buf
!= NULL
);
119 assert( p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
120 >= sizeof( uint16_t ) );
121 set_16( p_filter
, &p_sys
->p_out_buf
->p_buffer
[p_sys
->i_out_offset
], i_val
);
122 p_sys
->i_out_offset
+= sizeof( uint16_t );
125 static void write_padding( filter_t
*p_filter
, size_t i_size
)
127 filter_sys_t
*p_sys
= p_filter
->p_sys
;
128 assert( p_sys
->p_out_buf
!= NULL
);
130 assert( p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
>= i_size
);
132 uint8_t *p_out
= &p_sys
->p_out_buf
->p_buffer
[p_sys
->i_out_offset
];
133 memset( p_out
, 0, i_size
);
134 p_sys
->i_out_offset
+= i_size
;
137 static void write_data( filter_t
*p_filter
, const void *p_buf
, size_t i_size
,
138 bool b_input_big_endian
)
140 filter_sys_t
*p_sys
= p_filter
->p_sys
;
141 assert( p_sys
->p_out_buf
!= NULL
);
143 bool b_output_big_endian
=
144 p_filter
->fmt_out
.audio
.i_format
== VLC_CODEC_SPDIFB
;
145 uint8_t *p_out
= &p_sys
->p_out_buf
->p_buffer
[p_sys
->i_out_offset
];
146 const uint8_t *p_in
= p_buf
;
148 assert( p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
>= i_size
);
150 if( b_input_big_endian
!= b_output_big_endian
)
151 swab( p_in
, p_out
, i_size
& ~1 );
153 memcpy( p_out
, p_in
, i_size
& ~1 );
154 p_sys
->i_out_offset
+= ( i_size
& ~1 );
158 assert( p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
>= 2 );
159 p_out
+= ( i_size
& ~1 );
160 set_16( p_filter
, p_out
, p_in
[i_size
- 1] << 8 );
161 p_sys
->i_out_offset
+= 2;
165 static void write_buffer( filter_t
*p_filter
, block_t
*p_in_buf
)
167 filter_sys_t
*p_sys
= p_filter
->p_sys
;
168 write_data( p_filter
, p_in_buf
->p_buffer
, p_in_buf
->i_buffer
,
169 is_big_endian( p_filter
, p_in_buf
) );
170 p_sys
->p_out_buf
->i_length
+= p_in_buf
->i_length
;
173 static int write_init( filter_t
*p_filter
, block_t
*p_in_buf
,
174 size_t i_out_size
, unsigned i_nb_samples
)
176 filter_sys_t
*p_sys
= p_filter
->p_sys
;
178 assert( p_sys
->p_out_buf
== NULL
);
179 assert( i_out_size
> SPDIF_HEADER_SIZE
&& ( i_out_size
& 3 ) == 0 );
181 p_sys
->p_out_buf
= block_Alloc( i_out_size
);
182 if( !p_sys
->p_out_buf
)
184 p_sys
->p_out_buf
->i_dts
= p_in_buf
->i_dts
;
185 p_sys
->p_out_buf
->i_pts
= p_in_buf
->i_pts
;
186 p_sys
->p_out_buf
->i_nb_samples
= i_nb_samples
;
188 p_sys
->i_out_offset
= SPDIF_HEADER_SIZE
; /* Place for the S/PDIF header */
192 static void write_finalize( filter_t
*p_filter
, uint16_t i_data_type
,
193 uint8_t i_length_mul
)
195 filter_sys_t
*p_sys
= p_filter
->p_sys
;
196 assert( p_sys
->p_out_buf
!= NULL
);
197 assert( i_data_type
!= 0 );
198 uint8_t *p_out
= p_sys
->p_out_buf
->p_buffer
;
201 assert( p_sys
->i_out_offset
> SPDIF_HEADER_SIZE
);
202 assert( i_length_mul
== 1 || i_length_mul
== 8 );
204 set_16( p_filter
, &p_out
[0], 0xf872 ); /* syncword 1 */
205 set_16( p_filter
, &p_out
[2], 0x4e1f ); /* syncword 2 */
206 set_16( p_filter
, &p_out
[4], i_data_type
); /* data type */
207 /* length in bits or bytes */
208 set_16( p_filter
, &p_out
[6],
209 ( p_sys
->i_out_offset
- SPDIF_HEADER_SIZE
) * i_length_mul
);
212 if( p_sys
->i_out_offset
< p_sys
->p_out_buf
->i_buffer
)
213 write_padding( p_filter
,
214 p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
);
217 static int write_buffer_ac3( filter_t
*p_filter
, block_t
*p_in_buf
)
219 static const size_t a52_size
= A52_FRAME_NB
* 4;
221 if( unlikely( p_in_buf
->i_buffer
< 6
222 || p_in_buf
->i_buffer
+ SPDIF_HEADER_SIZE
> a52_size
223 || p_in_buf
->i_nb_samples
!= A52_FRAME_NB
) )
225 /* Input is not correctly packetizer. Try to parse the buffer in order
226 * to get the mandatory informations to play AC3 over S/PDIF */
227 vlc_a52_header_t a52
;
228 if( vlc_a52_header_Parse( &a52
, p_in_buf
->p_buffer
, p_in_buf
->i_buffer
)
229 != VLC_SUCCESS
|| a52
.b_eac3
|| a52
.i_size
> p_in_buf
->i_buffer
)
231 p_in_buf
->i_buffer
= a52
.i_size
;
232 p_in_buf
->i_nb_samples
= a52
.i_samples
;
235 if( p_in_buf
->i_buffer
+ SPDIF_HEADER_SIZE
> a52_size
236 || write_init( p_filter
, p_in_buf
, a52_size
, A52_FRAME_NB
) )
238 write_buffer( p_filter
, p_in_buf
);
239 write_finalize( p_filter
, IEC61937_AC3
|
240 ( ( p_in_buf
->p_buffer
[5] & 0x7 ) << 8 ) /* bsmod */,
243 return SPDIF_SUCCESS
;
247 static int write_buffer_eac3( filter_t
*p_filter
, block_t
*p_in_buf
)
249 filter_sys_t
*p_sys
= p_filter
->p_sys
;
251 /* The input block can contain the following:
252 * a/ One EAC3 independent stream (with 1, 2, 3 or 6 audio blocks per
254 * b/ One AC3 stream followed by one EAC3 dependent stream (with 6 audio
255 * blocks per syncframe)
256 * c/ One EAC3 independent stream followed by one EAC3 dependent stream
257 * (with 1, 2, 3 or 6 audio blocks per syncframe)
259 * One IEC61937_EAC3 frame must contain 6 audio blocks per syncframe. This
260 * function will gather input blocks until it reaches this amount of audio
263 * Example: for the c/ case with 1 audio blocks per syncframe, a
264 * IEC61937_EAC3 frame will contain 12 a52 streams: 6 independent + 6
265 * dependent EAC3 streams.
268 vlc_a52_header_t a52
;
269 if( vlc_a52_header_Parse( &a52
, p_in_buf
->p_buffer
, p_in_buf
->i_buffer
)
270 != VLC_SUCCESS
|| a52
.i_size
> p_in_buf
->i_buffer
)
273 if( p_in_buf
->i_buffer
> a52
.i_size
)
275 /* Check if the next stream is an eac3 dependent one */
276 vlc_a52_header_t a52_dep
;
277 const uint8_t *dep_buf
= &p_in_buf
->p_buffer
[a52
.i_size
];
278 const size_t dep_size
= p_in_buf
->i_buffer
- a52
.i_size
;
280 if( vlc_a52_header_Parse( &a52_dep
, dep_buf
, dep_size
) != VLC_SUCCESS
281 || a52_dep
.i_size
> dep_size
282 || !a52_dep
.b_eac3
|| a52_dep
.bs
.eac3
.strmtyp
!= EAC3_STRMTYP_DEPENDENT
283 || p_in_buf
->i_buffer
> a52
.i_size
+ a52_dep
.i_size
)
287 if( !p_sys
->p_out_buf
288 && write_init( p_filter
, p_in_buf
, AOUT_SPDIF_SIZE
* 4, AOUT_SPDIF_SIZE
) )
290 if( p_in_buf
->i_buffer
> p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
)
293 write_buffer( p_filter
, p_in_buf
);
295 /* cf. Annex E 2.3 of AC3 spec */
296 p_sys
->eac3
.i_nb_blocks
+= a52
.i_blocks_per_sync_frame
;
298 if( p_sys
->eac3
.i_nb_blocks
< 6 )
299 return SPDIF_MORE_DATA
;
300 else if ( p_sys
->eac3
.i_nb_blocks
> 6 )
303 write_finalize( p_filter
, IEC61937_EAC3
, 1 /* in bytes */ );
304 p_sys
->eac3
.i_nb_blocks
= 0;
305 return SPDIF_SUCCESS
;
308 /* Adapted from libavformat/spdifenc.c:
309 * It seems Dolby TrueHD frames have to be encapsulated in MAT frames before
310 * they can be encapsulated in IEC 61937.
311 * Here we encapsulate 24 TrueHD frames in a single MAT frame, padding them
312 * to achieve constant rate.
313 * The actual format of a MAT frame is unknown, but the below seems to work.
314 * However, it seems it is not actually necessary for the 24 TrueHD frames to
315 * be in an exact alignment with the MAT frame
317 static int write_buffer_truehd( filter_t
*p_filter
, block_t
*p_in_buf
)
319 #define TRUEHD_FRAME_OFFSET 2560
321 filter_sys_t
*p_sys
= p_filter
->p_sys
;
323 if( !p_sys
->p_out_buf
324 && write_init( p_filter
, p_in_buf
, 61440, 61440 / 16 ) )
328 if( p_sys
->truehd
.i_frame_count
== 0 )
330 static const char p_mat_start_code
[20] = {
331 0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00,
332 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, 0x49, 0x80, 0x77, 0xE0
334 write_data( p_filter
, p_mat_start_code
, 20, true );
335 /* We need to include the S/PDIF header in the first MAT frame */
336 i_padding
= TRUEHD_FRAME_OFFSET
- p_in_buf
->i_buffer
- 20
339 else if( p_sys
->truehd
.i_frame_count
== 11 )
341 /* The middle mat code need to be at the ((2560 * 12) - 4) offset */
342 i_padding
= TRUEHD_FRAME_OFFSET
- p_in_buf
->i_buffer
- 4;
344 else if( p_sys
->truehd
.i_frame_count
== 12 )
346 static const char p_mat_middle_code
[12] = {
347 0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA,
348 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0
350 write_data( p_filter
, p_mat_middle_code
, 12, true );
351 i_padding
= TRUEHD_FRAME_OFFSET
- p_in_buf
->i_buffer
- ( 12 - 4 );
353 else if( p_sys
->truehd
.i_frame_count
== 23 )
355 static const char p_mat_end_code
[16] = {
356 0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00,
357 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11
360 /* The end mat code need to be at the ((2560 * 24) - 24) offset */
361 i_padding
= TRUEHD_FRAME_OFFSET
- p_in_buf
->i_buffer
- 24;
363 if( i_padding
< 0 || p_in_buf
->i_buffer
+ i_padding
>
364 p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
)
367 write_buffer( p_filter
, p_in_buf
);
368 write_padding( p_filter
, i_padding
);
369 write_data( p_filter
, p_mat_end_code
, 16, true );
370 write_finalize( p_filter
, IEC61937_TRUEHD
, 1 /* in bytes */ );
371 p_sys
->truehd
.i_frame_count
= 0;
372 return SPDIF_SUCCESS
;
375 i_padding
= TRUEHD_FRAME_OFFSET
- p_in_buf
->i_buffer
;
377 if( i_padding
< 0 || p_in_buf
->i_buffer
+ i_padding
>
378 p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
)
381 write_buffer( p_filter
, p_in_buf
);
382 write_padding( p_filter
, i_padding
);
383 p_sys
->truehd
.i_frame_count
++;
384 return SPDIF_MORE_DATA
;
387 static int write_buffer_dts( filter_t
*p_filter
, block_t
*p_in_buf
)
389 uint16_t i_data_type
;
390 filter_sys_t
*p_sys
= p_filter
->p_sys
;
392 /* Only send the DTS core part */
393 vlc_dts_header_t core
;
394 if( vlc_dts_header_Parse( &core
, p_in_buf
->p_buffer
,
395 p_in_buf
->i_buffer
) != VLC_SUCCESS
)
397 p_in_buf
->i_nb_samples
= core
.i_frame_length
;
398 p_in_buf
->i_buffer
= core
.i_frame_size
;
400 switch( p_in_buf
->i_nb_samples
)
403 i_data_type
= IEC61937_DTS1
;
406 i_data_type
= IEC61937_DTS2
;
409 i_data_type
= IEC61937_DTS3
;
412 msg_Err( p_filter
, "Frame size %d not supported",
413 p_in_buf
->i_nb_samples
);
417 if( core
.syncword
== DTS_SYNC_CORE_14BITS_BE
||
418 core
.syncword
== DTS_SYNC_CORE_14BITS_LE
)
420 if( p_in_buf
->i_buffer
> p_in_buf
->i_nb_samples
* 4 )
422 if( write_init( p_filter
, p_in_buf
, p_in_buf
->i_nb_samples
* 4,
423 p_in_buf
->i_nb_samples
) )
426 uint8_t *p_out
= &p_sys
->p_out_buf
->p_buffer
[p_sys
->i_out_offset
];
427 ssize_t i_size
= vlc_dts_header_Convert14b16b( p_out
,
428 p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
,
429 p_in_buf
->p_buffer
, p_in_buf
->i_buffer
,
430 p_filter
->fmt_out
.audio
.i_format
== VLC_CODEC_SPDIFL
);
434 p_sys
->i_out_offset
+= i_size
;
435 p_sys
->p_out_buf
->i_length
+= p_in_buf
->i_length
;
439 if( p_in_buf
->i_buffer
+ SPDIF_HEADER_SIZE
> p_in_buf
->i_nb_samples
* 4 )
442 if( write_init( p_filter
, p_in_buf
, p_in_buf
->i_nb_samples
* 4,
443 p_in_buf
->i_nb_samples
) )
445 write_buffer( p_filter
, p_in_buf
);
448 write_finalize( p_filter
, i_data_type
, 8 /* in bits */ );
449 return SPDIF_SUCCESS
;
452 /* Adapted from libavformat/spdifenc.c:
453 * DTS type IV (DTS-HD) can be transmitted with various frame repetition
454 * periods; longer repetition periods allow for longer packets and therefore
455 * higher bitrate. Longer repetition periods mean that the constant bitrate of
456 * the output IEC 61937 stream is higher.
457 * The repetition period is measured in IEC 60958 frames (4 bytes).
459 static int dtshd_get_subtype( unsigned i_frame_length
)
461 switch( i_frame_length
)
463 case 512: return 0x0;
464 case 1024: return 0x1;
465 case 2048: return 0x2;
466 case 4096: return 0x3;
467 case 8192: return 0x4;
468 case 16384: return 0x5;
473 /* Adapted from libavformat/spdifenc.c: */
474 static int write_buffer_dtshd( filter_t
*p_filter
, block_t
*p_in_buf
)
476 static const char p_dtshd_start_code
[10] = {
477 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe
479 static const size_t i_dtshd_start_code
= sizeof( p_dtshd_start_code
);
481 filter_sys_t
*p_sys
= p_filter
->p_sys
;
482 vlc_dts_header_t core
;
483 if( vlc_dts_header_Parse( &core
, p_in_buf
->p_buffer
,
484 p_in_buf
->i_buffer
) != VLC_SUCCESS
)
486 unsigned i_period
= p_filter
->fmt_out
.audio
.i_rate
487 * core
.i_frame_length
/ core
.i_rate
;
489 int i_subtype
= dtshd_get_subtype( i_period
);
490 if( i_subtype
== -1 )
493 size_t i_in_size
= i_dtshd_start_code
+ 2 + p_in_buf
->i_buffer
;
494 size_t i_out_size
= i_period
* 4;
495 uint16_t i_data_type
= IEC61937_DTSHD
| i_subtype
<< 8;
497 if( p_sys
->dtshd
.b_skip
498 || i_in_size
+ SPDIF_HEADER_SIZE
> i_out_size
)
500 /* The bitrate is too high, pass only the core part */
501 p_in_buf
->i_buffer
= core
.i_frame_size
;
502 i_in_size
= i_dtshd_start_code
+ 2 + p_in_buf
->i_buffer
;
503 if( i_in_size
+ SPDIF_HEADER_SIZE
> i_out_size
)
506 /* Don't try to send substreams anymore. That way, we avoid to switch
507 * back and forth between DTD and DTS-HD */
508 p_sys
->dtshd
.b_skip
= true;
511 if( write_init( p_filter
, p_in_buf
, i_out_size
,
512 i_out_size
/ p_filter
->fmt_out
.audio
.i_bytes_per_frame
) )
515 write_data( p_filter
, p_dtshd_start_code
, i_dtshd_start_code
, true );
516 write_16( p_filter
, p_in_buf
->i_buffer
);
517 write_buffer( p_filter
, p_in_buf
);
519 /* Align so that (length_code & 0xf) == 0x8. This is reportedly needed
520 * with some receivers, but the exact requirement is unconfirmed. */
521 size_t i_align
= vlc_align( i_in_size
+ 0x8, 0x10 ) - 0x8;
523 if( i_align
> i_in_size
&& i_align
- i_in_size
524 <= p_sys
->p_out_buf
->i_buffer
- p_sys
->i_out_offset
)
525 write_padding( p_filter
, i_align
- i_in_size
);
527 write_finalize( p_filter
, i_data_type
, 1 /* in bytes */ );
528 return SPDIF_SUCCESS
;
531 static void Flush( filter_t
*p_filter
)
533 filter_sys_t
*p_sys
= p_filter
->p_sys
;
535 if( p_sys
->p_out_buf
!= NULL
)
537 block_Release( p_sys
->p_out_buf
);
538 p_sys
->p_out_buf
= NULL
;
540 switch( p_filter
->fmt_in
.audio
.i_format
)
542 case VLC_CODEC_TRUEHD
:
543 p_sys
->truehd
.i_frame_count
= 0;
546 p_sys
->eac3
.i_nb_blocks
= 0;
553 static block_t
*DoWork( filter_t
*p_filter
, block_t
*p_in_buf
)
555 filter_sys_t
*p_sys
= p_filter
->p_sys
;
556 block_t
*p_out_buf
= NULL
;
559 switch( p_filter
->fmt_in
.audio
.i_format
)
562 i_ret
= write_buffer_ac3( p_filter
, p_in_buf
);
565 i_ret
= write_buffer_eac3( p_filter
, p_in_buf
);
568 case VLC_CODEC_TRUEHD
:
569 i_ret
= write_buffer_truehd( p_filter
, p_in_buf
);
571 case VLC_CODEC_DTSHD
:
572 i_ret
= write_buffer_dtshd( p_filter
, p_in_buf
);
575 i_ret
= write_buffer_dts( p_filter
, p_in_buf
);
578 vlc_assert_unreachable();
584 assert( p_sys
->p_out_buf
->i_buffer
== p_sys
->i_out_offset
);
585 p_out_buf
= p_sys
->p_out_buf
;
586 p_sys
->p_out_buf
= NULL
;
588 case SPDIF_MORE_DATA
:
591 filter_Flush( p_filter
);
595 block_Release( p_in_buf
);
599 static void Close( filter_t
*p_filter
)
602 free( p_filter
->p_sys
);
605 static int Open( vlc_object_t
*p_this
)
607 filter_t
*p_filter
= (filter_t
*)p_this
;
610 if( ( p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_DTS
&&
611 p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_DTSHD
&&
612 p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_A52
&&
613 p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_EAC3
&&
614 p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_MLP
&&
615 p_filter
->fmt_in
.audio
.i_format
!= VLC_CODEC_TRUEHD
) ||
616 ( p_filter
->fmt_out
.audio
.i_format
!= VLC_CODEC_SPDIFL
&&
617 p_filter
->fmt_out
.audio
.i_format
!= VLC_CODEC_SPDIFB
) )
620 p_sys
= p_filter
->p_sys
= calloc( 1, sizeof(filter_sys_t
) );
621 if( unlikely( p_sys
== NULL
) )
624 static const struct vlc_filter_operations filter_ops
=
626 .filter_audio
= DoWork
,
630 p_filter
->ops
= &filter_ops
;