From patchwork Sun Mar 11 18:30:21 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Thompson X-Patchwork-Id: 7908 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.1.70 with SMTP id c67csp823098jad; Sun, 11 Mar 2018 11:30:54 -0700 (PDT) X-Google-Smtp-Source: AG47ELuhN+t7rLeTvLHBvwPNNlkWz9X/cRV7v6agXSpXCMb8iItzo516prFx1/wtrClgygqUhDDq X-Received: by 10.223.179.11 with SMTP id j11mr3454306wrd.223.1520793054433; Sun, 11 Mar 2018 11:30:54 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1520793054; cv=none; d=google.com; s=arc-20160816; b=rdYD29YcwLUmES9O0H+9w8uIvM5RGVg+6BhW16G4tLjyJX4MfdU9/heUlLwUquiGs8 9eQ6vCTgb/HrdeTjCY+pALBR73g4oEK8O3OxEr9OKivXcBpjaTdBtHMrSixjuuAJFnxN ASaOIVZFEsmYLt8sIuQv+f04xGa/kdFwlJk81v+R3MhnIkbtYOzaQnTdtl1fqC09xPi1 G9e8di3Q7rxLZmrC2DtVSlvYXYARGRCKQU1ETrCTX76/wJdFN1D42r/zyVUGHbUHWYcc DuINaMsvKbFRe5KpTOCkO1+3W+4LRsH2UCrBgtyC7H6JIBOyQr0fa9pY6P65Vp4lHhOe Sr6g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=STOnT9s1EmcCMBa2M+JAr7HqzZwPQ8Llaqs17PIeehk=; b=h8yWYvvYq+5VTFRwvJptSRlDGqeiOX6iuWunxQYA6RD7cE7X8jGYGwN8Qi7UYCdUYv KSEAbdfRMBgf/BVenFnmbueE4CXpg+yQ+9jlw+uuJzjjJy1rIPaH1XS/7nXw/UKBd4Cl QHHtac1IYULdT1TPA4dJ0Tiz+Q21N6GH+SUIxxm1BWvTZyIQVBRM24BXKBRBjttrXdz1 kwIVPimkBy87k3S2QGGQ4/Dtu0swfU9LueoU7khK4/7FbpZ4igmLFj6ruomgx4hu3Vsd J1qObzRXokl3o9kBykYrvQrjXppNWMSj+oEQ70apydcCI1IWC3vVq0R0EHnOv/76Hf/g tL4Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@jkqxz-net.20150623.gappssmtp.com header.s=20150623 header.b=dRIP1aHI; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id c12si4450907wrb.233.2018.03.11.11.30.54; Sun, 11 Mar 2018 11:30:54 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@jkqxz-net.20150623.gappssmtp.com header.s=20150623 header.b=dRIP1aHI; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 7EC5C68A211; Sun, 11 Mar 2018 20:30:28 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr0-f182.google.com (mail-wr0-f182.google.com [209.85.128.182]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 8585B689F24 for ; Sun, 11 Mar 2018 20:30:22 +0200 (EET) Received: by mail-wr0-f182.google.com with SMTP id n7so13504690wrn.5 for ; Sun, 11 Mar 2018 11:30:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jkqxz-net.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references; bh=zAd3p9RmTsJ0EAOExQ0m7mgztWDi8gFDSBp/ghAUjcE=; b=dRIP1aHIkXvH022QN33oHsvreHe4nOgBvTcfBsjo9aiMLyJlvBawDfWGsIh3W5fijH sAH8oFtOjBxFKJmBWeblpjM4nacq6rZP40RJp+7NCd/IOJK9vlYujT3U07UyzKxdJ1CC Zgtn+eiRUvKwo/d0KlmxMrEFxUKYJteV+tBWoYAbQKgBnHVExRQ+msFq8ZHBZDeljDz6 8RCMc6aLfueegqWVxEJkuDxBBfHtdWMATg8SkS9mZ6v6iVNXdqbZo1wNYDG3Zjl0+P5a Vx1h//N8OUvMsw9g6Wl8wdvWQ3aEzfp8lveKCYoTTJqpk2oNWB3up8mk7SfesL7SLei7 LWjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=zAd3p9RmTsJ0EAOExQ0m7mgztWDi8gFDSBp/ghAUjcE=; b=nRjbzh//pHxvbO3G2jZdSG8gmeiOpPlLu2ZL3ACA6rW1fKZ5mruUDirxJmhrFMk8N4 PPC6baEa0ZNPEtavUcCQUn8cY1jVTatQzaB+4YG4vVFNee5sbWgaOQkIFId3aXK5WLgu pWKHmVU1jIibqNFwls5FU37S16l5ql7iHysbvwsBYxOHGE28ao8GTvMuosASqPTnefAo hquKqflQ9QdUl88Y7YNdcveZXU3m8iNEs829qGtY01ZazEnpYcfNm4RJMojfjUBCB+Rz RPelJnJJn6/7siNtbZl0gyXJUdH9W1QdDtRVrLiRlObTr0QeqFS5nxRmFWKZi8UqI7Ry KGsQ== X-Gm-Message-State: AElRT7FLafima+s1dt2NIJX1W6SlquyFJK2NR7TGGYpo7e7fdk9TtUyz ENZF00MdQg65lqnKCZLCIoEv9z3W X-Received: by 10.223.187.19 with SMTP id r19mr4093770wrg.110.1520793031867; Sun, 11 Mar 2018 11:30:31 -0700 (PDT) Received: from rywe.jkqxz.net (cpc91242-cmbg18-2-0-cust650.5-4.cable.virginm.net. [82.8.130.139]) by smtp.gmail.com with ESMTPSA id 31sm5064514wrr.59.2018.03.11.11.30.30 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 11 Mar 2018 11:30:31 -0700 (PDT) From: Mark Thompson To: ffmpeg-devel@ffmpeg.org Date: Sun, 11 Mar 2018 18:30:21 +0000 Message-Id: <20180311183021.25556-8-sw@jkqxz.net> X-Mailer: git-send-email 2.16.1 In-Reply-To: <20180311183021.25556-1-sw@jkqxz.net> References: <20180311183021.25556-1-sw@jkqxz.net> Subject: [FFmpeg-devel] [PATCH 8/8] lavc: Add filter_units bitstream filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" This can remove units with types in or not in a given set from a stream. For example, it can be used to remove all non-VCL NAL units from an H.264 or H.265 stream. --- doc/bitstream_filters.texi | 29 +++++ libavcodec/Makefile | 1 + libavcodec/bitstream_filters.c | 1 + libavcodec/filter_units_bsf.c | 253 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 284 insertions(+) create mode 100644 libavcodec/filter_units_bsf.c diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index c196a30095..b6b4064c0c 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -93,6 +93,35 @@ When this option is enabled, the long-term headers are removed from the bitstream after extraction. @end table +@section filter_units + +Remove units with types in or not in a given set from the stream. + +@table @option +@item pass_types +List of unit types or ranges of unit types to pass through while removing +all others. This is specified as a '|'-separated list of unit type values +or ranges of values with '-'. + +@item remove_types +Identical to @option{pass_types}, except the units in the given set +removed and all others passed through. +@end table + +Extradata is unchanged by this transformation, but note that if the stream +contains inline parameter sets then the output may be unusable if they are +removed. + +For example, to remove all non-VCL NAL units from an H.264 stream: +@example +ffmpeg -i INPUT -c:v copy -bsf:v 'filter_units=pass_types=1-5' OUTPUT +@end example + +To remove all AUDs, SEI and filler from an H.265 stream: +@example +ffmpeg -i INPUT -c:v copy -bsf:v 'filter_units=remove_types=35|38-40' OUTPUT +@end example + @section h264_metadata Modify metadata embedded in an H.264 stream. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index ff6c9f8b2c..22095544a4 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1040,6 +1040,7 @@ OBJS-$(CONFIG_DUMP_EXTRADATA_BSF) += dump_extradata_bsf.o OBJS-$(CONFIG_DCA_CORE_BSF) += dca_core_bsf.o OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += extract_extradata_bsf.o \ h2645_parse.o +OBJS-$(CONFIG_FILTER_UNITS_BSF) += filter_units_bsf.o OBJS-$(CONFIG_H264_METADATA_BSF) += h264_metadata_bsf.o OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF) += h264_mp4toannexb_bsf.o OBJS-$(CONFIG_H264_REDUNDANT_PPS_BSF) += h264_redundant_pps_bsf.o diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index 338ef8251b..e1dc19871f 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -29,6 +29,7 @@ extern const AVBitStreamFilter ff_chomp_bsf; extern const AVBitStreamFilter ff_dump_extradata_bsf; extern const AVBitStreamFilter ff_dca_core_bsf; extern const AVBitStreamFilter ff_extract_extradata_bsf; +extern const AVBitStreamFilter ff_filter_units_bsf; extern const AVBitStreamFilter ff_h264_metadata_bsf; extern const AVBitStreamFilter ff_h264_mp4toannexb_bsf; extern const AVBitStreamFilter ff_h264_redundant_pps_bsf; diff --git a/libavcodec/filter_units_bsf.c b/libavcodec/filter_units_bsf.c new file mode 100644 index 0000000000..36b22ffc04 --- /dev/null +++ b/libavcodec/filter_units_bsf.c @@ -0,0 +1,253 @@ +/* + * 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 + +#include "libavutil/common.h" +#include "libavutil/opt.h" + +#include "bsf.h" +#include "cbs.h" + + +typedef struct FilterUnitsContext { + const AVClass *class; + + CodedBitstreamContext *cbc; + CodedBitstreamFragment fragment; + + const char *pass_types; + const char *remove_types; + + enum { + NOOP, + PASS, + REMOVE, + } mode; + CodedBitstreamUnitType *type_list; + int nb_types; +} FilterUnitsContext; + + +static int filter_units_make_type_list(const char *list_string, + CodedBitstreamUnitType **type_list, + int *nb_types) +{ + CodedBitstreamUnitType *list = NULL; + int pass, count; + + for (pass = 1; pass <= 2; pass++) { + long value, range_start, range_end; + const char *str; + char *value_end; + + count = 0; + for (str = list_string; *str;) { + value = strtol(str, &value_end, 0); + if (str == value_end) + goto invalid; + str = (const char *)value_end; + if (*str == '-') { + ++str; + range_start = value; + range_end = strtol(str, &value_end, 0); + if (str == value_end) + goto invalid; + + for (value = range_start; value < range_end; value++) { + if (pass == 2) + list[count] = value; + ++count; + } + } else { + if (pass == 2) + list[count] = value; + ++count; + } + if (*str == '|') + ++str; + } + if (pass == 1) { + list = av_malloc_array(count, sizeof(*list)); + if (!list) + return AVERROR(ENOMEM); + } + } + + *type_list = list; + *nb_types = count; + return 0; + +invalid: + av_freep(&list); + return AVERROR(EINVAL); +} + +static int filter_units_filter(AVBSFContext *bsf, AVPacket *out) +{ + FilterUnitsContext *ctx = bsf->priv_data; + CodedBitstreamFragment *frag = &ctx->fragment; + AVPacket *in = NULL; + int err, i, j; + + while (1) { + err = ff_bsf_get_packet(bsf, &in); + if (err < 0) + return err; + + if (ctx->mode == NOOP) { + av_packet_move_ref(out, in); + av_packet_free(&in); + return 0; + } + + err = ff_cbs_read_packet(ctx->cbc, frag, in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); + goto fail; + } + + for (i = 0; i < frag->nb_units; i++) { + for (j = 0; j < ctx->nb_types; j++) { + if (frag->units[i].type == ctx->type_list[j]) + break; + } + if (ctx->mode == REMOVE ? j < ctx->nb_types + : j >= ctx->nb_types) { + ff_cbs_delete_unit(ctx->cbc, frag, i); + --i; + } + } + + if (frag->nb_units > 0) + break; + + // Don't return packets with nothing in them. + av_packet_free(&in); + ff_cbs_fragment_uninit(ctx->cbc, frag); + } + + err = ff_cbs_write_packet(ctx->cbc, out, frag); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n"); + goto fail; + } + + err = av_packet_copy_props(out, in); + if (err < 0) + goto fail; + +fail: + ff_cbs_fragment_uninit(ctx->cbc, frag); + av_packet_free(&in); + + return err; +} + +static int filter_units_init(AVBSFContext *bsf) +{ + FilterUnitsContext *ctx = bsf->priv_data; + int err; + + if (ctx->pass_types && ctx->remove_types) { + av_log(bsf, AV_LOG_ERROR, "Exactly one of pass_types or " + "remove_types is required.\n"); + return AVERROR(EINVAL); + } + + if (ctx->pass_types) { + ctx->mode = PASS; + err = filter_units_make_type_list(ctx->pass_types, + &ctx->type_list, &ctx->nb_types); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to parse pass_types.\n"); + return err; + } + } else if (ctx->remove_types) { + ctx->mode = REMOVE; + err = filter_units_make_type_list(ctx->remove_types, + &ctx->type_list, &ctx->nb_types); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to parse remove_types.\n"); + return err; + } + } else { + return 0; + } + + err = ff_cbs_init(&ctx->cbc, bsf->par_in->codec_id, bsf); + if (err < 0) + return err; + + // Don't actually decompose anything, we only want the unit data. + ctx->cbc->decompose_unit_types = ctx->type_list; + ctx->cbc->nb_decompose_unit_types = 0; + + if (bsf->par_in->extradata) { + CodedBitstreamFragment ps; + + err = ff_cbs_read_extradata(ctx->cbc, &ps, bsf->par_in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n"); + } else { + err = ff_cbs_write_extradata(ctx->cbc, bsf->par_out, &ps); + if (err < 0) + av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n"); + } + + ff_cbs_fragment_uninit(ctx->cbc, &ps); + } + + return err; +} + +static void filter_units_close(AVBSFContext *bsf) +{ + FilterUnitsContext *ctx = bsf->priv_data; + + av_freep(&ctx->type_list); + + ff_cbs_close(&ctx->cbc); +} + +#define OFFSET(x) offsetof(FilterUnitsContext, x) +static const AVOption filter_units_options[] = { + { "pass_types", "List of unit types to pass through the filter.", + OFFSET(pass_types), AV_OPT_TYPE_STRING, { .str = NULL } }, + { "remove_types", "List of unit types to remove in the filter.", + OFFSET(remove_types), AV_OPT_TYPE_STRING, { .str = NULL } }, + + { NULL } +}; + +static const AVClass filter_units_class = { + .class_name = "filter_units", + .item_name = av_default_item_name, + .option = filter_units_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const AVBitStreamFilter ff_filter_units_bsf = { + .name = "filter_units", + .priv_data_size = sizeof(FilterUnitsContext), + .priv_class = &filter_units_class, + .init = &filter_units_init, + .close = &filter_units_close, + .filter = &filter_units_filter, + .codec_ids = ff_cbs_all_codec_ids, +};