[FFmpeg-devel] [RFC/PATCH]TrueHD (and E-AC-3) over HDMI

Anssi Hannula anssi.hannula
Mon Aug 2 21:11:45 CEST 2010


Carl Eugen Hoyos kirjoitti sunnuntai, 1. elokuuta 2010 03:16:52:
> Hi!
> 
> Anssi Hannula succeeded in bitstreaming TrueHD over HDMI. To test, it is
> necessary to hack ALSA to correctly set HBR mode:
> http://thread.gmane.org/gmane.linux.alsa.devel/75317
> plus "format |= 0x8000;" in hdmi_setup_stream().

(this works for Intel/NVIDIA HDMI only atm; I may fix atihdmi to use this code
later, though I have no hardware to test)

Also, in the future HBR will likely be set automatically when channels = 8 and
AES0 has 0x02 (non-audio) bit set. This will probably get to alsa in a few
days.

> Attached patch is untested (I am away from my receiver atm), but comments
> are welcome, I will test before end of the week.

Tested and both E-AC3 and TrueHD work. Comments below.

> Will we need a second muxer "hdmi" to refuse muxing E-AC-3 and TrueHD (and
> DTS-HD) into "spdif" or is it the users responsibility to only mux useful
> streams?

I'm very slightly in favor of a single muxer (though IMHO it should be named
'iec61937' and not 'spdif', but that's too late to changenow;  'spdif' to me
means 'IEC (60)958' encapsulation, which in reality is done by the sound
hardware AFAIU).

> And how will the user be able to signal to libavformat if he
> wants the complete DTS-HD stream muxed (for hdmi) or only the DTS core
> (for SPDIF)?

I fear I don't know enough of DTS-HD right now to answer this.. If needed, I
guess we can have two muxers "hdmi" and "spdif" then.

> Index: libavformat/spdif.c
> ===================================================================
> --- libavformat/spdif.c	(Revision 24623)
> +++ libavformat/spdif.c	(Arbeitskopie)
> @@ -82,6 +82,11 @@
>      /// function, which generates codec dependent header information.
>      /// Sets data_type and data_offset
>      int (*header_info) (AVFormatContext *s, AVPacket *pkt);
> +
> +    uint8_t *hd_buf;                ///< allocated buffer to concatenate hd audio frames
> +    int hd_buf_size;                ///< size of the hd audio buffer
> +    int hd_buf_count;               ///< number of frames in the hd audio buffer
> +    int old_pkt_size;               ///< size of all frames in the hd audio buffer
>  } IEC958Context;
[...]
> +static int spdif_header_eac3(AVFormatContext *s, AVPacket *pkt)
> +{
> +    IEC958Context *ctx = s->priv_data;
> +    static const uint8_t eac3_repeat[4] = {6, 3, 2, 1};
> +    int repeat = 1;
> +    if ((pkt->data[4] & 0xc0) != 0xc0) /* fscod */
> +        repeat = eac3_repeat[(pkt->data[4] & 0x30) >> 4]; /* numblkscod */
> +
> +    ctx->hd_buf = av_fast_realloc(ctx->hd_buf, &ctx->hd_buf_size, ctx->old_pkt_size + pkt->size);
> +    if (!ctx->hd_buf)
> +        return AVERROR(ENOMEM);
> +    memcpy(&ctx->hd_buf[ctx->old_pkt_size], pkt->data, pkt->size);
> +    if (++ctx->hd_buf_count < repeat){
> +        ctx->pkt_offset = 0;
> +        ctx->old_pkt_size += pkt->size;
> +        return 0;
> +    }
> +    ctx->hd_buf_count = 0;
> +    ctx->old_pkt_size = 0;
> +
> +    ctx->data_type  = IEC958_EAC3;
> +    ctx->pkt_offset = 24576;
> +    return 0;
> +}
[...]
> @@ -264,8 +347,10 @@
>      ret = ctx->header_info(s, pkt);
>      if (ret < 0)
>          return -1;
> +    if (!ctx->pkt_offset)
> +        return 0;
>  
> -    padding = (ctx->pkt_offset - BURST_HEADER_SIZE - pkt->size) >> 1;
> +    padding = (ctx->pkt_offset - BURST_HEADER_SIZE - ctx->hd_buf_size) >> 1;
>      if (padding < 0) {
>          av_log(s, AV_LOG_ERROR, "bitrate is too high\n");
>          return -1;
> @@ -277,23 +362,24 @@
>      put_le16(s->pb, ctx->pkt_size);  //Pd

ctx->pkt_size is not correct here. It contains the amount of bits in
the last packet. For TrueHD and E-AC3 (and IIRC AAC, maybe some more),
Pd should be the full payload size in bytes. Not that my receiver
cares...

>  #if HAVE_BIGENDIAN
> -    put_buffer(s->pb, pkt->data, pkt->size & ~1);
> +    put_buffer(s->pb, pkt->data, ctx->hd_buf_size & ~1);
>  #else
> -    av_fast_malloc(&ctx->buffer, &ctx->buffer_size, pkt->size + FF_INPUT_BUFFER_PADDING_SIZE);
> +    av_fast_malloc(&ctx->buffer, &ctx->buffer_size, ctx->hd_buf_size + FF_INPUT_BUFFER_PADDING_SIZE);
>      if (!ctx->buffer)
>          return AVERROR(ENOMEM);
> -    bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)pkt->data, pkt->size >> 1);
> -    put_buffer(s->pb, ctx->buffer, pkt->size & ~1);
> +    bswap_buf16((uint16_t *)ctx->buffer, (uint16_t *)ctx->hd_buf, ctx->hd_buf_size >> 1);
> +    put_buffer(s->pb, ctx->buffer, ctx->hd_buf_size & ~1);
>  #endif
>  
>      if (pkt->size & 1)
> -        put_be16(s->pb, pkt->data[pkt->size - 1]);
> +        put_be16(s->pb, ctx->hd_buf[ctx->hd_buf_size - 1]);
>  
>      for (; padding > 0; padding--)
>          put_be16(s->pb, 0);
>  
>      av_log(s, AV_LOG_DEBUG, "type=%x len=%i pkt_offset=%i\n",
>             ctx->data_type, pkt->size, ctx->pkt_offset);
> +    ctx->hd_buf_size = 0;

You are using ctx->hd_buf_size quite a lot, but ctx->hd_buf_size
is the allocated size of the buffer, not the amount of data in it...
Also, zeroing it prevents av_fast_realloc from using the existing buffer.

I guess you could just use ctx->old_pkt_size in place of ctx->hd_buf_size
and drop the zeroing it from spdif_header_eac3() and set it to 61424 for
truehd, using hd_buf_size just for av_fast_realloc(). Or something like that.


>      put_flush_packet(s->pb);
>      return 0;
> 

I guess truehd/e-ac3 support warrants a Changelog line as well.

-- 
Anssi Hannula



More information about the ffmpeg-devel mailing list