lavc: Add filter_units bitstream filter

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.
This commit is contained in:
Mark Thompson 2018-03-06 18:49:27 +00:00
parent 389f4c3e0d
commit c99f837dde
5 changed files with 288 additions and 0 deletions

1
configure vendored
View File

@ -2919,6 +2919,7 @@ vc1_parser_select="vc1dsp"
# bitstream_filters
aac_adtstoasc_bsf_select="adts_header"
filter_units_bsf_select="cbs"
h264_metadata_bsf_select="cbs_h264"
h264_redundant_pps_bsf_select="cbs_h264"
hevc_metadata_bsf_select="cbs_h265"

View File

@ -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 hapqa_extract
Extract Rgb or Alpha part of an HAPQA file, without recompression, in order to create an HAPQ or an HAPAlphaOnly file.

View File

@ -1043,6 +1043,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

View File

@ -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;

View File

@ -0,0 +1,256 @@
/*
* 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 <stdlib.h>
#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)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
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 }, .flags = FLAGS },
{ "remove_types", "List of unit types to remove in the filter.",
OFFSET(remove_types), AV_OPT_TYPE_STRING,
{ .str = NULL }, .flags = FLAGS },
{ 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,
};