[vlc-devel] [PATCH 04/10] converter/tospdif: handle dts-hd
Jean-Baptiste Kempf
jb at videolan.org
Wed Jan 18 09:51:33 CET 2017
LGTM.
On Wed, 18 Jan 2017, at 09:20, Thomas Guillem wrote:
> ---
> modules/audio_filter/converter/tospdif.c | 104
> ++++++++++++++++++++++++++++++-
> 1 file changed, 103 insertions(+), 1 deletion(-)
>
> diff --git a/modules/audio_filter/converter/tospdif.c
> b/modules/audio_filter/converter/tospdif.c
> index bcc1f7c4a0..78eae744d8 100644
> --- a/modules/audio_filter/converter/tospdif.c
> +++ b/modules/audio_filter/converter/tospdif.c
> @@ -66,6 +66,10 @@ struct filter_sys_t
> {
> unsigned int i_frame_count;
> } truehd;
> + struct
> + {
> + bool b_skip;
> + } dtshd;
> };
> };
>
> @@ -77,6 +81,7 @@ struct filter_sys_t
> #define IEC61937_DTS1 0x0B
> #define IEC61937_DTS2 0x0C
> #define IEC61937_DTS3 0x0D
> +#define IEC61937_DTSHD 0x11
>
> #define SPDIF_MORE_DATA 1
> #define SPDIF_SUCCESS VLC_SUCCESS
> @@ -107,6 +112,17 @@ static void set_16( filter_t *p_filter, void *p_buf,
> uint16_t i_val )
> SetWLE( p_buf, i_val );
> }
>
> +static void write_16( filter_t *p_filter, uint16_t i_val )
> +{
> + filter_sys_t *p_sys = p_filter->p_sys;
> + assert( p_sys->p_out_buf != NULL );
> +
> + assert( p_sys->p_out_buf->i_buffer - p_sys->i_out_offset
> + >= sizeof( uint16_t ) );
> + set_16( p_filter, &p_sys->p_out_buf->p_buffer[p_sys->i_out_offset],
> i_val );
> + p_sys->i_out_offset += sizeof( uint16_t );
> +}
> +
> static void write_padding( filter_t *p_filter, size_t i_size )
> {
> filter_sys_t *p_sys = p_filter->p_sys;
> @@ -401,6 +417,86 @@ static int write_buffer_dts( filter_t *p_filter,
> block_t *p_in_buf )
> return SPDIF_SUCCESS;
> }
>
> +/* Adapted from libavformat/spdifenc.c:
> + * DTS type IV (DTS-HD) can be transmitted with various frame repetition
> + * periods; longer repetition periods allow for longer packets and
> therefore
> + * higher bitrate. Longer repetition periods mean that the constant
> bitrate of
> + * the output IEC 61937 stream is higher.
> + * The repetition period is measured in IEC 60958 frames (4 bytes).
> + */
> +static int dtshd_get_subtype( unsigned i_frame_length )
> +{
> + switch( i_frame_length )
> + {
> + case 512: return 0x0;
> + case 1024: return 0x1;
> + case 2048: return 0x2;
> + case 4096: return 0x3;
> + case 8192: return 0x4;
> + case 16384: return 0x5;
> + default: return -1;
> + }
> +}
> +
> +/* Adapted from libavformat/spdifenc.c: */
> +static int write_buffer_dtshd( filter_t *p_filter, block_t *p_in_buf )
> +{
> + static const char p_dtshd_start_code[10] = {
> + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe
> + };
> + static const size_t i_dtshd_start_code = sizeof( p_dtshd_start_code
> );
> +
> + filter_sys_t *p_sys = p_filter->p_sys;
> + vlc_dts_header_t core;
> + if( vlc_dts_header_Parse( &core, p_in_buf->p_buffer,
> + p_in_buf->i_buffer ) != VLC_SUCCESS )
> + return SPDIF_ERROR;
> + unsigned i_period = p_filter->fmt_out.audio.i_rate
> + * core.i_frame_length / core.i_rate;
> +
> + int i_subtype = dtshd_get_subtype( i_period );
> + if( i_subtype == -1 )
> + return SPDIF_ERROR;
> +
> + size_t i_in_size = i_dtshd_start_code + 2 + p_in_buf->i_buffer;
> + size_t i_out_size = i_period * 4;
> + uint16_t i_data_type = IEC61937_DTSHD | i_subtype << 8;
> +
> + if( p_filter->p_sys->dtshd.b_skip
> + || i_in_size + SPDIF_HEADER_SIZE > i_out_size )
> + {
> + /* The bitrate is too high, pass only the core part */
> + p_in_buf->i_buffer = core.i_frame_size;
> + i_in_size = i_dtshd_start_code + 2 + p_in_buf->i_buffer;
> + if( i_in_size + SPDIF_HEADER_SIZE > i_out_size )
> + return SPDIF_ERROR;
> +
> + /* Don't try to send substreams anymore. That way, we avoid to
> switch
> + * back and forth between DTD and DTS-HD */
> + p_filter->p_sys->dtshd.b_skip = true;
> + }
> +
> + if( write_init( p_filter, p_in_buf, i_out_size,
> + i_out_size /
> p_filter->fmt_out.audio.i_bytes_per_frame ) )
> + return SPDIF_ERROR;
> +
> + write_data( p_filter, p_dtshd_start_code, i_dtshd_start_code, true
> );
> + write_16( p_filter, p_in_buf->i_buffer );
> + write_buffer( p_filter, p_in_buf );
> +
> + /* Align so that (length_code & 0xf) == 0x8. This is reportedly
> needed
> + * with some receivers, but the exact requirement is unconfirmed. */
> +#define ALIGN(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
> + size_t i_align = ALIGN( i_in_size + 0x8, 0x10 ) - 0x8;
> +#undef ALIGN
> + if( i_align > i_in_size && i_align - i_in_size
> + <= p_sys->p_out_buf->i_buffer - p_sys->i_out_offset )
> + write_padding( p_filter, i_align - i_in_size );
> +
> + write_finalize( p_filter, i_data_type, 1 /* in bytes */ );
> + return SPDIF_SUCCESS;
> +}
> +
> static void Flush( filter_t *p_filter )
> {
> filter_sys_t *p_sys = p_filter->p_sys;
> @@ -442,7 +538,13 @@ static block_t *DoWork( filter_t *p_filter, block_t
> *p_in_buf )
> i_ret = write_buffer_truehd( p_filter, p_in_buf );
> break;
> case VLC_CODEC_DTS:
> - i_ret = write_buffer_dts( p_filter, p_in_buf );
> + /* if the fmt_out is configured for a higher rate than 48kHz
> + * (IEC958 rate), use the DTS-HD framing to pass the DTS
> Core and
> + * or DTS substreams (like DTS-HD MA). */
> + if( p_filter->fmt_out.audio.i_rate > 48000 )
> + i_ret = write_buffer_dtshd( p_filter, p_in_buf );
> + else
> + i_ret = write_buffer_dts( p_filter, p_in_buf );
> break;
> default:
> vlc_assert_unreachable();
> --
> 2.11.0
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
--
Jean-Baptiste Kempf - President
+33 672 704 734
More information about the vlc-devel
mailing list