[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