Message ID | 20181120181702.17988-1-onemda@gmail.com |
---|---|
State | Accepted |
Headers | show |
On 11/20/2018 3:17 PM, Paul B Mahol wrote: > Signed-off-by: Paul B Mahol <onemda@gmail.com> > --- > doc/bitstream_filters.texi | 4 + > libavcodec/Makefile | 1 + > libavcodec/bitstream_filters.c | 1 + > libavcodec/truehd_core_bsf.c | 182 +++++++++++++++++++++++++++++++++ > 4 files changed, 188 insertions(+) > create mode 100644 libavcodec/truehd_core_bsf.c > > diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi > index 655a2c1e63..15c578aa8a 100644 > --- a/doc/bitstream_filters.texi > +++ b/doc/bitstream_filters.texi > @@ -634,6 +634,10 @@ This can be useful for debugging low-level stream issues. > > Supports H.264, H.265, MPEG-2 and VP9. > > +@section truehd_core > + > +Extract the core from a TrueHD stream, dropping ATMOS data. > + > @section vp9_metadata > > Modify metadata embedded in a VP9 stream. > diff --git a/libavcodec/Makefile b/libavcodec/Makefile > index 716f26d191..bcced2e371 100644 > --- a/libavcodec/Makefile > +++ b/libavcodec/Makefile > @@ -1085,6 +1085,7 @@ OBJS-$(CONFIG_PRORES_METADATA_BSF) += prores_metadata_bsf.o > OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += remove_extradata_bsf.o > OBJS-$(CONFIG_TEXT2MOVSUB_BSF) += movsub_bsf.o > OBJS-$(CONFIG_TRACE_HEADERS_BSF) += trace_headers_bsf.o > +OBJS-$(CONFIG_TRUEHD_CORE_BSF) += truehd_core_bsf.o > OBJS-$(CONFIG_VP9_METADATA_BSF) += vp9_metadata_bsf.o > OBJS-$(CONFIG_VP9_RAW_REORDER_BSF) += vp9_raw_reorder_bsf.o > OBJS-$(CONFIG_VP9_SUPERFRAME_BSF) += vp9_superframe_bsf.o > diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c > index 2a8598bac2..2c999d3c1d 100644 > --- a/libavcodec/bitstream_filters.c > +++ b/libavcodec/bitstream_filters.c > @@ -51,6 +51,7 @@ extern const AVBitStreamFilter ff_prores_metadata_bsf; > extern const AVBitStreamFilter ff_remove_extradata_bsf; > extern const AVBitStreamFilter ff_text2movsub_bsf; > extern const AVBitStreamFilter ff_trace_headers_bsf; > +extern const AVBitStreamFilter ff_truehd_core_bsf; > extern const AVBitStreamFilter ff_vp9_metadata_bsf; > extern const AVBitStreamFilter ff_vp9_raw_reorder_bsf; > extern const AVBitStreamFilter ff_vp9_superframe_bsf; > diff --git a/libavcodec/truehd_core_bsf.c b/libavcodec/truehd_core_bsf.c > new file mode 100644 > index 0000000000..0449719185 > --- /dev/null > +++ b/libavcodec/truehd_core_bsf.c > @@ -0,0 +1,182 @@ > +/* > + * Copyright (c) 2018 Paul B Mahol > + * > + * This file is part of FFmpeg. > + * > + * FFmpeg is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * FFmpeg is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with FFmpeg; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > + */ > + > +#include "avcodec.h" > +#include "bsf.h" > +#include "get_bits.h" > +#include "mlp_parser.h" > +#include "mlp.h" Missing deps in configure or Makefile for mlp parser and whatever else is needed. > + > +typedef struct AccessUnit { > + uint8_t bits[4]; > + uint16_t offset; > + uint16_t optional; > +} AccessUnit; > + > +typedef struct TrueHDCoreContext { > + const AVClass *class; > + > + AccessUnit units[4]; If this is going to be overwritten on every frame and old state doesn't matter, then allocate it on stack. > + > + MLPHeaderInfo hdr; > +} TrueHDCoreContext; > + > +static int truehd_core_filter(AVBSFContext *ctx, AVPacket *out) > +{ > + TrueHDCoreContext *s = ctx->priv_data; > + GetBitContext gbc; > + AVPacket *in; > + int ret, i, size, last_offset = 0; > + int in_size, out_size; > + int have_header = 0; > + int substream_bits = 0; > + int start, end; > + uint16_t dts; > + > + ret = ff_bsf_get_packet(ctx, &in); > + if (ret < 0) > + return ret; > + > + ret = init_get_bits8(&gbc, in->data, 4); No need for init_get_bits8() if it's going to be 4 bytes. Use init_get_bits() and skip all the pointless overflow checks. > + if (ret < 0) > + goto fail; > + > + skip_bits(&gbc, 4); > + in_size = get_bits(&gbc, 12) * 2; > + if (in_size < 4 || in_size > in->size) > + goto fail; > + > + out_size = in_size; > + dts = get_bits(&gbc, 16); > + > + ret = init_get_bits8(&gbc, in->data + 4, in->size - 4); > + if (ret < 0) > + goto fail; > + > + if (show_bits_long(&gbc, 32) == 0xf8726fba) { > + if ((ret = ff_mlp_read_major_sync(ctx, &s->hdr, &gbc)) != 0) > + goto fail; > + have_header = 1; > + } > + > + start = get_bits_count(&gbc); > + for (i = 0; i < s->hdr.num_substreams; i++) { > + for (int j = 0; j < 4; j++) > + s->units[i].bits[j] = get_bits1(&gbc); > + > + s->units[i].offset = get_bits(&gbc, 12) * 2; > + if (i < FFMIN(s->hdr.num_substreams, 3)) { > + last_offset = s->units[i].offset; > + substream_bits += 16; > + } > + > + if (s->units[i].bits[0]) { > + s->units[i].optional = get_bits(&gbc, 16); > + if (i < FFMIN(s->hdr.num_substreams, 3)) > + substream_bits += 16; > + } > + } > + end = get_bits_count(&gbc); > + > + ret = av_packet_copy_props(out, in); Move this to the end. See below. > + if (ret < 0) > + goto fail; > + > + size = ((end + 7) >> 3) + 4 + last_offset; > + if (size >= 0 && size <= in->size) > + out_size = size; > + ret = av_grow_packet(out, out_size); out is clear/unallocated , so use av_new_packet() instead, and put it... > + if (ret < 0) > + goto fail; > + > + if (out_size < in_size) { > + int bpos = 0, reduce = (end - start - substream_bits) >> 4; > + uint16_t parity_nibble = 0; > + uint16_t auheader; > + ...here. > + AV_WB16(out->data + 2, dts); > + parity_nibble = dts; > + out->size -= reduce * 2; > + parity_nibble ^= out->size / 2; > + > + if (have_header) { > + memcpy(out->data + 4, in->data + 4, 28); > + out->data[16 + 4] = (out->data[16 + 4] & 0x0f) | (FFMIN(s->hdr.num_substreams, 3) << 4); > + out->data[25 + 4] = out->data[25 + 4] & 0xfe; > + out->data[26 + 4] = 0xff; > + out->data[27 + 4] = 0xff; > + AV_WL16(out->data + 4 + 26, ff_mlp_checksum16(out->data + 4, 26)); > + } > + > + for (i = 0; i < FFMIN(s->hdr.num_substreams, 3); i++) { > + uint16_t substr_hdr = 0; > + > + substr_hdr |= (s->units[i].bits[0] << 15); > + substr_hdr |= (s->units[i].bits[1] << 14); > + substr_hdr |= (s->units[i].bits[2] << 13); > + substr_hdr |= (s->units[i].bits[3] << 12); > + substr_hdr |= (s->units[i].offset / 2) & 0x0FFF; > + > + AV_WB16(out->data + have_header * 28 + 4 + bpos, substr_hdr); > + > + parity_nibble ^= out->data[have_header * 28 + 4 + bpos++]; > + parity_nibble ^= out->data[have_header * 28 + 4 + bpos++]; > + > + if (s->units[i].bits[0]) { > + AV_WB16(out->data + have_header * 28 + 4 + bpos, s->units[i].optional); > + > + parity_nibble ^= out->data[have_header * 28 + 4 + bpos++]; > + parity_nibble ^= out->data[have_header * 28 + 4 + bpos++]; > + } > + } > + > + parity_nibble ^= parity_nibble >> 8; > + parity_nibble ^= parity_nibble >> 4; > + parity_nibble &= 0xF; > + > + memcpy(out->data + have_header * 28 + 4 + bpos, > + in->data + 4 + (end >> 3), > + out_size - (4 + (end >> 3))); > + auheader = (parity_nibble ^ 0xF) << 12; > + auheader |= (out->size / 2) & 0x0fff; > + AV_WB16(out->data, auheader); Put the av_packet_copy_props() call here. > + } else { > + memcpy(out->data, in->data, out_size); av_packet_move_ref(out, in); out->size = out_size; There's no need to allocate and copy data and props when the output packet is going to be the same as the input. > + } > + > + av_packet_free(&in); > + > + return 0; > +fail: > + av_packet_unref(out); > + av_packet_free(&in); > + return ret; Combine this for both failure and success cases. Check for ret < 0 to call av_packet_unref(out), and return ret regardless of value. > +} > + > +static const enum AVCodecID codec_ids[] = { > + AV_CODEC_ID_TRUEHD, AV_CODEC_ID_NONE, > +}; > + > +const AVBitStreamFilter ff_truehd_core_bsf = { > + .name = "truehd_core", > + .priv_data_size = sizeof(TrueHDCoreContext), > + .filter = truehd_core_filter, > + .codec_ids = codec_ids, > +}; >
diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index 655a2c1e63..15c578aa8a 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -634,6 +634,10 @@ This can be useful for debugging low-level stream issues. Supports H.264, H.265, MPEG-2 and VP9. +@section truehd_core + +Extract the core from a TrueHD stream, dropping ATMOS data. + @section vp9_metadata Modify metadata embedded in a VP9 stream. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 716f26d191..bcced2e371 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1085,6 +1085,7 @@ OBJS-$(CONFIG_PRORES_METADATA_BSF) += prores_metadata_bsf.o OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += remove_extradata_bsf.o OBJS-$(CONFIG_TEXT2MOVSUB_BSF) += movsub_bsf.o OBJS-$(CONFIG_TRACE_HEADERS_BSF) += trace_headers_bsf.o +OBJS-$(CONFIG_TRUEHD_CORE_BSF) += truehd_core_bsf.o OBJS-$(CONFIG_VP9_METADATA_BSF) += vp9_metadata_bsf.o OBJS-$(CONFIG_VP9_RAW_REORDER_BSF) += vp9_raw_reorder_bsf.o OBJS-$(CONFIG_VP9_SUPERFRAME_BSF) += vp9_superframe_bsf.o diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index 2a8598bac2..2c999d3c1d 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -51,6 +51,7 @@ extern const AVBitStreamFilter ff_prores_metadata_bsf; extern const AVBitStreamFilter ff_remove_extradata_bsf; extern const AVBitStreamFilter ff_text2movsub_bsf; extern const AVBitStreamFilter ff_trace_headers_bsf; +extern const AVBitStreamFilter ff_truehd_core_bsf; extern const AVBitStreamFilter ff_vp9_metadata_bsf; extern const AVBitStreamFilter ff_vp9_raw_reorder_bsf; extern const AVBitStreamFilter ff_vp9_superframe_bsf; diff --git a/libavcodec/truehd_core_bsf.c b/libavcodec/truehd_core_bsf.c new file mode 100644 index 0000000000..0449719185 --- /dev/null +++ b/libavcodec/truehd_core_bsf.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2018 Paul B Mahol + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avcodec.h" +#include "bsf.h" +#include "get_bits.h" +#include "mlp_parser.h" +#include "mlp.h" + +typedef struct AccessUnit { + uint8_t bits[4]; + uint16_t offset; + uint16_t optional; +} AccessUnit; + +typedef struct TrueHDCoreContext { + const AVClass *class; + + AccessUnit units[4]; + + MLPHeaderInfo hdr; +} TrueHDCoreContext; + +static int truehd_core_filter(AVBSFContext *ctx, AVPacket *out) +{ + TrueHDCoreContext *s = ctx->priv_data; + GetBitContext gbc; + AVPacket *in; + int ret, i, size, last_offset = 0; + int in_size, out_size; + int have_header = 0; + int substream_bits = 0; + int start, end; + uint16_t dts; + + ret = ff_bsf_get_packet(ctx, &in); + if (ret < 0) + return ret; + + ret = init_get_bits8(&gbc, in->data, 4); + if (ret < 0) + goto fail; + + skip_bits(&gbc, 4); + in_size = get_bits(&gbc, 12) * 2; + if (in_size < 4 || in_size > in->size) + goto fail; + + out_size = in_size; + dts = get_bits(&gbc, 16); + + ret = init_get_bits8(&gbc, in->data + 4, in->size - 4); + if (ret < 0) + goto fail; + + if (show_bits_long(&gbc, 32) == 0xf8726fba) { + if ((ret = ff_mlp_read_major_sync(ctx, &s->hdr, &gbc)) != 0) + goto fail; + have_header = 1; + } + + start = get_bits_count(&gbc); + for (i = 0; i < s->hdr.num_substreams; i++) { + for (int j = 0; j < 4; j++) + s->units[i].bits[j] = get_bits1(&gbc); + + s->units[i].offset = get_bits(&gbc, 12) * 2; + if (i < FFMIN(s->hdr.num_substreams, 3)) { + last_offset = s->units[i].offset; + substream_bits += 16; + } + + if (s->units[i].bits[0]) { + s->units[i].optional = get_bits(&gbc, 16); + if (i < FFMIN(s->hdr.num_substreams, 3)) + substream_bits += 16; + } + } + end = get_bits_count(&gbc); + + ret = av_packet_copy_props(out, in); + if (ret < 0) + goto fail; + + size = ((end + 7) >> 3) + 4 + last_offset; + if (size >= 0 && size <= in->size) + out_size = size; + ret = av_grow_packet(out, out_size); + if (ret < 0) + goto fail; + + if (out_size < in_size) { + int bpos = 0, reduce = (end - start - substream_bits) >> 4; + uint16_t parity_nibble = 0; + uint16_t auheader; + + AV_WB16(out->data + 2, dts); + parity_nibble = dts; + out->size -= reduce * 2; + parity_nibble ^= out->size / 2; + + if (have_header) { + memcpy(out->data + 4, in->data + 4, 28); + out->data[16 + 4] = (out->data[16 + 4] & 0x0f) | (FFMIN(s->hdr.num_substreams, 3) << 4); + out->data[25 + 4] = out->data[25 + 4] & 0xfe; + out->data[26 + 4] = 0xff; + out->data[27 + 4] = 0xff; + AV_WL16(out->data + 4 + 26, ff_mlp_checksum16(out->data + 4, 26)); + } + + for (i = 0; i < FFMIN(s->hdr.num_substreams, 3); i++) { + uint16_t substr_hdr = 0; + + substr_hdr |= (s->units[i].bits[0] << 15); + substr_hdr |= (s->units[i].bits[1] << 14); + substr_hdr |= (s->units[i].bits[2] << 13); + substr_hdr |= (s->units[i].bits[3] << 12); + substr_hdr |= (s->units[i].offset / 2) & 0x0FFF; + + AV_WB16(out->data + have_header * 28 + 4 + bpos, substr_hdr); + + parity_nibble ^= out->data[have_header * 28 + 4 + bpos++]; + parity_nibble ^= out->data[have_header * 28 + 4 + bpos++]; + + if (s->units[i].bits[0]) { + AV_WB16(out->data + have_header * 28 + 4 + bpos, s->units[i].optional); + + parity_nibble ^= out->data[have_header * 28 + 4 + bpos++]; + parity_nibble ^= out->data[have_header * 28 + 4 + bpos++]; + } + } + + parity_nibble ^= parity_nibble >> 8; + parity_nibble ^= parity_nibble >> 4; + parity_nibble &= 0xF; + + memcpy(out->data + have_header * 28 + 4 + bpos, + in->data + 4 + (end >> 3), + out_size - (4 + (end >> 3))); + auheader = (parity_nibble ^ 0xF) << 12; + auheader |= (out->size / 2) & 0x0fff; + AV_WB16(out->data, auheader); + } else { + memcpy(out->data, in->data, out_size); + } + + av_packet_free(&in); + + return 0; +fail: + av_packet_unref(out); + av_packet_free(&in); + return ret; +} + +static const enum AVCodecID codec_ids[] = { + AV_CODEC_ID_TRUEHD, AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_truehd_core_bsf = { + .name = "truehd_core", + .priv_data_size = sizeof(TrueHDCoreContext), + .filter = truehd_core_filter, + .codec_ids = codec_ids, +};
Signed-off-by: Paul B Mahol <onemda@gmail.com> --- doc/bitstream_filters.texi | 4 + libavcodec/Makefile | 1 + libavcodec/bitstream_filters.c | 1 + libavcodec/truehd_core_bsf.c | 182 +++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 libavcodec/truehd_core_bsf.c