This commit is contained in:
2025-08-27 15:09:05 +09:00
parent 57961d84de
commit a13f66d917
9896 changed files with 2193048 additions and 730 deletions
+53
View File
@@ -0,0 +1,53 @@
clean::
$(RM) $(CLEANSUFFIXES:%=libavcodec/bsf/%)
OBJS-$(CONFIG_AAC_ADTSTOASC_BSF) += bsf/aac_adtstoasc.o
OBJS-$(CONFIG_APV_METADATA_BSF) += bsf/apv_metadata.o
OBJS-$(CONFIG_AV1_FRAME_MERGE_BSF) += bsf/av1_frame_merge.o
OBJS-$(CONFIG_AV1_FRAME_SPLIT_BSF) += bsf/av1_frame_split.o
OBJS-$(CONFIG_AV1_METADATA_BSF) += bsf/av1_metadata.o
OBJS-$(CONFIG_CHOMP_BSF) += bsf/chomp.o
OBJS-$(CONFIG_DCA_CORE_BSF) += bsf/dca_core.o
OBJS-$(CONFIG_DTS2PTS_BSF) += bsf/dts2pts.o
OBJS-$(CONFIG_DUMP_EXTRADATA_BSF) += bsf/dump_extradata.o
OBJS-$(CONFIG_DV_ERROR_MARKER_BSF) += bsf/dv_error_marker.o
OBJS-$(CONFIG_EAC3_CORE_BSF) += bsf/eac3_core.o
OBJS-$(CONFIG_EIA608_TO_SMPTE436M_BSF) += bsf/eia608_to_smpte436m.o
OBJS-$(CONFIG_EVC_FRAME_MERGE_BSF) += bsf/evc_frame_merge.o
OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += bsf/extract_extradata.o
OBJS-$(CONFIG_FILTER_UNITS_BSF) += bsf/filter_units.o
OBJS-$(CONFIG_H264_METADATA_BSF) += bsf/h264_metadata.o
OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF) += bsf/h264_mp4toannexb.o
OBJS-$(CONFIG_H264_REDUNDANT_PPS_BSF) += bsf/h264_redundant_pps.o
OBJS-$(CONFIG_HAPQA_EXTRACT_BSF) += bsf/hapqa_extract.o
OBJS-$(CONFIG_HEVC_METADATA_BSF) += bsf/h265_metadata.o
OBJS-$(CONFIG_DOVI_RPU_BSF) += bsf/dovi_rpu.o
OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += bsf/hevc_mp4toannexb.o
OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += bsf/imx_dump_header.o
OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF) += bsf/media100_to_mjpegb.o
OBJS-$(CONFIG_MJPEG2JPEG_BSF) += bsf/mjpeg2jpeg.o
OBJS-$(CONFIG_MJPEGA_DUMP_HEADER_BSF) += bsf/mjpega_dump_header.o
OBJS-$(CONFIG_MOV2TEXTSUB_BSF) += bsf/movsub.o
OBJS-$(CONFIG_MPEG2_METADATA_BSF) += bsf/mpeg2_metadata.o
OBJS-$(CONFIG_MPEG4_UNPACK_BFRAMES_BSF) += bsf/mpeg4_unpack_bframes.o
OBJS-$(CONFIG_NOISE_BSF) += bsf/noise.o
OBJS-$(CONFIG_NULL_BSF) += bsf/null.o
OBJS-$(CONFIG_OPUS_METADATA_BSF) += bsf/opus_metadata.o
OBJS-$(CONFIG_PCM_RECHUNK_BSF) += bsf/pcm_rechunk.o
OBJS-$(CONFIG_PGS_FRAME_MERGE_BSF) += bsf/pgs_frame_merge.o
OBJS-$(CONFIG_PRORES_METADATA_BSF) += bsf/prores_metadata.o
OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += bsf/remove_extradata.o
OBJS-$(CONFIG_SETTS_BSF) += bsf/setts.o
OBJS-$(CONFIG_SHOWINFO_BSF) += bsf/showinfo.o
OBJS-$(CONFIG_SMPTE436M_TO_EIA608_BSF) += bsf/smpte436m_to_eia608.o
OBJS-$(CONFIG_TEXT2MOVSUB_BSF) += bsf/movsub.o
OBJS-$(CONFIG_TRACE_HEADERS_BSF) += bsf/trace_headers.o
OBJS-$(CONFIG_TRUEHD_CORE_BSF) += bsf/truehd_core.o
OBJS-$(CONFIG_VP9_METADATA_BSF) += bsf/vp9_metadata.o
OBJS-$(CONFIG_VP9_RAW_REORDER_BSF) += bsf/vp9_raw_reorder.o
OBJS-$(CONFIG_VP9_SUPERFRAME_BSF) += bsf/vp9_superframe.o
OBJS-$(CONFIG_VP9_SUPERFRAME_SPLIT_BSF) += bsf/vp9_superframe_split.o
OBJS-$(CONFIG_VVC_METADATA_BSF) += bsf/h266_metadata.o
OBJS-$(CONFIG_VVC_MP4TOANNEXB_BSF) += bsf/vvc_mp4toannexb.o
libavcodec/bsf/%.o: CPPFLAGS += -I$(SRC_PATH)/libavcodec/
+156
View File
@@ -0,0 +1,156 @@
/*
* MPEG-2/4 AAC ADTS to MPEG-4 Audio Specific Configuration bitstream filter
* Copyright (c) 2009 Alex Converse <alex.converse@gmail.com>
*
* 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 "adts_header.h"
#include "adts_parser.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "put_bits.h"
#include "get_bits.h"
#include "mpeg4audio.h"
#include "mpeg4audio_copy_pce.h"
typedef struct AACBSFContext {
int first_frame_done;
} AACBSFContext;
/**
* This filter creates an MPEG-4 AudioSpecificConfig from an MPEG-2/4
* ADTS header and removes the ADTS header.
*/
static int aac_adtstoasc_filter(AVBSFContext *bsfc, AVPacket *pkt)
{
AACBSFContext *ctx = bsfc->priv_data;
PutBitContext pb;
AACADTSHeaderInfo hdr;
int ret;
ret = ff_bsf_get_packet_ref(bsfc, pkt);
if (ret < 0)
return ret;
if (bsfc->par_in->extradata && pkt->size >= 2 && (AV_RB16(pkt->data) >> 4) != 0xfff)
return 0;
if (pkt->size < AV_AAC_ADTS_HEADER_SIZE)
goto packet_too_small;
if (ff_adts_header_parse_buf(pkt->data, &hdr) < 0) {
av_log(bsfc, AV_LOG_ERROR, "Error parsing ADTS frame header!\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
if (!hdr.crc_absent && hdr.num_aac_frames > 1) {
avpriv_report_missing_feature(bsfc,
"Multiple RDBs per frame with CRC");
ret = AVERROR_PATCHWELCOME;
goto fail;
}
pkt->size -= AV_AAC_ADTS_HEADER_SIZE + 2 * !hdr.crc_absent;
if (pkt->size <= 0)
goto packet_too_small;
pkt->data += AV_AAC_ADTS_HEADER_SIZE + 2 * !hdr.crc_absent;
if (!ctx->first_frame_done) {
int pce_size = 0;
uint8_t pce_data[MAX_PCE_SIZE];
uint8_t *extradata;
if (!hdr.chan_config) {
GetBitContext gb;
init_get_bits(&gb, pkt->data, pkt->size * 8);
if (get_bits(&gb, 3) != 5) {
avpriv_report_missing_feature(bsfc,
"PCE-based channel configuration "
"without PCE as first syntax "
"element");
ret = AVERROR_PATCHWELCOME;
goto fail;
}
init_put_bits(&pb, pce_data, MAX_PCE_SIZE);
pce_size = ff_copy_pce_data(&pb, &gb) / 8;
flush_put_bits(&pb);
pkt->size -= get_bits_count(&gb)/8;
pkt->data += get_bits_count(&gb)/8;
}
extradata = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
2 + pce_size);
if (!extradata) {
ret = AVERROR(ENOMEM);
goto fail;
}
init_put_bits(&pb, extradata, 2 + pce_size);
put_bits(&pb, 5, hdr.object_type);
put_bits(&pb, 4, hdr.sampling_index);
put_bits(&pb, 4, hdr.chan_config);
put_bits(&pb, 1, 0); //frame length - 1024 samples
put_bits(&pb, 1, 0); //does not depend on core coder
put_bits(&pb, 1, 0); //is not extension
flush_put_bits(&pb);
if (pce_size) {
memcpy(extradata + 2, pce_data, pce_size);
}
ctx->first_frame_done = 1;
}
return 0;
packet_too_small:
av_log(bsfc, AV_LOG_ERROR, "Input packet too small\n");
ret = AVERROR_INVALIDDATA;
fail:
av_packet_unref(pkt);
return ret;
}
static int aac_adtstoasc_init(AVBSFContext *ctx)
{
/* Validate the extradata if the stream is already MPEG-4 AudioSpecificConfig */
if (ctx->par_in->extradata) {
MPEG4AudioConfig mp4ac;
int ret = avpriv_mpeg4audio_get_config2(&mp4ac, ctx->par_in->extradata,
ctx->par_in->extradata_size, 1, ctx);
if (ret < 0) {
av_log(ctx, AV_LOG_ERROR, "Error parsing AudioSpecificConfig extradata!\n");
return ret;
}
}
return 0;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_AAC, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_aac_adtstoasc_bsf = {
.p.name = "aac_adtstoasc",
.p.codec_ids = codec_ids,
.priv_data_size = sizeof(AACBSFContext),
.init = aac_adtstoasc_init,
.filter = aac_adtstoasc_filter,
};
+134
View File
@@ -0,0 +1,134 @@
/*
* 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 "libavutil/common.h"
#include "libavutil/opt.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_bsf.h"
#include "cbs_apv.h"
typedef struct APVMetadataContext {
CBSBSFContext common;
int color_primaries;
int transfer_characteristics;
int matrix_coefficients;
int full_range_flag;
} APVMetadataContext;
static int apv_metadata_update_frame_header(AVBSFContext *bsf,
APVRawFrameHeader *hdr)
{
APVMetadataContext *ctx = bsf->priv_data;
if (ctx->color_primaries >= 0 ||
ctx->transfer_characteristics >= 0 ||
ctx->matrix_coefficients >= 0 ||
ctx->full_range_flag >= 0) {
hdr->color_description_present_flag = 1;
if (ctx->color_primaries >= 0)
hdr->color_primaries = ctx->color_primaries;
if (ctx->transfer_characteristics >= 0)
hdr->transfer_characteristics = ctx->transfer_characteristics;
if (ctx->matrix_coefficients >= 0)
hdr->matrix_coefficients = ctx->matrix_coefficients;
if (ctx->full_range_flag >= 0)
hdr->full_range_flag = ctx->full_range_flag;
}
return 0;
}
static int apv_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt,
CodedBitstreamFragment *frag)
{
int err, i;
for (i = 0; i < frag->nb_units; i++) {
if (frag->units[i].type == APV_PBU_PRIMARY_FRAME) {
APVRawFrame *pbu = frag->units[i].content;
err = apv_metadata_update_frame_header(bsf, &pbu->frame_header);
if (err < 0)
return err;
}
}
return 0;
}
static const CBSBSFType apv_metadata_type = {
.codec_id = AV_CODEC_ID_APV,
.fragment_name = "access unit",
.unit_name = "PBU",
.update_fragment = &apv_metadata_update_fragment,
};
static int apv_metadata_init(AVBSFContext *bsf)
{
return ff_cbs_bsf_generic_init(bsf, &apv_metadata_type);
}
#define OFFSET(x) offsetof(APVMetadataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption apv_metadata_options[] = {
{ "color_primaries", "Set color primaries (section 5.3.5)",
OFFSET(color_primaries), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "transfer_characteristics", "Set transfer characteristics (section 5.3.5)",
OFFSET(transfer_characteristics), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "matrix_coefficients", "Set matrix coefficients (section 5.3.5)",
OFFSET(matrix_coefficients), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "full_range_flag", "Set full range flag flag (section 5.3.5)",
OFFSET(full_range_flag), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 1, FLAGS, .unit = "cr" },
{ "tv", "TV (limited) range", 0, AV_OPT_TYPE_CONST,
{ .i64 = 0 }, .flags = FLAGS, .unit = "cr" },
{ "pc", "PC (full) range", 0, AV_OPT_TYPE_CONST,
{ .i64 = 1 }, .flags = FLAGS, .unit = "cr" },
{ NULL }
};
static const AVClass apv_metadata_class = {
.class_name = "apv_metadata_bsf",
.item_name = av_default_item_name,
.option = apv_metadata_options,
.version = LIBAVUTIL_VERSION_INT,
};
static const enum AVCodecID apv_metadata_codec_ids[] = {
AV_CODEC_ID_APV, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_apv_metadata_bsf = {
.p.name = "apv_metadata",
.p.codec_ids = apv_metadata_codec_ids,
.p.priv_class = &apv_metadata_class,
.priv_data_size = sizeof(APVMetadataContext),
.init = &apv_metadata_init,
.close = &ff_cbs_bsf_generic_close,
.filter = &ff_cbs_bsf_generic_filter,
};
+167
View File
@@ -0,0 +1,167 @@
/*
* Copyright (c) 2019 James Almer <jamrial@gmail.com>
*
* 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 "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_av1.h"
typedef struct AV1FMergeContext {
CodedBitstreamContext *input;
CodedBitstreamContext *output;
CodedBitstreamFragment frag[2];
AVPacket *pkt, *in;
int idx;
} AV1FMergeContext;
static void av1_frame_merge_flush(AVBSFContext *bsf)
{
AV1FMergeContext *ctx = bsf->priv_data;
ff_cbs_fragment_reset(&ctx->frag[0]);
ff_cbs_fragment_reset(&ctx->frag[1]);
av_packet_unref(ctx->in);
av_packet_unref(ctx->pkt);
}
static int av1_frame_merge_filter(AVBSFContext *bsf, AVPacket *out)
{
AV1FMergeContext *ctx = bsf->priv_data;
CodedBitstreamFragment *frag = &ctx->frag[ctx->idx], *tu = &ctx->frag[!ctx->idx];
AVPacket *in = ctx->in, *buffer_pkt = ctx->pkt;
int err, i;
err = ff_bsf_get_packet_ref(bsf, in);
if (err < 0) {
if (err == AVERROR_EOF && tu->nb_units > 0)
goto eof;
return err;
}
err = ff_cbs_read_packet(ctx->input, frag, in);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n");
goto fail;
}
if (frag->nb_units == 0) {
av_log(bsf, AV_LOG_ERROR, "No OBU in packet.\n");
err = AVERROR_INVALIDDATA;
goto fail;
}
if (tu->nb_units == 0 && frag->units[0].type != AV1_OBU_TEMPORAL_DELIMITER) {
av_log(bsf, AV_LOG_ERROR, "Missing Temporal Delimiter.\n");
err = AVERROR_INVALIDDATA;
goto fail;
}
for (i = 1; i < frag->nb_units; i++) {
if (frag->units[i].type == AV1_OBU_TEMPORAL_DELIMITER) {
av_log(bsf, AV_LOG_ERROR, "Temporal Delimiter in the middle of a packet.\n");
err = AVERROR_INVALIDDATA;
goto fail;
}
}
if (tu->nb_units > 0 && frag->units[0].type == AV1_OBU_TEMPORAL_DELIMITER) {
eof:
err = ff_cbs_write_packet(ctx->output, buffer_pkt, tu);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n");
goto fail;
}
av_packet_move_ref(out, buffer_pkt);
// Swap fragment index, to avoid copying fragment references.
ctx->idx = !ctx->idx;
} else {
for (i = 0; i < frag->nb_units; i++) {
err = ff_cbs_insert_unit_content(tu, -1, frag->units[i].type,
frag->units[i].content, frag->units[i].content_ref);
if (err < 0)
goto fail;
}
err = AVERROR(EAGAIN);
}
/* Buffer packets with timestamps (there should be at most one per TU)
* or any packet if buffer_pkt is empty. The latter is needed to
* passthrough positions in case there are no timestamps like with
* the raw OBU demuxer. */
if (!buffer_pkt->data ||
in->pts != AV_NOPTS_VALUE && buffer_pkt->pts == AV_NOPTS_VALUE) {
av_packet_unref(buffer_pkt);
av_packet_move_ref(buffer_pkt, in);
} else
av_packet_unref(in);
ff_cbs_fragment_reset(&ctx->frag[ctx->idx]);
fail:
if (err < 0 && err != AVERROR(EAGAIN))
av1_frame_merge_flush(bsf);
return err;
}
static int av1_frame_merge_init(AVBSFContext *bsf)
{
AV1FMergeContext *ctx = bsf->priv_data;
int err;
ctx->in = av_packet_alloc();
ctx->pkt = av_packet_alloc();
if (!ctx->in || !ctx->pkt)
return AVERROR(ENOMEM);
err = ff_cbs_init(&ctx->input, AV_CODEC_ID_AV1, bsf);
if (err < 0)
return err;
return ff_cbs_init(&ctx->output, AV_CODEC_ID_AV1, bsf);
}
static void av1_frame_merge_close(AVBSFContext *bsf)
{
AV1FMergeContext *ctx = bsf->priv_data;
ff_cbs_fragment_free(&ctx->frag[0]);
ff_cbs_fragment_free(&ctx->frag[1]);
av_packet_free(&ctx->in);
av_packet_free(&ctx->pkt);
ff_cbs_close(&ctx->input);
ff_cbs_close(&ctx->output);
}
static const enum AVCodecID av1_frame_merge_codec_ids[] = {
AV_CODEC_ID_AV1, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_av1_frame_merge_bsf = {
.p.name = "av1_frame_merge",
.p.codec_ids = av1_frame_merge_codec_ids,
.priv_data_size = sizeof(AV1FMergeContext),
.init = av1_frame_merge_init,
.flush = av1_frame_merge_flush,
.close = av1_frame_merge_close,
.filter = av1_frame_merge_filter,
};
+261
View File
@@ -0,0 +1,261 @@
/*
* Copyright (c) 2019 James Almer <jamrial@gmail.com>
*
* 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
*/
/**
* @file
* This bitstream filter splits AV1 Temporal Units into packets containing
* just one frame, plus any leading and trailing OBUs that may be present at
* the beginning or end, respectively.
*
* Temporal Units already containing only one frame will be passed through
* unchanged. When splitting can't be performed, the Temporal Unit will be
* passed through containing only the remaining OBUs starting from the first
* one after the last successfully split frame.
*/
#include "libavutil/avassert.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_av1.h"
typedef struct AV1FSplitContext {
AVPacket *buffer_pkt;
CodedBitstreamContext *cbc;
CodedBitstreamFragment temporal_unit;
int nb_frames;
int cur_frame;
int cur_frame_idx;
int last_frame_idx;
} AV1FSplitContext;
static int av1_frame_split_filter(AVBSFContext *ctx, AVPacket *out)
{
AV1FSplitContext *s = ctx->priv_data;
CodedBitstreamFragment *td = &s->temporal_unit;
int i, ret;
int split = !!s->buffer_pkt->data;
if (!s->buffer_pkt->data) {
int nb_frames = 0;
ret = ff_bsf_get_packet_ref(ctx, s->buffer_pkt);
if (ret < 0)
return ret;
ret = ff_cbs_read_packet(s->cbc, td, s->buffer_pkt);
if (ret < 0) {
av_log(ctx, AV_LOG_WARNING, "Failed to parse temporal unit.\n");
goto passthrough;
}
for (i = 0; i < td->nb_units; i++) {
CodedBitstreamUnit *unit = &td->units[i];
if (unit->type == AV1_OBU_FRAME ||
unit->type == AV1_OBU_FRAME_HEADER)
nb_frames++;
else if (unit->type == AV1_OBU_TILE_LIST) {
av_log(ctx, AV_LOG_VERBOSE, "Large scale tiles are unsupported.\n");
goto passthrough;
}
}
if (nb_frames > 1) {
s->cur_frame = 0;
s->cur_frame_idx = s->last_frame_idx = 0;
s->nb_frames = nb_frames;
split = 1;
}
}
if (split) {
AV1RawFrameHeader *frame = NULL;
int cur_frame_type = -1, size = 0;
for (i = s->cur_frame_idx; i < td->nb_units; i++) {
CodedBitstreamUnit *unit = &td->units[i];
size += unit->data_size;
if (unit->type == AV1_OBU_FRAME) {
AV1RawOBU *obu = unit->content;
if (frame) {
av_log(ctx, AV_LOG_WARNING, "Frame OBU found when Tile data for a "
"previous frame was expected.\n");
goto passthrough;
}
frame = &obu->obu.frame.header;
cur_frame_type = obu->header.obu_type;
s->last_frame_idx = s->cur_frame_idx;
s->cur_frame_idx = i + 1;
s->cur_frame++;
// split here unless it's the last frame, in which case
// include every trailing OBU
if (s->cur_frame < s->nb_frames)
break;
} else if (unit->type == AV1_OBU_FRAME_HEADER) {
AV1RawOBU *obu = unit->content;
if (frame) {
av_log(ctx, AV_LOG_WARNING, "Frame Header OBU found when Tile data for a "
"previous frame was expected.\n");
goto passthrough;
}
frame = &obu->obu.frame_header;
cur_frame_type = obu->header.obu_type;
s->last_frame_idx = s->cur_frame_idx;
s->cur_frame++;
// split here if show_existing_frame unless it's the last
// frame, in which case include every trailing OBU
if (frame->show_existing_frame &&
s->cur_frame < s->nb_frames) {
s->cur_frame_idx = i + 1;
break;
}
} else if (unit->type == AV1_OBU_TILE_GROUP) {
AV1RawOBU *obu = unit->content;
AV1RawTileGroup *group = &obu->obu.tile_group;
if (!frame || cur_frame_type != AV1_OBU_FRAME_HEADER) {
av_log(ctx, AV_LOG_WARNING, "Unexpected Tile Group OBU found before a "
"Frame Header.\n");
goto passthrough;
}
if ((group->tg_end == (frame->tile_cols * frame->tile_rows) - 1) &&
// include every trailing OBU with the last frame
s->cur_frame < s->nb_frames) {
s->cur_frame_idx = i + 1;
break;
}
}
}
av_assert0(frame && s->cur_frame <= s->nb_frames);
ret = av_packet_ref(out, s->buffer_pkt);
if (ret < 0)
goto fail;
out->data = (uint8_t *)td->units[s->last_frame_idx].data;
out->size = size;
// skip the frame in the buffer packet if it's split successfully, so it's not present
// if the packet is passed through in case of failure when splitting another frame.
s->buffer_pkt->data += size;
s->buffer_pkt->size -= size;
if (!frame->show_existing_frame && !frame->show_frame)
out->pts = AV_NOPTS_VALUE;
if (s->cur_frame == s->nb_frames) {
av_packet_unref(s->buffer_pkt);
ff_cbs_fragment_reset(td);
}
return 0;
}
passthrough:
av_packet_move_ref(out, s->buffer_pkt);
ret = 0;
fail:
if (ret < 0) {
av_packet_unref(out);
av_packet_unref(s->buffer_pkt);
}
ff_cbs_fragment_reset(td);
return ret;
}
static const CodedBitstreamUnitType decompose_unit_types[] = {
AV1_OBU_TEMPORAL_DELIMITER,
AV1_OBU_SEQUENCE_HEADER,
AV1_OBU_FRAME_HEADER,
AV1_OBU_TILE_GROUP,
AV1_OBU_FRAME,
};
static int av1_frame_split_init(AVBSFContext *ctx)
{
AV1FSplitContext *s = ctx->priv_data;
CodedBitstreamFragment *td = &s->temporal_unit;
int ret;
s->buffer_pkt = av_packet_alloc();
if (!s->buffer_pkt)
return AVERROR(ENOMEM);
ret = ff_cbs_init(&s->cbc, AV_CODEC_ID_AV1, ctx);
if (ret < 0)
return ret;
s->cbc->decompose_unit_types = decompose_unit_types;
s->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types);
if (!ctx->par_in->extradata_size)
return 0;
ret = ff_cbs_read_extradata(s->cbc, td, ctx->par_in);
if (ret < 0)
av_log(ctx, AV_LOG_WARNING, "Failed to parse extradata.\n");
ff_cbs_fragment_reset(td);
return 0;
}
static void av1_frame_split_flush(AVBSFContext *ctx)
{
AV1FSplitContext *s = ctx->priv_data;
av_packet_unref(s->buffer_pkt);
ff_cbs_fragment_reset(&s->temporal_unit);
}
static void av1_frame_split_close(AVBSFContext *ctx)
{
AV1FSplitContext *s = ctx->priv_data;
av_packet_free(&s->buffer_pkt);
ff_cbs_fragment_free(&s->temporal_unit);
ff_cbs_close(&s->cbc);
}
static const enum AVCodecID av1_frame_split_codec_ids[] = {
AV_CODEC_ID_AV1, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_av1_frame_split_bsf = {
.p.name = "av1_frame_split",
.p.codec_ids = av1_frame_split_codec_ids,
.priv_data_size = sizeof(AV1FSplitContext),
.init = av1_frame_split_init,
.flush = av1_frame_split_flush,
.close = av1_frame_split_close,
.filter = av1_frame_split_filter,
};
+231
View File
@@ -0,0 +1,231 @@
/*
* 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 "libavutil/common.h"
#include "libavutil/opt.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_bsf.h"
#include "cbs_av1.h"
typedef struct AV1MetadataContext {
CBSBSFContext common;
int td;
AV1RawOBU td_obu;
int color_primaries;
int transfer_characteristics;
int matrix_coefficients;
int color_range;
int chroma_sample_position;
AVRational tick_rate;
int num_ticks_per_picture;
int delete_padding;
} AV1MetadataContext;
static int av1_metadata_update_sequence_header(AVBSFContext *bsf,
AV1RawSequenceHeader *seq)
{
AV1MetadataContext *ctx = bsf->priv_data;
AV1RawColorConfig *clc = &seq->color_config;
AV1RawTimingInfo *tim = &seq->timing_info;
if (ctx->color_primaries >= 0 ||
ctx->transfer_characteristics >= 0 ||
ctx->matrix_coefficients >= 0) {
clc->color_description_present_flag = 1;
if (ctx->color_primaries >= 0)
clc->color_primaries = ctx->color_primaries;
if (ctx->transfer_characteristics >= 0)
clc->transfer_characteristics = ctx->transfer_characteristics;
if (ctx->matrix_coefficients >= 0)
clc->matrix_coefficients = ctx->matrix_coefficients;
}
if (ctx->color_range >= 0) {
if (clc->color_primaries == AVCOL_PRI_BT709 &&
clc->transfer_characteristics == AVCOL_TRC_IEC61966_2_1 &&
clc->matrix_coefficients == AVCOL_SPC_RGB) {
av_log(bsf, AV_LOG_WARNING, "Warning: color_range cannot be set "
"on RGB streams encoded in BT.709 sRGB.\n");
} else {
clc->color_range = ctx->color_range;
}
}
if (ctx->chroma_sample_position >= 0) {
if (clc->mono_chrome || !clc->subsampling_x || !clc->subsampling_y) {
av_log(bsf, AV_LOG_WARNING, "Warning: chroma_sample_position "
"can only be set for 4:2:0 streams.\n");
} else {
clc->chroma_sample_position = ctx->chroma_sample_position;
}
}
if (ctx->tick_rate.num && ctx->tick_rate.den) {
int num, den;
av_reduce(&num, &den, ctx->tick_rate.num, ctx->tick_rate.den,
UINT32_MAX > INT_MAX ? UINT32_MAX : INT_MAX);
tim->time_scale = num;
tim->num_units_in_display_tick = den;
seq->timing_info_present_flag = 1;
if (ctx->num_ticks_per_picture > 0) {
tim->equal_picture_interval = 1;
tim->num_ticks_per_picture_minus_1 =
ctx->num_ticks_per_picture - 1;
}
}
return 0;
}
static int av1_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt,
CodedBitstreamFragment *frag)
{
AV1MetadataContext *ctx = bsf->priv_data;
int err, i;
for (i = 0; i < frag->nb_units; i++) {
if (frag->units[i].type == AV1_OBU_SEQUENCE_HEADER) {
AV1RawOBU *obu = frag->units[i].content;
err = av1_metadata_update_sequence_header(bsf, &obu->obu.sequence_header);
if (err < 0)
return err;
}
}
// If a Temporal Delimiter is present, it must be the first OBU.
if (frag->nb_units && frag->units[0].type == AV1_OBU_TEMPORAL_DELIMITER) {
if (ctx->td == BSF_ELEMENT_REMOVE)
ff_cbs_delete_unit(frag, 0);
} else if (pkt && ctx->td == BSF_ELEMENT_INSERT) {
err = ff_cbs_insert_unit_content(frag, 0, AV1_OBU_TEMPORAL_DELIMITER,
&ctx->td_obu, NULL);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to insert Temporal Delimiter.\n");
return err;
}
}
if (ctx->delete_padding) {
for (i = frag->nb_units - 1; i >= 0; i--) {
if (frag->units[i].type == AV1_OBU_PADDING)
ff_cbs_delete_unit(frag, i);
}
}
return 0;
}
static const CBSBSFType av1_metadata_type = {
.codec_id = AV_CODEC_ID_AV1,
.fragment_name = "temporal unit",
.unit_name = "OBU",
.update_fragment = &av1_metadata_update_fragment,
};
static int av1_metadata_init(AVBSFContext *bsf)
{
AV1MetadataContext *ctx = bsf->priv_data;
ctx->td_obu = (AV1RawOBU) {
.header.obu_type = AV1_OBU_TEMPORAL_DELIMITER,
};
return ff_cbs_bsf_generic_init(bsf, &av1_metadata_type);
}
#define OFFSET(x) offsetof(AV1MetadataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption av1_metadata_options[] = {
BSF_ELEMENT_OPTIONS_PIR("td", "Temporal Delimiter OBU",
td, FLAGS),
{ "color_primaries", "Set color primaries (section 6.4.2)",
OFFSET(color_primaries), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "transfer_characteristics", "Set transfer characteristics (section 6.4.2)",
OFFSET(transfer_characteristics), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "matrix_coefficients", "Set matrix coefficients (section 6.4.2)",
OFFSET(matrix_coefficients), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "color_range", "Set color range flag (section 6.4.2)",
OFFSET(color_range), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 1, FLAGS, .unit = "cr" },
{ "tv", "TV (limited) range", 0, AV_OPT_TYPE_CONST,
{ .i64 = 0 }, .flags = FLAGS, .unit = "cr" },
{ "pc", "PC (full) range", 0, AV_OPT_TYPE_CONST,
{ .i64 = 1 }, .flags = FLAGS, .unit = "cr" },
{ "chroma_sample_position", "Set chroma sample position (section 6.4.2)",
OFFSET(chroma_sample_position), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 3, FLAGS, .unit = "csp" },
{ "unknown", "Unknown chroma sample position", 0, AV_OPT_TYPE_CONST,
{ .i64 = AV1_CSP_UNKNOWN }, .flags = FLAGS, .unit = "csp" },
{ "vertical", "Left chroma sample position", 0, AV_OPT_TYPE_CONST,
{ .i64 = AV1_CSP_VERTICAL }, .flags = FLAGS, .unit = "csp" },
{ "colocated", "Top-left chroma sample position", 0, AV_OPT_TYPE_CONST,
{ .i64 = AV1_CSP_COLOCATED }, .flags = FLAGS, .unit = "csp" },
{ "tick_rate", "Set display tick rate (time_scale / num_units_in_display_tick)",
OFFSET(tick_rate), AV_OPT_TYPE_RATIONAL,
{ .dbl = 0.0 }, 0, UINT_MAX, FLAGS },
{ "num_ticks_per_picture", "Set display ticks per picture for CFR streams",
OFFSET(num_ticks_per_picture), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, INT_MAX, FLAGS },
{ "delete_padding", "Delete all Padding OBUs",
OFFSET(delete_padding), AV_OPT_TYPE_BOOL,
{ .i64 = 0 }, 0, 1, FLAGS},
{ NULL }
};
static const AVClass av1_metadata_class = {
.class_name = "av1_metadata_bsf",
.item_name = av_default_item_name,
.option = av1_metadata_options,
.version = LIBAVUTIL_VERSION_INT,
};
static const enum AVCodecID av1_metadata_codec_ids[] = {
AV_CODEC_ID_AV1, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_av1_metadata_bsf = {
.p.name = "av1_metadata",
.p.codec_ids = av1_metadata_codec_ids,
.p.priv_class = &av1_metadata_class,
.priv_data_size = sizeof(AV1MetadataContext),
.init = &av1_metadata_init,
.close = &ff_cbs_bsf_generic_close,
.filter = &ff_cbs_bsf_generic_filter,
};
+45
View File
@@ -0,0 +1,45 @@
/*
* Chomp bitstream filter
* Copyright (c) 2010 Alex Converse <alex.converse@gmail.com>
*
* 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 "bsf.h"
#include "bsf_internal.h"
static int chomp_filter(AVBSFContext *ctx, AVPacket *pkt)
{
int ret;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0)
return ret;
while (pkt->size > 0 && !pkt->data[pkt->size - 1])
pkt->size--;
return 0;
}
/**
* This filter removes a string of NULL bytes from the end of a packet.
*/
const FFBitStreamFilter ff_chomp_bsf = {
.p.name = "chomp",
.filter = chomp_filter,
};
+61
View File
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2016 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 "bsf.h"
#include "bsf_internal.h"
#include "bytestream.h"
#include "dca_syncwords.h"
static int dca_core_filter(AVBSFContext *ctx, AVPacket *pkt)
{
GetByteContext gb;
uint32_t syncword;
int core_size = 0, ret;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0)
return ret;
bytestream2_init(&gb, pkt->data, pkt->size);
syncword = bytestream2_get_be32(&gb);
bytestream2_skip(&gb, 1);
switch (syncword) {
case DCA_SYNCWORD_CORE_BE:
core_size = ((bytestream2_get_be24(&gb) >> 4) & 0x3fff) + 1;
break;
}
if (core_size > 0 && core_size <= pkt->size) {
pkt->size = core_size;
}
return 0;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_DTS, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_dca_core_bsf = {
.p.name = "dca_core",
.p.codec_ids = codec_ids,
.filter = dca_core_filter,
};
+289
View File
@@ -0,0 +1,289 @@
/*
* 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 "libavutil/common.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_bsf.h"
#include "cbs_av1.h"
#include "cbs_h265.h"
#include "dovi_rpu.h"
#include "h2645data.h"
#include "h265_profile_level.h"
#include "itut35.h"
#include "hevc/hevc.h"
typedef struct DoviRpuContext {
CBSBSFContext common;
DOVIContext dec;
DOVIContext enc;
int strip;
int compression;
} DoviRpuContext;
static int update_rpu(AVBSFContext *bsf, const AVPacket *pkt, int flags,
const uint8_t *rpu, size_t rpu_size,
uint8_t **out_rpu, int *out_size)
{
DoviRpuContext *s = bsf->priv_data;
AVDOVIMetadata *metadata = NULL;
int ret;
ret = ff_dovi_rpu_parse(&s->dec, rpu, rpu_size, 0);
if (ret < 0) {
ff_dovi_ctx_flush(&s->dec);
return ret;
}
ret = ff_dovi_get_metadata(&s->dec, &metadata);
if (ret == 0 /* no metadata */) {
*out_rpu = NULL;
*out_size = 0;
return 0;
} else if (ret < 0) {
ff_dovi_ctx_flush(&s->dec);
return ret;
}
if (pkt && !(pkt->flags & AV_PKT_FLAG_KEY))
flags |= FF_DOVI_COMPRESS_RPU;
ret = ff_dovi_rpu_generate(&s->enc, metadata, flags, out_rpu, out_size);
av_free(metadata);
if (ret < 0)
ff_dovi_ctx_flush(&s->enc);
return ret;
}
static int dovi_rpu_update_fragment_hevc(AVBSFContext *bsf, AVPacket *pkt,
CodedBitstreamFragment *au)
{
DoviRpuContext *s = bsf->priv_data;
CodedBitstreamUnit *nal = au->nb_units ? &au->units[au->nb_units - 1] : NULL;
uint8_t *rpu = NULL;
int rpu_size, ret;
// HEVC_NAL_UNSPEC62 is Dolby Vision PRU and HEVC_NAL_UNSPEC63 is Dolby Vision EL
if (!nal || (nal->type != HEVC_NAL_UNSPEC62 && nal->type != HEVC_NAL_UNSPEC63))
return 0;
if (s->strip) {
ff_cbs_delete_unit(au, au->nb_units - 1);
return 0;
}
if (nal->type == HEVC_NAL_UNSPEC63)
return 0;
ret = update_rpu(bsf, pkt, 0, nal->data + 2, nal->data_size - 2, &rpu, &rpu_size);
if (ret < 0)
return ret;
/* NAL unit header + NAL prefix */
if (rpu_size + 3 <= nal->data_size && av_buffer_is_writable(nal->data_ref)) {
memcpy(nal->data + 3, rpu, rpu_size);
av_free(rpu);
nal->data_size = rpu_size + 3;
} else {
AVBufferRef *ref = av_buffer_alloc(rpu_size + 3);
if (!ref) {
av_free(rpu);
return AVERROR(ENOMEM);
}
memcpy(ref->data, nal->data, 3);
memcpy(ref->data + 3, rpu, rpu_size);
av_buffer_unref(&nal->data_ref);
av_free(rpu);
nal->data = ref->data;
nal->data_size = rpu_size + 3;
nal->data_ref = ref;
nal->data_bit_padding = 0;
}
return 0;
}
static int dovi_rpu_update_fragment_av1(AVBSFContext *bsf, AVPacket *pkt,
CodedBitstreamFragment *frag)
{
DoviRpuContext *s = bsf->priv_data;
int provider_code, provider_oriented_code, rpu_size, ret;
AVBufferRef *ref;
uint8_t *rpu;
for (int i = 0; i < frag->nb_units; i++) {
AV1RawOBU *obu = frag->units[i].content;
AV1RawMetadataITUTT35 *t35 = &obu->obu.metadata.metadata.itut_t35;
if (frag->units[i].type != AV1_OBU_METADATA ||
obu->obu.metadata.metadata_type != AV1_METADATA_TYPE_ITUT_T35 ||
t35->itu_t_t35_country_code != ITU_T_T35_COUNTRY_CODE_US ||
t35->payload_size < 6)
continue;
provider_code = AV_RB16(t35->payload);
provider_oriented_code = AV_RB32(t35->payload + 2);
if (provider_code != ITU_T_T35_PROVIDER_CODE_DOLBY ||
provider_oriented_code != 0x800)
continue;
if (s->strip) {
ff_cbs_delete_unit(frag, i);
return 0;
}
ret = update_rpu(bsf, pkt, FF_DOVI_WRAP_T35,
t35->payload + 6, t35->payload_size - 6,
&rpu, &rpu_size);
if (ret < 0)
return ret;
ref = av_buffer_create(rpu, rpu_size, av_buffer_default_free, NULL, 0);
if (!ref) {
av_free(rpu);
return AVERROR(ENOMEM);
}
av_buffer_unref(&t35->payload_ref);
t35->payload_ref = ref;
t35->payload = rpu + 1; /* skip country code */
t35->payload_size = rpu_size - 1;
break; /* should be only one RPU per packet */
}
return 0;
}
static const CBSBSFType dovi_rpu_hevc_type = {
.codec_id = AV_CODEC_ID_HEVC,
.fragment_name = "access unit",
.unit_name = "NAL unit",
.update_fragment = &dovi_rpu_update_fragment_hevc,
};
static const CBSBSFType dovi_rpu_av1_type = {
.codec_id = AV_CODEC_ID_AV1,
.fragment_name = "temporal unit",
.unit_name = "OBU",
.update_fragment = &dovi_rpu_update_fragment_av1,
};
static int dovi_rpu_init(AVBSFContext *bsf)
{
int ret;
DoviRpuContext *s = bsf->priv_data;
s->dec.logctx = s->enc.logctx = bsf;
s->enc.enable = 1;
if (s->compression == AV_DOVI_COMPRESSION_RESERVED) {
av_log(bsf, AV_LOG_ERROR, "Invalid compression level: %d\n", s->compression);
return AVERROR(EINVAL);
}
if (s->strip) {
av_packet_side_data_remove(bsf->par_out->coded_side_data,
&bsf->par_out->nb_coded_side_data,
AV_PKT_DATA_DOVI_CONF);
} else {
const AVPacketSideData *sd;
sd = av_packet_side_data_get(bsf->par_out->coded_side_data,
bsf->par_out->nb_coded_side_data,
AV_PKT_DATA_DOVI_CONF);
if (sd) {
AVDOVIDecoderConfigurationRecord *cfg;
cfg = (AVDOVIDecoderConfigurationRecord *) sd->data;
s->dec.cfg = *cfg;
/* Update configuration record before setting to enc ctx */
cfg->dv_md_compression = s->compression;
if (s->compression && s->dec.cfg.dv_profile < 8) {
av_log(bsf, AV_LOG_ERROR, "Invalid compression level %d for "
"Dolby Vision profile %d.\n", s->compression, s->dec.cfg.dv_profile);
return AVERROR(EINVAL);
}
s->enc.cfg = *cfg;
} else {
av_log(bsf, AV_LOG_WARNING, "No Dolby Vision configuration record "
"found? Generating one, but results may be invalid.\n");
ret = ff_dovi_configure_from_codedpar(&s->enc, bsf->par_out, NULL, s->compression,
FF_COMPLIANCE_NORMAL);
if (ret < 0)
return ret;
/* Be conservative in accepting all compressed RPUs */
s->dec.cfg = s->enc.cfg;
s->dec.cfg.dv_md_compression = AV_DOVI_COMPRESSION_EXTENDED;
}
}
switch (bsf->par_in->codec_id) {
case AV_CODEC_ID_HEVC:
return ff_cbs_bsf_generic_init(bsf, &dovi_rpu_hevc_type);
case AV_CODEC_ID_AV1:
return ff_cbs_bsf_generic_init(bsf, &dovi_rpu_av1_type);
default:
return AVERROR_BUG;
}
}
static void dovi_rpu_close(AVBSFContext *bsf)
{
DoviRpuContext *s = bsf->priv_data;
ff_dovi_ctx_unref(&s->dec);
ff_dovi_ctx_unref(&s->enc);
ff_cbs_bsf_generic_close(bsf);
}
#define OFFSET(x) offsetof(DoviRpuContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption dovi_rpu_options[] = {
{ "strip", "Strip Dolby Vision metadata", OFFSET(strip), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
{ "compression", "DV metadata compression mode", OFFSET(compression), AV_OPT_TYPE_INT, { .i64 = AV_DOVI_COMPRESSION_LIMITED }, 0, AV_DOVI_COMPRESSION_EXTENDED, FLAGS, .unit = "compression" },
{ "none", "Don't compress metadata", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, .flags = FLAGS, .unit = "compression" },
{ "limited", "Limited metadata compression", 0, AV_OPT_TYPE_CONST, {.i64 = AV_DOVI_COMPRESSION_LIMITED}, .flags = FLAGS, .unit = "compression" },
{ "extended", "Extended metadata compression",0, AV_OPT_TYPE_CONST, {.i64 = AV_DOVI_COMPRESSION_EXTENDED}, .flags = FLAGS, .unit = "compression" },
{ NULL }
};
static const AVClass dovi_rpu_class = {
.class_name = "dovi_rpu_bsf",
.item_name = av_default_item_name,
.option = dovi_rpu_options,
.version = LIBAVUTIL_VERSION_INT,
};
static const enum AVCodecID dovi_rpu_codec_ids[] = {
AV_CODEC_ID_HEVC, AV_CODEC_ID_AV1, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_dovi_rpu_bsf = {
.p.name = "dovi_rpu",
.p.codec_ids = dovi_rpu_codec_ids,
.p.priv_class = &dovi_rpu_class,
.priv_data_size = sizeof(DoviRpuContext),
.init = &dovi_rpu_init,
.close = &dovi_rpu_close,
.filter = &ff_cbs_bsf_generic_filter,
};
+550
View File
@@ -0,0 +1,550 @@
/*
* Copyright (c) 2022 James Almer
*
* 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
*/
/**
* @file
* Derive PTS by reordering DTS from supported streams
*/
#include "libavutil/avassert.h"
#include "libavutil/fifo.h"
#include "libavutil/mem.h"
#include "libavutil/tree.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_h264.h"
#include "h264_parse.h"
#include "h264_ps.h"
#include "libavutil/refstruct.h"
typedef struct DTS2PTSNode {
int64_t dts;
int64_t duration;
int poc;
int gop;
} DTS2PTSNode;
typedef struct DTS2PTSFrame {
AVPacket *pkt;
int poc;
int poc_diff;
int gop;
} DTS2PTSFrame;
typedef struct DTS2PTSH264Context {
H264POCContext poc;
SPS sps;
int poc_diff;
int last_poc;
int highest_poc;
int picture_structure;
} DTS2PTSH264Context;
typedef struct DTS2PTSContext {
struct AVTreeNode *root;
AVFifo *fifo;
AVRefStructPool *node_pool;
// Codec specific function pointers and constants
int (*init)(AVBSFContext *ctx);
int (*filter)(AVBSFContext *ctx);
void (*flush)(AVBSFContext *ctx);
size_t fifo_size;
CodedBitstreamContext *cbc;
CodedBitstreamFragment au;
union {
DTS2PTSH264Context h264;
} u;
int nb_frame;
int gop;
int eof;
} DTS2PTSContext;
// AVTreeNode callbacks
static int cmp_insert(const void *key, const void *node)
{
int ret = ((const DTS2PTSNode *)key)->poc - ((const DTS2PTSNode *)node)->poc;
if (!ret)
ret = ((const DTS2PTSNode *)key)->gop - ((const DTS2PTSNode *)node)->gop;
return ret;
}
static int cmp_find(const void *key, const void *node)
{
const DTS2PTSFrame * key1 = key;
const DTS2PTSNode *node1 = node;
int ret = FFDIFFSIGN(key1->poc, node1->poc);
if (!ret)
ret = key1->gop - node1->gop;
return ret;
}
static int dec_poc(void *opaque, void *elem)
{
DTS2PTSNode *node = elem;
int dec = *(int *)opaque;
node->poc -= dec;
return 0;
}
static int free_node(void *opaque, void *elem)
{
DTS2PTSNode *node = elem;
av_refstruct_unref(&node);
return 0;
}
// Shared functions
static int alloc_and_insert_node(AVBSFContext *ctx, int64_t ts, int64_t duration,
int poc, int poc_diff, int gop)
{
DTS2PTSContext *s = ctx->priv_data;
for (int i = 0; i < poc_diff; i++) {
struct AVTreeNode *node = av_tree_node_alloc();
DTS2PTSNode *poc_node, *ret;
if (!node)
return AVERROR(ENOMEM);
poc_node = av_refstruct_pool_get(s->node_pool);
if (!poc_node) {
av_free(node);
return AVERROR(ENOMEM);
}
if (i && ts != AV_NOPTS_VALUE)
ts += duration / poc_diff;
*poc_node = (DTS2PTSNode) { ts, duration, poc++, gop };
ret = av_tree_insert(&s->root, poc_node, cmp_insert, &node);
if (ret && ret != poc_node) {
*ret = *poc_node;
av_refstruct_unref(&poc_node);
av_free(node);
}
}
return 0;
}
// H.264
static const CodedBitstreamUnitType h264_decompose_unit_types[] = {
H264_NAL_SPS,
H264_NAL_PPS,
H264_NAL_IDR_SLICE,
H264_NAL_SLICE,
};
static int h264_init(AVBSFContext *ctx)
{
DTS2PTSContext *s = ctx->priv_data;
DTS2PTSH264Context *h264 = &s->u.h264;
s->cbc->decompose_unit_types = h264_decompose_unit_types;
s->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(h264_decompose_unit_types);
s->nb_frame = -(ctx->par_in->video_delay << 1);
h264->last_poc = h264->highest_poc = INT_MIN;
return 0;
}
static int get_mmco_reset(const H264RawSliceHeader *header)
{
if (header->nal_unit_header.nal_ref_idc == 0 ||
!header->adaptive_ref_pic_marking_mode_flag)
return 0;
for (int i = 0; i < H264_MAX_MMCO_COUNT; i++) {
if (header->mmco[i].memory_management_control_operation == 0)
return 0;
else if (header->mmco[i].memory_management_control_operation == 5)
return 1;
}
return 0;
}
static int h264_queue_frame(AVBSFContext *ctx, AVPacket *pkt, int poc, int *queued)
{
DTS2PTSContext *s = ctx->priv_data;
DTS2PTSH264Context *h264 = &s->u.h264;
DTS2PTSFrame frame;
int poc_diff, ret;
poc_diff = (h264->picture_structure == 3) + 1;
if (h264->sps.frame_mbs_only_flag && h264->poc_diff)
poc_diff = FFMIN(poc_diff, h264->poc_diff);
if (poc < 0) {
av_tree_enumerate(s->root, &poc_diff, NULL, dec_poc);
s->nb_frame -= poc_diff;
}
// Check if there was a POC reset (Like an IDR slice)
if (s->nb_frame > h264->highest_poc) {
s->nb_frame = 0;
s->gop = (s->gop + 1) % s->fifo_size;
h264->highest_poc = h264->last_poc;
}
ret = alloc_and_insert_node(ctx, pkt->dts, pkt->duration, s->nb_frame, poc_diff, s->gop);
if (ret < 0)
return ret;
av_log(ctx, AV_LOG_DEBUG, "Queueing frame with POC %d, GOP %d, dts %"PRId64"\n",
poc, s->gop, pkt->dts);
s->nb_frame += poc_diff;
// Add frame to output FIFO only once
if (*queued)
return 0;
frame = (DTS2PTSFrame) { pkt, poc, poc_diff, s->gop };
ret = av_fifo_write(s->fifo, &frame, 1);
av_assert2(ret >= 0);
*queued = 1;
return 0;
}
static int h264_filter(AVBSFContext *ctx)
{
DTS2PTSContext *s = ctx->priv_data;
DTS2PTSH264Context *h264 = &s->u.h264;
CodedBitstreamFragment *au = &s->au;
AVPacket *in;
int output_picture_number = INT_MIN;
int field_poc[2];
int queued = 0, ret;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
ret = ff_cbs_read_packet(s->cbc, au, in);
if (ret < 0) {
av_log(ctx, AV_LOG_WARNING, "Failed to parse access unit.\n");
goto fail;
}
for (int i = 0; i < au->nb_units; i++) {
CodedBitstreamUnit *unit = &au->units[i];
switch (unit->type) {
case H264_NAL_IDR_SLICE:
h264->poc.prev_frame_num = 0;
h264->poc.prev_frame_num_offset = 0;
h264->poc.prev_poc_msb =
h264->poc.prev_poc_lsb = 0;
// fall-through
case H264_NAL_SLICE: {
const H264RawSlice *slice = unit->content;
const H264RawSliceHeader *header = &slice->header;
const CodedBitstreamH264Context *cbs_h264 = s->cbc->priv_data;
const H264RawSPS *sps = cbs_h264->active_sps;
int got_reset;
if (!sps) {
av_log(ctx, AV_LOG_ERROR, "No active SPS for a slice\n");
goto fail;
}
// Initialize the SPS struct with the fields ff_h264_init_poc() cares about
h264->sps.frame_mbs_only_flag = sps->frame_mbs_only_flag;
h264->sps.log2_max_frame_num = sps->log2_max_frame_num_minus4 + 4;
h264->sps.poc_type = sps->pic_order_cnt_type;
h264->sps.log2_max_poc_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
h264->sps.offset_for_non_ref_pic = sps->offset_for_non_ref_pic;
h264->sps.offset_for_top_to_bottom_field = sps->offset_for_top_to_bottom_field;
h264->sps.poc_cycle_length = sps->num_ref_frames_in_pic_order_cnt_cycle;
for (int j = 0; j < h264->sps.poc_cycle_length; j++)
h264->sps.offset_for_ref_frame[j] = sps->offset_for_ref_frame[j];
h264->picture_structure = sps->frame_mbs_only_flag ? 3 :
(header->field_pic_flag ?
header->field_pic_flag + header->bottom_field_flag : 3);
h264->poc.frame_num = header->frame_num;
h264->poc.poc_lsb = header->pic_order_cnt_lsb;
h264->poc.delta_poc_bottom = header->delta_pic_order_cnt_bottom;
h264->poc.delta_poc[0] = header->delta_pic_order_cnt[0];
h264->poc.delta_poc[1] = header->delta_pic_order_cnt[1];
field_poc[0] = field_poc[1] = INT_MAX;
ret = ff_h264_init_poc(field_poc, &output_picture_number, &h264->sps,
&h264->poc, h264->picture_structure,
header->nal_unit_header.nal_ref_idc);
if (ret < 0) {
av_log(ctx, AV_LOG_ERROR, "ff_h264_init_poc() failure\n");
goto fail;
}
got_reset = get_mmco_reset(header);
h264->poc.prev_frame_num = got_reset ? 0 : h264->poc.frame_num;
h264->poc.prev_frame_num_offset = got_reset ? 0 : h264->poc.frame_num_offset;
if (header->nal_unit_header.nal_ref_idc != 0) {
h264->poc.prev_poc_msb = got_reset ? 0 : h264->poc.poc_msb;
if (got_reset)
h264->poc.prev_poc_lsb = h264->picture_structure == 2 ? 0 : field_poc[0];
else
h264->poc.prev_poc_lsb = h264->poc.poc_lsb;
}
if (output_picture_number != h264->last_poc) {
if (h264->last_poc != INT_MIN) {
int64_t diff = FFABS(h264->last_poc - (int64_t)output_picture_number);
if ((output_picture_number < 0) && !h264->last_poc)
h264->poc_diff = 0;
else if (FFABS((int64_t)output_picture_number) < h264->poc_diff) {
diff = FFABS(output_picture_number);
h264->poc_diff = 0;
}
if ((!h264->poc_diff || (h264->poc_diff > diff)) && diff <= INT_MAX) {
h264->poc_diff = diff;
if (h264->poc_diff == 1 && h264->sps.frame_mbs_only_flag) {
av_tree_enumerate(s->root, &h264->poc_diff, NULL, dec_poc);
s->nb_frame -= 2;
}
}
}
h264->last_poc = output_picture_number;
h264->highest_poc = FFMAX(h264->highest_poc, output_picture_number);
ret = h264_queue_frame(ctx, in, output_picture_number, &queued);
if (ret < 0)
goto fail;
}
break;
}
default:
break;
}
}
if (output_picture_number == INT_MIN) {
av_log(ctx, AV_LOG_ERROR, "No slices in access unit\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
ret = 0;
fail:
ff_cbs_fragment_reset(au);
if (!queued)
av_packet_free(&in);
return ret;
}
static void h264_flush(AVBSFContext *ctx)
{
DTS2PTSContext *s = ctx->priv_data;
DTS2PTSH264Context *h264 = &s->u.h264;
memset(&h264->sps, 0, sizeof(h264->sps));
memset(&h264->poc, 0, sizeof(h264->poc));
s->nb_frame = -(ctx->par_in->video_delay << 1);
h264->last_poc = h264->highest_poc = INT_MIN;
}
// Core functions
static const struct {
enum AVCodecID id;
int (*init)(AVBSFContext *ctx);
int (*filter)(AVBSFContext *ctx);
void (*flush)(AVBSFContext *ctx);
size_t fifo_size;
} func_tab[] = {
{ AV_CODEC_ID_H264, h264_init, h264_filter, h264_flush, H264_MAX_DPB_FRAMES * 2 * 2 },
};
static int dts2pts_init(AVBSFContext *ctx)
{
DTS2PTSContext *s = ctx->priv_data;
CodedBitstreamFragment *au = &s->au;
int i, ret;
for (i = 0; i < FF_ARRAY_ELEMS(func_tab); i++) {
if (func_tab[i].id == ctx->par_in->codec_id) {
s->init = func_tab[i].init;
s->filter = func_tab[i].filter;
s->flush = func_tab[i].flush;
s->fifo_size = func_tab[i].fifo_size;
break;
}
}
if (i == FF_ARRAY_ELEMS(func_tab))
return AVERROR_BUG;
av_assert0(s->filter && s->fifo_size);
s->fifo = av_fifo_alloc2(s->fifo_size, sizeof(DTS2PTSFrame), 0);
if (!s->fifo)
return AVERROR(ENOMEM);
s->node_pool = av_refstruct_pool_alloc(sizeof(DTS2PTSNode),
AV_REFSTRUCT_POOL_FLAG_NO_ZEROING);
if (!s->node_pool)
return AVERROR(ENOMEM);
ret = ff_cbs_init(&s->cbc, ctx->par_in->codec_id, ctx);
if (ret < 0)
return ret;
if (s->init) {
ret = s->init(ctx);
if (ret < 0)
return ret;
}
if (!ctx->par_in->extradata_size)
return 0;
ret = ff_cbs_read_extradata(s->cbc, au, ctx->par_in);
if (ret < 0)
av_log(ctx, AV_LOG_WARNING, "Failed to parse extradata.\n");
ff_cbs_fragment_reset(au);
return 0;
}
static int dts2pts_filter(AVBSFContext *ctx, AVPacket *out)
{
DTS2PTSContext *s = ctx->priv_data;
DTS2PTSNode *poc_node = NULL, *next[2] = { NULL, NULL };
DTS2PTSFrame frame;
int ret;
// Fill up the FIFO and POC tree
while (!s->eof && av_fifo_can_write(s->fifo)) {
ret = s->filter(ctx);
if (ret < 0) {
if (ret != AVERROR_EOF)
return ret;
s->eof = 1;
}
}
if (!av_fifo_can_read(s->fifo))
return AVERROR_EOF;
// Fetch a packet from the FIFO
ret = av_fifo_read(s->fifo, &frame, 1);
av_assert2(ret >= 0);
av_packet_move_ref(out, frame.pkt);
av_packet_free(&frame.pkt);
// Search the timestamp for the requested POC and set PTS
poc_node = av_tree_find(s->root, &frame, cmp_find, (void **)next);
if (!poc_node) {
poc_node = next[1];
if (!poc_node || poc_node->poc != frame.poc)
poc_node = next[0];
}
if (poc_node && poc_node->poc == frame.poc) {
out->pts = poc_node->dts;
if (!s->eof) {
// Remove the found entry from the tree
DTS2PTSFrame dup = (DTS2PTSFrame) { NULL, frame.poc + 1, frame.poc_diff, frame.gop };
for (; dup.poc_diff > 0; dup.poc++, dup.poc_diff--) {
struct AVTreeNode *node = NULL;
if (!poc_node || poc_node->dts != out->pts)
continue;
av_tree_insert(&s->root, poc_node, cmp_insert, &node);
av_refstruct_unref(&poc_node);
av_free(node);
poc_node = av_tree_find(s->root, &dup, cmp_find, NULL);
}
}
} else if (s->eof && frame.poc > INT_MIN) {
DTS2PTSFrame dup = (DTS2PTSFrame) { NULL, frame.poc - 1, frame.poc_diff, frame.gop };
poc_node = av_tree_find(s->root, &dup, cmp_find, NULL);
if (poc_node && poc_node->poc == dup.poc) {
out->pts = poc_node->dts;
if (out->pts != AV_NOPTS_VALUE)
out->pts += poc_node->duration;
ret = alloc_and_insert_node(ctx, out->pts, out->duration,
frame.poc, frame.poc_diff, frame.gop);
if (ret < 0) {
av_packet_unref(out);
return ret;
}
if (!ret)
av_log(ctx, AV_LOG_DEBUG, "Queueing frame for POC %d, GOP %d, dts %"PRId64", "
"generated from POC %d, GOP %d, dts %"PRId64", duration %"PRId64"\n",
frame.poc, frame.gop, out->pts,
poc_node->poc, poc_node->gop, poc_node->dts, poc_node->duration);
} else
av_log(ctx, AV_LOG_WARNING, "No timestamp for POC %d in tree\n", frame.poc);
} else
av_log(ctx, AV_LOG_WARNING, "No timestamp for POC %d in tree\n", frame.poc);
av_log(ctx, AV_LOG_DEBUG, "Returning frame for POC %d, GOP %d, dts %"PRId64", pts %"PRId64"\n",
frame.poc, frame.gop, out->dts, out->pts);
return 0;
}
static void dts2pts_flush(AVBSFContext *ctx)
{
DTS2PTSContext *s = ctx->priv_data;
DTS2PTSFrame frame;
if (s->flush)
s->flush(ctx);
s->eof = 0;
s->gop = 0;
while (s->fifo && av_fifo_read(s->fifo, &frame, 1) >= 0)
av_packet_free(&frame.pkt);
av_tree_enumerate(s->root, NULL, NULL, free_node);
av_tree_destroy(s->root);
s->root = NULL;
ff_cbs_fragment_reset(&s->au);
if (s->cbc)
ff_cbs_flush(s->cbc);
}
static void dts2pts_close(AVBSFContext *ctx)
{
DTS2PTSContext *s = ctx->priv_data;
dts2pts_flush(ctx);
av_fifo_freep2(&s->fifo);
av_refstruct_pool_uninit(&s->node_pool);
ff_cbs_fragment_free(&s->au);
ff_cbs_close(&s->cbc);
}
static const enum AVCodecID dts2pts_codec_ids[] = {
AV_CODEC_ID_H264,
AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_dts2pts_bsf = {
.p.name = "dts2pts",
.p.codec_ids = dts2pts_codec_ids,
.priv_data_size = sizeof(DTS2PTSContext),
.init = dts2pts_init,
.flush = dts2pts_flush,
.close = dts2pts_close,
.filter = dts2pts_filter,
};
+106
View File
@@ -0,0 +1,106 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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 <string.h>
#include "bsf.h"
#include "bsf_internal.h"
#include "libavutil/log.h"
#include "libavutil/opt.h"
enum DumpFreq {
DUMP_FREQ_KEYFRAME,
DUMP_FREQ_ALL,
};
typedef struct DumpExtradataContext {
const AVClass *class;
AVPacket pkt;
int freq;
} DumpExtradataContext;
static int dump_extradata(AVBSFContext *ctx, AVPacket *out)
{
DumpExtradataContext *s = ctx->priv_data;
AVPacket *in = &s->pkt;
int ret = 0;
ret = ff_bsf_get_packet_ref(ctx, in);
if (ret < 0)
return ret;
if (ctx->par_in->extradata &&
(s->freq == DUMP_FREQ_ALL ||
(s->freq == DUMP_FREQ_KEYFRAME && in->flags & AV_PKT_FLAG_KEY)) &&
(in->size < ctx->par_in->extradata_size ||
memcmp(in->data, ctx->par_in->extradata, ctx->par_in->extradata_size))) {
if (in->size >= INT_MAX - ctx->par_in->extradata_size) {
ret = AVERROR(ERANGE);
goto fail;
}
ret = av_new_packet(out, in->size + ctx->par_in->extradata_size);
if (ret < 0)
goto fail;
ret = av_packet_copy_props(out, in);
if (ret < 0) {
av_packet_unref(out);
goto fail;
}
memcpy(out->data, ctx->par_in->extradata, ctx->par_in->extradata_size);
memcpy(out->data + ctx->par_in->extradata_size, in->data, in->size);
} else {
av_packet_move_ref(out, in);
}
fail:
av_packet_unref(in);
return ret;
}
#define OFFSET(x) offsetof(DumpExtradataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption options[] = {
{ "freq", "When to dump extradata", OFFSET(freq), AV_OPT_TYPE_INT,
{ .i64 = DUMP_FREQ_KEYFRAME }, DUMP_FREQ_KEYFRAME, DUMP_FREQ_ALL, FLAGS, .unit = "freq" },
{ "k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = DUMP_FREQ_KEYFRAME }, .flags = FLAGS, .unit = "freq" },
{ "keyframe", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = DUMP_FREQ_KEYFRAME }, .flags = FLAGS, .unit = "freq" },
{ "e", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = DUMP_FREQ_ALL }, .flags = FLAGS, .unit = "freq" },
{ "all", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = DUMP_FREQ_ALL }, .flags = FLAGS, .unit = "freq" },
{ NULL },
};
static const AVClass dump_extradata_class = {
.class_name = "dump_extradata bsf",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFBitStreamFilter ff_dump_extradata_bsf = {
.p.name = "dump_extra",
.p.priv_class = &dump_extradata_class,
.priv_data_size = sizeof(DumpExtradataContext),
.filter = dump_extradata,
};
+138
View File
@@ -0,0 +1,138 @@
/*
* Copyright (c) 2022 Michael Niedermayer
*
* 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 "bsf.h"
#include "bsf_internal.h"
#include "libavutil/colorspace.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/opt.h"
typedef struct DVErrorMarkerContext {
const AVClass *class;
uint8_t color_rgba[4];
int sta;
uint8_t marked_block[76];
} DVErrorMarkerContext;
static void setdc(uint8_t *b, const uint8_t color_rgba[4], int cblocks, int y_step, int v_step, int u_step) {
for (int i=0; i<4; i++) {
b[0] = RGB_TO_Y_JPEG(color_rgba[0], color_rgba[1],color_rgba[2]) + 128;
b[1] = 0x06;
b += y_step;
}
for (int i=0; i<cblocks; i++) {
b[0] = RGB_TO_V_JPEG(color_rgba[0], color_rgba[1],color_rgba[2]) - 128;
b[1] = 0x16;
b += v_step;
}
for (int i=0; i<cblocks; i++) {
b[0] = RGB_TO_U_JPEG(color_rgba[0], color_rgba[1],color_rgba[2]) - 128;
b[1] = 0x16;
b += u_step;
}
}
static int dv_error_marker_init(AVBSFContext *ctx)
{
DVErrorMarkerContext *s = ctx->priv_data;
memset(s->marked_block, -1, 76);
setdc(s->marked_block, s->color_rgba, 1, 14, 10, 10);
setdc(s->marked_block, s->color_rgba, 2, 10, 10, 8);
return 0;
}
static int dv_error_marker_filter(AVBSFContext *ctx, AVPacket *pkt)
{
DVErrorMarkerContext *s = ctx->priv_data;
int ret = ff_bsf_get_packet_ref(ctx, pkt);
uint8_t *p;
int writable = 0;
int stamask = s->sta;
int match_count = 0;
if (ret < 0)
return ret;
p = pkt->data;
for(int i = 0; i < pkt->size - 79; i+=80) {
// see page 44-46 or section 5.5 of http://web.archive.org/web/20060927044735/http://www.smpte.org/smpte_store/standards/pdf/s314m.pdf.
if ((p[i] >> 4) == 9 && ((stamask >> (p[i+3] >> 4))&1)) {
if (!writable) {
ret = av_packet_make_writable(pkt);
if (ret < 0) {
av_packet_unref(pkt);
return ret;
}
writable = 1;
p = pkt->data;
}
memcpy(p+i+4, s->marked_block, 76);
match_count ++;
}
}
av_log(ctx, AV_LOG_DEBUG, "%8"PRId64": Replaced %5d blocks by color %X\n", pkt->pts, match_count, AV_RB32(s->color_rgba));
return 0;
}
#define OFFSET(x) offsetof(DVErrorMarkerContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption options[] = {
{ "color" , "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "yellow"}, 0, 0, FLAGS },
{ "sta" , "specify which error status value to match"
, OFFSET(sta ), AV_OPT_TYPE_FLAGS, {.i64 = 0xFFFE}, 0, 0xFFFF, FLAGS, .unit = "sta" },
{ "ok" , "No error, no concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0001}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "Aa" , "No error, concealment from previous frame type a",0, AV_OPT_TYPE_CONST, {.i64 = 0x0004}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "Ba" , "No error, concealment from next frame type a", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0010}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "Ca" , "No error, unspecified concealment type a", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0040}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "erri" , "Error with inserted code, No concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0080}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "erru" , "Error with unidentified pos, No concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x8000}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "err" , "Error, No concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x8080}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "Ab" , "No error, concealment from previous frame type b",0, AV_OPT_TYPE_CONST, {.i64 = 0x0400}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "Bb" , "No error, concealment from next frame type b", 0, AV_OPT_TYPE_CONST, {.i64 = 0x1000}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "Cb" , "No error, unspecified concealment type b", 0, AV_OPT_TYPE_CONST, {.i64 = 0x4000}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "A" , "No error, concealment from previous frame", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0404}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "B" , "No error, concealment from next frame", 0, AV_OPT_TYPE_CONST, {.i64 = 0x1010}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "C" , "No error, unspecified concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x4040}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "a" , "No error, concealment type a", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0054}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "b" , "No error, concealment type b", 0, AV_OPT_TYPE_CONST, {.i64 = 0x5400}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "res" , "Reserved", 0, AV_OPT_TYPE_CONST, {.i64 = 0x2B2A}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "notok" , "Error or concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0xD4D4}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ "notres" , "Not reserved", 0, AV_OPT_TYPE_CONST, {.i64 = 0xD4D5}, 0, 0xFFFF, FLAGS, .unit = "sta"},
{ NULL },
};
static const AVClass dv_error_marker_class = {
.class_name = "dv_error_marker",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFBitStreamFilter ff_dv_error_marker_bsf = {
.p.name = "dv_error_marker",
.p.codec_ids = (const enum AVCodecID []){ AV_CODEC_ID_DVVIDEO, AV_CODEC_ID_NONE },
.p.priv_class = &dv_error_marker_class,
.priv_data_size = sizeof(DVErrorMarkerContext),
.init = dv_error_marker_init,
.filter = dv_error_marker_filter,
};
+86
View File
@@ -0,0 +1,86 @@
/*
* 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 "bsf.h"
#include "bsf_internal.h"
#include "get_bits.h"
#include "ac3_parser_internal.h"
static int eac3_core_filter(AVBSFContext *ctx, AVPacket *pkt)
{
AC3HeaderInfo hdr;
GetBitContext gbc;
int ret;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0)
return ret;
ret = init_get_bits8(&gbc, pkt->data, pkt->size);
if (ret < 0)
goto fail;
ret = ff_ac3_parse_header(&gbc, &hdr);
if (ret < 0) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
if (hdr.frame_type == EAC3_FRAME_TYPE_INDEPENDENT ||
hdr.frame_type == EAC3_FRAME_TYPE_AC3_CONVERT) {
pkt->size = FFMIN(hdr.frame_size, pkt->size);
} else if (hdr.frame_type == EAC3_FRAME_TYPE_DEPENDENT && pkt->size > hdr.frame_size) {
AC3HeaderInfo hdr2;
ret = init_get_bits8(&gbc, pkt->data + hdr.frame_size, pkt->size - hdr.frame_size);
if (ret < 0)
goto fail;
ret = ff_ac3_parse_header(&gbc, &hdr2);
if (ret < 0) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
if (hdr2.frame_type == EAC3_FRAME_TYPE_INDEPENDENT ||
hdr2.frame_type == EAC3_FRAME_TYPE_AC3_CONVERT) {
pkt->size -= hdr.frame_size;
pkt->data += hdr.frame_size;
} else {
pkt->size = 0;
}
} else {
pkt->size = 0;
}
return 0;
fail:
av_packet_unref(pkt);
return ret;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_EAC3, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_eac3_core_bsf = {
.p.name = "eac3_core",
.p.codec_ids = codec_ids,
.filter = eac3_core_filter,
};
+277
View File
@@ -0,0 +1,277 @@
/*
* EIA-608 to MXF SMPTE-436M ANC bitstream filter
* Copyright (c) 2025 Jacob Lifshay
*
* 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 "bsf.h"
#include "bsf_internal.h"
#include "codec_id.h"
#include "libavcodec/smpte_436m.h"
#include "libavcodec/smpte_436m_internal.h"
#include "libavutil/avassert.h"
#include "libavutil/avutil.h"
#include "libavutil/error.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/macros.h"
#include "libavutil/opt.h"
#include "libavutil/rational.h"
typedef struct EIA608ToSMPTE436MContext {
const AVClass *class;
unsigned line_number;
unsigned cdp_sequence_cntr;
unsigned wrapping_type_opt;
unsigned sample_coding_opt;
AVSmpte436mWrappingType wrapping_type;
AVSmpte436mPayloadSampleCoding sample_coding;
AVRational cdp_frame_rate;
uint8_t cdp_frame_rate_byte;
} EIA608ToSMPTE436MContext;
// clang-format off
static const AVSmpte291mAnc8bit test_anc = {
.did = 0x61,
.sdid_or_dbn = 0x01,
.data_count = 0x49,
.payload = {
// header
0x96, 0x69, 0x49, 0x7F, 0x43, 0xFA, 0x8D, 0x72, 0xF4,
// 608 triples
0xFC, 0x80, 0x80, 0xFD, 0x80, 0x80,
// 708 padding
0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
// footer
0x74, 0xFA, 0x8D, 0x81,
},
.checksum = 0xAB,
};
// clang-format on
static av_cold int ff_eia608_to_smpte436m_init(AVBSFContext *ctx)
{
EIA608ToSMPTE436MContext *priv = ctx->priv_data;
priv->wrapping_type = priv->wrapping_type_opt;
priv->sample_coding = priv->sample_coding_opt;
// validate we can handle the selected wrapping type and sample coding
AVSmpte436mCodedAnc coded_anc;
int ret = av_smpte_291m_anc_8bit_encode(
&coded_anc, priv->line_number, priv->wrapping_type, priv->sample_coding, &test_anc, ctx);
if (ret < 0)
return ret;
ctx->par_out->codec_type = AVMEDIA_TYPE_DATA;
ctx->par_out->codec_id = AV_CODEC_ID_SMPTE_436M_ANC;
static const struct {
AVRational frame_rate;
uint8_t cdp_frame_rate;
} known_frame_rates[] = {
{ .frame_rate = { .num = 24000, .den = 1001 }, .cdp_frame_rate = 0x1F },
{ .frame_rate = { .num = 24, .den = 1 }, .cdp_frame_rate = 0x2F },
{ .frame_rate = { .num = 25, .den = 1 }, .cdp_frame_rate = 0x3F },
{ .frame_rate = { .num = 30000, .den = 1001 }, .cdp_frame_rate = 0x4F },
{ .frame_rate = { .num = 30, .den = 1 }, .cdp_frame_rate = 0x5F },
{ .frame_rate = { .num = 50, .den = 1 }, .cdp_frame_rate = 0x6F },
{ .frame_rate = { .num = 60000, .den = 1001 }, .cdp_frame_rate = 0x7F },
{ .frame_rate = { .num = 60, .den = 1 }, .cdp_frame_rate = 0x8F },
};
priv->cdp_frame_rate_byte = 0;
for (int i = 0; i < FF_ARRAY_ELEMS(known_frame_rates); i++) {
if (known_frame_rates[i].frame_rate.num == priv->cdp_frame_rate.num && known_frame_rates[i].frame_rate.den == priv->cdp_frame_rate.den) {
priv->cdp_frame_rate_byte = known_frame_rates[i].cdp_frame_rate;
break;
}
}
if (priv->cdp_frame_rate_byte == 0) {
av_log(ctx,
AV_LOG_FATAL,
"cdp_frame_rate not supported: %d/%d\n",
priv->cdp_frame_rate.num,
priv->cdp_frame_rate.den);
return AVERROR(EINVAL);
}
return 0;
}
static int ff_eia608_to_smpte436m_filter(AVBSFContext *ctx, AVPacket *out)
{
EIA608ToSMPTE436MContext *priv = ctx->priv_data;
AVPacket *in;
int ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
AVSmpte291mAnc8bit anc;
anc.did = 0x61;
anc.sdid_or_dbn = 0x1;
uint8_t *p = anc.payload;
*p++ = 0x96; // cdp_identifier -- always 0x9669
*p++ = 0x69;
uint8_t *cdp_length_p = p++;
*p++ = priv->cdp_frame_rate_byte;
const uint8_t FLAG_CC_DATA_PRESENT = 0x40;
const uint8_t FLAG_CAPTION_SERVICE_ACTIVE = 0x2;
const uint8_t FLAG_RESERVED = 0x1; // must always be set
*p++ = FLAG_CC_DATA_PRESENT | FLAG_CAPTION_SERVICE_ACTIVE | FLAG_RESERVED;
AV_WB16(p, priv->cdp_sequence_cntr);
p += 2;
const uint8_t CC_DATA_SECTION_ID = 0x72;
*p++ = CC_DATA_SECTION_ID;
uint8_t *cc_count_p = p++;
const uint8_t CC_COUNT_MASK = 0x1F;
const int CDP_FOOTER_SIZE = 4;
int cc_count = in->size / 3;
int space_left = AV_SMPTE_291M_ANC_PAYLOAD_CAPACITY - (p - anc.payload);
int cc_data_space_left = space_left - CDP_FOOTER_SIZE;
int max_cc_count = FFMAX(cc_data_space_left / 3, CC_COUNT_MASK);
if (cc_count > max_cc_count) {
av_log(ctx,
AV_LOG_ERROR,
"cc_count (%d) is bigger than the maximum supported (%d), truncating captions packet\n",
cc_count,
max_cc_count);
cc_count = max_cc_count;
}
*cc_count_p = cc_count | ~CC_COUNT_MASK; // other bits are reserved and set to ones
for (size_t i = 0; i < cc_count; i++) {
size_t start = i * 3;
*p++ = in->data[start] | 0xF8; // fill reserved bits with ones
*p++ = in->data[start + 1];
*p++ = in->data[start + 2];
}
const uint8_t CDP_FOOTER_ID = 0x74;
*p++ = CDP_FOOTER_ID;
AV_WB16(p, priv->cdp_sequence_cntr);
p += 2;
uint8_t *packet_checksum_p = p;
*p++ = 0;
anc.data_count = p - anc.payload;
*cdp_length_p = anc.data_count;
int sum = 0;
for (int i = 0; i < anc.data_count; i++) {
sum += anc.payload[i];
}
// set to an 8-bit value such that the sum of the bytes of the whole CDP mod 2^8 is 0
*packet_checksum_p = -sum;
priv->cdp_sequence_cntr++;
// cdp_sequence_cntr wraps around at 16-bits
priv->cdp_sequence_cntr &= 0xFFFFU;
av_smpte_291m_anc_8bit_fill_checksum(&anc);
AVSmpte436mCodedAnc coded_anc;
ret = av_smpte_291m_anc_8bit_encode(
&coded_anc, priv->line_number, (AVSmpte436mWrappingType)priv->wrapping_type, priv->sample_coding, &anc, ctx);
if (ret < 0)
goto fail;
ret = av_smpte_436m_anc_encode(NULL, 0, 1, &coded_anc);
if (ret < 0)
goto fail;
ret = av_new_packet(out, ret);
if (ret < 0)
goto fail;
ret = av_packet_copy_props(out, in);
if (ret < 0)
goto fail;
ret = av_smpte_436m_anc_encode(out->data, out->size, 1, &coded_anc);
if (ret < 0)
goto fail;
ret = 0;
fail:
if (ret < 0)
av_packet_unref(out);
av_packet_free(&in);
return ret;
}
#define OFFSET(x) offsetof(EIA608ToSMPTE436MContext, x)
#define FLAGS AV_OPT_FLAG_BSF_PARAM
// clang-format off
static const AVOption options[] = {
{ "line_number", "line number -- you probably want 9 or 11", OFFSET(line_number), AV_OPT_TYPE_UINT, { .i64 = 9 }, 0, 0xFFFF, FLAGS },
{ "wrapping_type", "wrapping type", OFFSET(wrapping_type_opt), AV_OPT_TYPE_UINT, { .i64 = AV_SMPTE_436M_WRAPPING_TYPE_VANC_FRAME }, 0, 0xFF, FLAGS, .unit = "wrapping_type" },
FF_SMPTE_436M_WRAPPING_TYPE_VANC_AVOPTIONS(FLAGS, "wrapping_type"),
{ "sample_coding", "payload sample coding", OFFSET(sample_coding_opt), AV_OPT_TYPE_UINT, { .i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA }, 0, 0xFF, FLAGS, .unit = "sample_coding" },
FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ANC_AVOPTIONS(FLAGS, "sample_coding"),
{ "initial_cdp_sequence_cntr", "initial cdp_*_sequence_cntr value", OFFSET(cdp_sequence_cntr), AV_OPT_TYPE_UINT, { .i64 = 0 }, 0, 0xFFFF, FLAGS },
{ "cdp_frame_rate", "set the `cdp_frame_rate` fields", OFFSET(cdp_frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "30000/1001" }, 0, INT_MAX, FLAGS },
{ NULL },
};
// clang-format on
static const AVClass eia608_to_smpte436m_class = {
.class_name = "eia608_to_smpte436m bitstream filter",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFBitStreamFilter ff_eia608_to_smpte436m_bsf = {
.p.name = "eia608_to_smpte436m",
.p.codec_ids = (const enum AVCodecID[]){ AV_CODEC_ID_EIA_608, AV_CODEC_ID_NONE },
.p.priv_class = &eia608_to_smpte436m_class,
.priv_data_size = sizeof(EIA608ToSMPTE436MContext),
.init = ff_eia608_to_smpte436m_init,
.filter = ff_eia608_to_smpte436m_filter,
};
+290
View File
@@ -0,0 +1,290 @@
/*
* Copyright (c) 2019 James Almer <jamrial@gmail.com>
*
* 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 "libavutil/mem.h"
#include "get_bits.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "evc.h"
#include "evc_parse.h"
#include "evc_ps.h"
// Access unit data
typedef struct AccessUnitBuffer {
uint8_t *data; // the data buffer
size_t data_size; // size of data in bytes
unsigned capacity; // buffer capacity
} AccessUnitBuffer;
typedef struct EVCFMergeContext {
AVPacket *in, *buffer_pkt;
EVCParamSets ps;
EVCParserPoc poc;
AccessUnitBuffer au_buffer;
} EVCFMergeContext;
static int end_of_access_unit_found(const EVCParamSets *ps, const EVCParserSliceHeader *sh,
const EVCParserPoc *poc, enum EVCNALUnitType nalu_type)
{
EVCParserPPS *pps = ps->pps[sh->slice_pic_parameter_set_id];
EVCParserSPS *sps = ps->sps[pps->pps_seq_parameter_set_id];
av_assert0(sps && pps);
if (sps->profile_idc == 0) { // BASELINE profile
if (nalu_type == EVC_NOIDR_NUT || nalu_type == EVC_IDR_NUT)
return 1;
} else { // MAIN profile
if (nalu_type == EVC_NOIDR_NUT) {
if (poc->PicOrderCntVal != poc->prevPicOrderCntVal)
return 1;
} else if (nalu_type == EVC_IDR_NUT)
return 1;
}
return 0;
}
static void evc_frame_merge_flush(AVBSFContext *bsf)
{
EVCFMergeContext *ctx = bsf->priv_data;
ff_evc_ps_free(&ctx->ps);
av_packet_unref(ctx->in);
av_packet_unref(ctx->buffer_pkt);
ctx->au_buffer.data_size = 0;
}
static int parse_nal_unit(AVBSFContext *bsf, const uint8_t *buf, int buf_size)
{
EVCFMergeContext *ctx = bsf->priv_data;
GetBitContext gb;
enum EVCNALUnitType nalu_type;
int tid, err;
err = init_get_bits8(&gb, buf, buf_size);
if (err < 0)
return err;
// @see ISO_IEC_23094-1_2020, 7.4.2.2 NAL unit header semantic (Table 4 - NAL unit type codes and NAL unit type classes)
// @see enum EVCNALUnitType in evc.h
if (get_bits1(&gb)) {// forbidden_zero_bit
av_log(bsf, AV_LOG_ERROR, "Invalid NAL unit header\n");
return AVERROR_INVALIDDATA;
}
nalu_type = get_bits(&gb, 6) - 1;
if (nalu_type < EVC_NOIDR_NUT || nalu_type > EVC_UNSPEC_NUT62) {
av_log(bsf, AV_LOG_ERROR, "Invalid NAL unit type: (%d)\n", nalu_type);
return AVERROR_INVALIDDATA;
}
tid = get_bits(&gb, 3);
skip_bits(&gb, 5); // nuh_reserved_zero_5bits
skip_bits1(&gb); // nuh_extension_flag
switch (nalu_type) {
case EVC_SPS_NUT:
err = ff_evc_parse_sps(&gb, &ctx->ps);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "SPS parsing error\n");
return err;
}
break;
case EVC_PPS_NUT:
err = ff_evc_parse_pps(&gb, &ctx->ps);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "PPS parsing error\n");
return err;
}
break;
case EVC_IDR_NUT: // Coded slice of a IDR or non-IDR picture
case EVC_NOIDR_NUT: {
EVCParserSliceHeader sh;
err = ff_evc_parse_slice_header(&gb, &sh, &ctx->ps, nalu_type);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Slice header parsing error\n");
return err;
}
// POC (picture order count of the current picture) derivation
// @see ISO/IEC 23094-1:2020(E) 8.3.1 Decoding process for picture order count
err = ff_evc_derive_poc(&ctx->ps, &sh, &ctx->poc, nalu_type, tid);
if (err < 0)
return err;
return end_of_access_unit_found(&ctx->ps, &sh, &ctx->poc, nalu_type);
break;
}
case EVC_SEI_NUT: // Supplemental Enhancement Information
case EVC_APS_NUT: // Adaptation parameter set
case EVC_FD_NUT: // Filler data
default:
break;
}
return 0;
}
static int evc_frame_merge_filter(AVBSFContext *bsf, AVPacket *out)
{
EVCFMergeContext *ctx = bsf->priv_data;
AVPacket *in = ctx->in, *buffer_pkt = ctx->buffer_pkt;
size_t data_size;
int au_end_found = 0, err;
while (!au_end_found) {
uint8_t *buffer;
uint32_t nalu_size;
if (!in->size) {
av_packet_unref(in);
err = ff_bsf_get_packet_ref(bsf, in);
if (err < 0) {
if (err == AVERROR_EOF && ctx->au_buffer.data_size > 0)
break;
return err;
}
/* Buffer packets with timestamps (there should be at most one per AU)
* or any packet if buffer_pkt is empty. The latter is needed to
* passthrough positions in case there are no timestamps like with
* the raw EVC demuxer. */
if (!buffer_pkt->data ||
in->pts != AV_NOPTS_VALUE && buffer_pkt->pts == AV_NOPTS_VALUE) {
err = av_packet_ref(buffer_pkt, in);
if (err < 0)
goto end;
}
}
// Buffer size is not enough for buffer to store NAL unit 4-bytes prefix (length)
if (in->size < EVC_NALU_LENGTH_PREFIX_SIZE)
return AVERROR_INVALIDDATA;
nalu_size = evc_read_nal_unit_length(in->data, EVC_NALU_LENGTH_PREFIX_SIZE, bsf);
if (!nalu_size || nalu_size > INT_MAX) {
av_log(bsf, AV_LOG_ERROR, "Invalid NAL unit size: (%u)\n", nalu_size);
err = AVERROR_INVALIDDATA;
goto end;
}
if (in->size < nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE) {
err = AVERROR_INVALIDDATA;
goto end;
}
err = parse_nal_unit(bsf, in->data + EVC_NALU_LENGTH_PREFIX_SIZE, nalu_size);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Parsing of NAL unit failed\n");
goto end;
}
au_end_found = err;
nalu_size += EVC_NALU_LENGTH_PREFIX_SIZE;
data_size = ctx->au_buffer.data_size + nalu_size;
if (data_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) {
av_log(bsf, AV_LOG_ERROR, "Assembled packet is too big\n");
err = AVERROR(ERANGE);
goto end;
}
buffer = av_fast_realloc(ctx->au_buffer.data, &ctx->au_buffer.capacity,
data_size);
if (!buffer) {
av_freep(&ctx->au_buffer.data);
err = AVERROR_INVALIDDATA;
goto end;
}
ctx->au_buffer.data = buffer;
memcpy(ctx->au_buffer.data + ctx->au_buffer.data_size, in->data, nalu_size);
ctx->au_buffer.data_size = data_size;
in->data += nalu_size;
in->size -= nalu_size;
}
av_packet_unref(in);
data_size = ctx->au_buffer.data_size;
ctx->au_buffer.data_size = 0;
// drop the data in buffer_pkt, if any, but keep the props
av_buffer_unref(&buffer_pkt->buf);
err = av_buffer_realloc(&buffer_pkt->buf, data_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (err < 0)
goto end;
buffer_pkt->data = buffer_pkt->buf->data;
buffer_pkt->size = data_size;
av_packet_move_ref(out, buffer_pkt);
memcpy(out->data, ctx->au_buffer.data, data_size);
memset(out->data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
err = 0;
end:
if (err < 0) {
av_packet_unref(in);
av_packet_unref(buffer_pkt);
ctx->au_buffer.data_size = 0;
}
return err;
}
static int evc_frame_merge_init(AVBSFContext *bsf)
{
EVCFMergeContext *ctx = bsf->priv_data;
ctx->in = av_packet_alloc();
ctx->buffer_pkt = av_packet_alloc();
if (!ctx->in || !ctx->buffer_pkt)
return AVERROR(ENOMEM);
return 0;
}
static void evc_frame_merge_close(AVBSFContext *bsf)
{
EVCFMergeContext *ctx = bsf->priv_data;
av_packet_free(&ctx->in);
av_packet_free(&ctx->buffer_pkt);
ff_evc_ps_free(&ctx->ps);
ctx->au_buffer.capacity = 0;
av_freep(&ctx->au_buffer.data);
ctx->au_buffer.data_size = 0;
}
static const enum AVCodecID evc_frame_merge_codec_ids[] = {
AV_CODEC_ID_EVC, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_evc_frame_merge_bsf = {
.p.name = "evc_frame_merge",
.p.codec_ids = evc_frame_merge_codec_ids,
.priv_data_size = sizeof(EVCFMergeContext),
.init = evc_frame_merge_init,
.flush = evc_frame_merge_flush,
.close = evc_frame_merge_close,
.filter = evc_frame_merge_filter,
};
+475
View File
@@ -0,0 +1,475 @@
/*
* 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 <stdint.h>
#include "libavutil/log.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "av1.h"
#include "av1_parse.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "bytestream.h"
#include "h2645_parse.h"
#include "h264.h"
#include "startcode.h"
#include "vc1_common.h"
#include "vvc.h"
#include "hevc/hevc.h"
typedef struct ExtractExtradataContext {
const AVClass *class;
int (*extract)(AVBSFContext *ctx, AVPacket *pkt,
uint8_t **data, int *size);
/* AV1 specific fields */
AV1Packet av1_pkt;
/* H264/HEVC specific fields */
H2645Packet h2645_pkt;
/* AVOptions */
int remove;
} ExtractExtradataContext;
static int val_in_array(const int *arr, size_t len, int val)
{
for (size_t i = 0; i < len; i++)
if (arr[i] == val)
return 1;
return 0;
}
static int metadata_is_global(const AV1OBU *obu)
{
static const int metadata_obu_types[] = {
AV1_METADATA_TYPE_HDR_CLL, AV1_METADATA_TYPE_HDR_MDCV,
};
GetBitContext gb;
int metadata_type;
if (init_get_bits(&gb, obu->data, obu->size_bits) < 0)
return 0;
metadata_type = get_leb(&gb);
return val_in_array(metadata_obu_types, FF_ARRAY_ELEMS(metadata_obu_types),
metadata_type);
}
static int obu_is_global(const AV1OBU *obu)
{
static const int extradata_obu_types[] = {
AV1_OBU_SEQUENCE_HEADER, AV1_OBU_METADATA,
};
if (!val_in_array(extradata_obu_types, FF_ARRAY_ELEMS(extradata_obu_types),
obu->type))
return 0;
if (obu->type != AV1_OBU_METADATA)
return 1;
return metadata_is_global(obu);
}
static int extract_extradata_av1(AVBSFContext *ctx, AVPacket *pkt,
uint8_t **data, int *size)
{
ExtractExtradataContext *s = ctx->priv_data;
int extradata_size = 0, filtered_size = 0;
int i, has_seq = 0, ret = 0;
ret = ff_av1_packet_split(&s->av1_pkt, pkt->data, pkt->size, ctx);
if (ret < 0)
return ret;
for (i = 0; i < s->av1_pkt.nb_obus; i++) {
AV1OBU *obu = &s->av1_pkt.obus[i];
if (obu_is_global(obu)) {
extradata_size += obu->raw_size;
if (obu->type == AV1_OBU_SEQUENCE_HEADER)
has_seq = 1;
} else if (s->remove) {
filtered_size += obu->raw_size;
}
}
if (extradata_size && has_seq) {
AVBufferRef *filtered_buf = NULL;
PutByteContext pb_filtered_data, pb_extradata;
uint8_t *extradata;
if (s->remove) {
filtered_buf = av_buffer_alloc(filtered_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!filtered_buf) {
return AVERROR(ENOMEM);
}
memset(filtered_buf->data + filtered_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
extradata = av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!extradata) {
av_buffer_unref(&filtered_buf);
return AVERROR(ENOMEM);
}
*data = extradata;
*size = extradata_size;
bytestream2_init_writer(&pb_extradata, extradata, extradata_size);
if (s->remove)
bytestream2_init_writer(&pb_filtered_data, filtered_buf->data, filtered_size);
for (i = 0; i < s->av1_pkt.nb_obus; i++) {
AV1OBU *obu = &s->av1_pkt.obus[i];
if (obu_is_global(obu)) {
bytestream2_put_bufferu(&pb_extradata, obu->raw_data, obu->raw_size);
} else if (s->remove) {
bytestream2_put_bufferu(&pb_filtered_data, obu->raw_data, obu->raw_size);
}
}
if (s->remove) {
av_buffer_unref(&pkt->buf);
pkt->buf = filtered_buf;
pkt->data = filtered_buf->data;
pkt->size = filtered_size;
}
}
return 0;
}
static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
uint8_t **data, int *size)
{
static const int extradata_nal_types_vvc[] = {
VVC_VPS_NUT, VVC_SPS_NUT, VVC_PPS_NUT,
};
static const int extradata_nal_types_hevc[] = {
HEVC_NAL_VPS, HEVC_NAL_SPS, HEVC_NAL_PPS,
};
static const int extradata_nal_types_h264[] = {
H264_NAL_SPS, H264_NAL_PPS,
};
ExtractExtradataContext *s = ctx->priv_data;
int extradata_size = 0, filtered_size = 0;
const int *extradata_nal_types;
size_t nb_extradata_nal_types;
int i, has_sps = 0, has_vps = 0, ret = 0;
if (ctx->par_in->codec_id == AV_CODEC_ID_VVC) {
extradata_nal_types = extradata_nal_types_vvc;
nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_vvc);
} else if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) {
extradata_nal_types = extradata_nal_types_hevc;
nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_hevc);
} else {
extradata_nal_types = extradata_nal_types_h264;
nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_h264);
}
ret = ff_h2645_packet_split(&s->h2645_pkt, pkt->data, pkt->size,
ctx, 0, ctx->par_in->codec_id, H2645_FLAG_SMALL_PADDING);
if (ret < 0)
return ret;
for (i = 0; i < s->h2645_pkt.nb_nals; i++) {
H2645NAL *nal = &s->h2645_pkt.nals[i];
if (val_in_array(extradata_nal_types, nb_extradata_nal_types, nal->type)) {
extradata_size += nal->raw_size + 3;
if (ctx->par_in->codec_id == AV_CODEC_ID_VVC) {
if (nal->type == VVC_SPS_NUT) has_sps = 1;
if (nal->type == VVC_VPS_NUT) has_vps = 1;
} else if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) {
if (nal->type == HEVC_NAL_SPS) has_sps = 1;
if (nal->type == HEVC_NAL_VPS) has_vps = 1;
} else {
if (nal->type == H264_NAL_SPS) has_sps = 1;
}
} else if (s->remove) {
filtered_size += nal->raw_size + 3;
}
}
if (extradata_size &&
((ctx->par_in->codec_id == AV_CODEC_ID_VVC && has_sps) ||
(ctx->par_in->codec_id == AV_CODEC_ID_HEVC && has_sps && has_vps) ||
(ctx->par_in->codec_id == AV_CODEC_ID_H264 && has_sps))) {
AVBufferRef *filtered_buf = NULL;
PutByteContext pb_filtered_data, pb_extradata;
uint8_t *extradata;
if (s->remove) {
filtered_buf = av_buffer_alloc(filtered_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!filtered_buf) {
return AVERROR(ENOMEM);
}
memset(filtered_buf->data + filtered_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
extradata = av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!extradata) {
av_buffer_unref(&filtered_buf);
return AVERROR(ENOMEM);
}
*data = extradata;
*size = extradata_size;
bytestream2_init_writer(&pb_extradata, extradata, extradata_size);
if (s->remove)
bytestream2_init_writer(&pb_filtered_data, filtered_buf->data, filtered_size);
for (i = 0; i < s->h2645_pkt.nb_nals; i++) {
H2645NAL *nal = &s->h2645_pkt.nals[i];
if (val_in_array(extradata_nal_types, nb_extradata_nal_types,
nal->type)) {
bytestream2_put_be24u(&pb_extradata, 1); //startcode
bytestream2_put_bufferu(&pb_extradata, nal->raw_data, nal->raw_size);
} else if (s->remove) {
bytestream2_put_be24u(&pb_filtered_data, 1); // startcode
bytestream2_put_bufferu(&pb_filtered_data, nal->raw_data, nal->raw_size);
}
}
if (s->remove) {
av_buffer_unref(&pkt->buf);
pkt->buf = filtered_buf;
pkt->data = filtered_buf->data;
pkt->size = filtered_size;
}
}
return 0;
}
static int extract_extradata_vc1(AVBSFContext *ctx, AVPacket *pkt,
uint8_t **data, int *size)
{
ExtractExtradataContext *s = ctx->priv_data;
const uint8_t *ptr = pkt->data, *end = pkt->data + pkt->size;
uint32_t state = UINT32_MAX;
int has_extradata = 0, extradata_size = 0;
while (ptr < end) {
ptr = avpriv_find_start_code(ptr, end, &state);
if (state == VC1_CODE_SEQHDR || state == VC1_CODE_ENTRYPOINT) {
has_extradata = 1;
} else if (has_extradata && IS_MARKER(state)) {
extradata_size = ptr - 4 - pkt->data;
break;
}
}
if (extradata_size) {
*data = av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!*data)
return AVERROR(ENOMEM);
memcpy(*data, pkt->data, extradata_size);
*size = extradata_size;
if (s->remove) {
pkt->data += extradata_size;
pkt->size -= extradata_size;
}
}
return 0;
}
static int extract_extradata_mpeg12(AVBSFContext *ctx, AVPacket *pkt,
uint8_t **data, int *size)
{
ExtractExtradataContext *s = ctx->priv_data;
uint32_t state = UINT32_MAX;
int i, found = 0;
for (i = 0; i < pkt->size; i++) {
state = (state << 8) | pkt->data[i];
if (state == 0x1B3)
found = 1;
else if (found && state != 0x1B5 && state < 0x200 && state >= 0x100) {
*size = i - 3;
*data = av_malloc(*size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!*data)
return AVERROR(ENOMEM);
memcpy(*data, pkt->data, *size);
if (s->remove) {
pkt->data += *size;
pkt->size -= *size;
}
break;
}
}
return 0;
}
static int extract_extradata_mpeg4(AVBSFContext *ctx, AVPacket *pkt,
uint8_t **data, int *size)
{
ExtractExtradataContext *s = ctx->priv_data;
const uint8_t *ptr = pkt->data, *end = pkt->data + pkt->size;
uint32_t state = UINT32_MAX;
while (ptr < end) {
ptr = avpriv_find_start_code(ptr, end, &state);
if (state == 0x1B3 || state == 0x1B6) {
if (ptr - pkt->data > 4) {
*size = ptr - 4 - pkt->data;
*data = av_malloc(*size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!*data)
return AVERROR(ENOMEM);
memcpy(*data, pkt->data, *size);
if (s->remove) {
pkt->data += *size;
pkt->size -= *size;
}
}
break;
}
}
return 0;
}
static const struct {
enum AVCodecID id;
int (*extract)(AVBSFContext *ctx, AVPacket *pkt,
uint8_t **data, int *size);
} extract_tab[] = {
{ AV_CODEC_ID_AV1, extract_extradata_av1 },
{ AV_CODEC_ID_AVS2, extract_extradata_mpeg4 },
{ AV_CODEC_ID_AVS3, extract_extradata_mpeg4 },
{ AV_CODEC_ID_CAVS, extract_extradata_mpeg4 },
{ AV_CODEC_ID_H264, extract_extradata_h2645 },
{ AV_CODEC_ID_HEVC, extract_extradata_h2645 },
{ AV_CODEC_ID_MPEG1VIDEO, extract_extradata_mpeg12 },
{ AV_CODEC_ID_MPEG2VIDEO, extract_extradata_mpeg12 },
{ AV_CODEC_ID_MPEG4, extract_extradata_mpeg4 },
{ AV_CODEC_ID_VC1, extract_extradata_vc1 },
{ AV_CODEC_ID_VVC, extract_extradata_h2645 },
};
static int extract_extradata_init(AVBSFContext *ctx)
{
ExtractExtradataContext *s = ctx->priv_data;
int i;
for (i = 0; i < FF_ARRAY_ELEMS(extract_tab); i++) {
if (extract_tab[i].id == ctx->par_in->codec_id) {
s->extract = extract_tab[i].extract;
break;
}
}
if (!s->extract)
return AVERROR_BUG;
return 0;
}
static int extract_extradata_filter(AVBSFContext *ctx, AVPacket *pkt)
{
ExtractExtradataContext *s = ctx->priv_data;
uint8_t *extradata = NULL;
int extradata_size;
int ret = 0;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0)
return ret;
ret = s->extract(ctx, pkt, &extradata, &extradata_size);
if (ret < 0)
goto fail;
if (extradata) {
memset(extradata + extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
extradata, extradata_size);
if (ret < 0) {
av_freep(&extradata);
goto fail;
}
}
return 0;
fail:
av_packet_unref(pkt);
return ret;
}
static void extract_extradata_close(AVBSFContext *ctx)
{
ExtractExtradataContext *s = ctx->priv_data;
ff_av1_packet_uninit(&s->av1_pkt);
ff_h2645_packet_uninit(&s->h2645_pkt);
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_AV1,
AV_CODEC_ID_AVS2,
AV_CODEC_ID_AVS3,
AV_CODEC_ID_CAVS,
AV_CODEC_ID_H264,
AV_CODEC_ID_HEVC,
AV_CODEC_ID_MPEG1VIDEO,
AV_CODEC_ID_MPEG2VIDEO,
AV_CODEC_ID_MPEG4,
AV_CODEC_ID_VC1,
AV_CODEC_ID_VVC,
AV_CODEC_ID_NONE,
};
#define OFFSET(x) offsetof(ExtractExtradataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption options[] = {
{ "remove", "remove the extradata from the bitstream", OFFSET(remove), AV_OPT_TYPE_INT,
{ .i64 = 0 }, 0, 1, FLAGS },
{ NULL },
};
static const AVClass extract_extradata_class = {
.class_name = "extract_extradata",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFBitStreamFilter ff_extract_extradata_bsf = {
.p.name = "extract_extradata",
.p.codec_ids = codec_ids,
.p.priv_class = &extract_extradata_class,
.priv_data_size = sizeof(ExtractExtradataContext),
.init = extract_extradata_init,
.filter = extract_extradata_filter,
.close = extract_extradata_close,
};
+287
View File
@@ -0,0 +1,287 @@
/*
* 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 <stdbool.h>
#include <stdlib.h>
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
typedef struct FilterUnitsContext {
const AVClass *class;
CodedBitstreamContext *cbc;
CodedBitstreamFragment fragment;
const char *pass_types;
const char *remove_types;
enum AVDiscard discard;
int discard_flags;
enum {
NOOP,
PASS,
REMOVE,
} mode;
CodedBitstreamUnitType *type_list;
int nb_types;
bool passthrough;
} 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 *pkt)
{
FilterUnitsContext *ctx = bsf->priv_data;
CodedBitstreamFragment *frag = &ctx->fragment;
int err, i, j;
err = ff_bsf_get_packet_ref(bsf, pkt);
if (err < 0)
return err;
if (ctx->passthrough)
return 0;
err = ff_cbs_read_packet(ctx->cbc, frag, pkt);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n");
goto fail;
}
ff_cbs_discard_units(ctx->cbc, frag, ctx->discard, ctx->discard_flags);
if (ctx->mode != NOOP) {
for (i = frag->nb_units - 1; i >= 0; 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(frag, i);
}
}
if (frag->nb_units == 0) {
// Don't return packets with nothing in them.
err = AVERROR(EAGAIN);
goto fail;
}
err = ff_cbs_write_packet(ctx->cbc, pkt, frag);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n");
goto fail;
}
fail:
if (err < 0)
av_packet_unref(pkt);
ff_cbs_fragment_reset(frag);
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 if (ctx->discard == AVDISCARD_NONE) {
ctx->passthrough = true;
return 0;
}
err = ff_cbs_init(&ctx->cbc, bsf->par_in->codec_id, bsf);
if (err < 0)
return err;
if (ctx->discard == AVDISCARD_NONE) {
// 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 *frag = &ctx->fragment;
err = ff_cbs_read_extradata(ctx->cbc, frag, 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, frag);
if (err < 0)
av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n");
}
ff_cbs_fragment_reset(frag);
}
return err;
}
static void filter_units_close(AVBSFContext *bsf)
{
FilterUnitsContext *ctx = bsf->priv_data;
av_freep(&ctx->type_list);
ff_cbs_fragment_free(&ctx->fragment);
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 },
{ "discard", "Remove the selected frames",
OFFSET(discard), AV_OPT_TYPE_INT,
{ .i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
{ "none" , "discard none",
0, AV_OPT_TYPE_CONST,
{ .i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
{ "default" , "discard none, but can be changed after dynamically",
0, AV_OPT_TYPE_CONST,
{ .i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
{ "nonref", "discard all non-reference frames",
0, AV_OPT_TYPE_CONST,
{ .i64 = AVDISCARD_NONREF }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
{ "bidir", "discard all bidirectional frames",
0, AV_OPT_TYPE_CONST,
{ .i64 = AVDISCARD_BIDIR }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
{ "nonintra", "discard all frames except I frames",
0, AV_OPT_TYPE_CONST,
{ .i64 = AVDISCARD_NONINTRA }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
{ "nonkey", "discard all frames except keyframes",
0, AV_OPT_TYPE_CONST,
{ .i64 = AVDISCARD_NONKEY }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
{ "all", "discard all frames",
0, AV_OPT_TYPE_CONST,
{ .i64 = AVDISCARD_ALL }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"},
{ "discard_flags", "flags to control the discard frame behavior",
OFFSET(discard_flags), AV_OPT_TYPE_FLAGS,
{ .i64 = DISCARD_FLAG_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "discard_flags"},
{ "keep_non_vcl", "non-vcl units even if the picture has been dropped",
0, AV_OPT_TYPE_CONST,
{ .i64 = DISCARD_FLAG_KEEP_NON_VCL }, INT_MIN, INT_MAX, FLAGS, .unit = "discard_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 FFBitStreamFilter ff_filter_units_bsf = {
.p.name = "filter_units",
.p.codec_ids = ff_cbs_all_codec_ids,
.p.priv_class = &filter_units_class,
.priv_data_size = sizeof(FilterUnitsContext),
.init = &filter_units_init,
.close = &filter_units_close,
.filter = &filter_units_filter,
};
+724
View File
@@ -0,0 +1,724 @@
/*
* 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 "libavutil/avstring.h"
#include "libavutil/display.h"
#include "libavutil/common.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_bsf.h"
#include "cbs_h264.h"
#include "cbs_sei.h"
#include "h264.h"
#include "h264_levels.h"
#include "h2645data.h"
#include "sei.h"
enum {
FLIP_HORIZONTAL = 1,
FLIP_VERTICAL = 2,
};
enum {
LEVEL_UNSET = -2,
LEVEL_AUTO = -1,
};
typedef struct H264MetadataContext {
CBSBSFContext common;
int done_first_au;
int aud;
H264RawAUD aud_nal;
AVRational sample_aspect_ratio;
int overscan_appropriate_flag;
int video_format;
int video_full_range_flag;
int colour_primaries;
int transfer_characteristics;
int matrix_coefficients;
int chroma_sample_loc_type;
AVRational tick_rate;
int fixed_frame_rate_flag;
int zero_new_constraint_set_flags;
int crop_left;
int crop_right;
int crop_top;
int crop_bottom;
const char *sei_user_data;
SEIRawUserDataUnregistered sei_user_data_payload;
int delete_filler;
int display_orientation;
double rotate;
int flip;
H264RawSEIDisplayOrientation display_orientation_payload;
int level;
} H264MetadataContext;
static int h264_metadata_insert_aud(AVBSFContext *bsf,
CodedBitstreamFragment *au)
{
H264MetadataContext *ctx = bsf->priv_data;
int primary_pic_type_mask = 0xff;
int err, i, j;
static const int primary_pic_type_table[] = {
0x084, // 2, 7
0x0a5, // 0, 2, 5, 7
0x0e7, // 0, 1, 2, 5, 6, 7
0x210, // 4, 9
0x318, // 3, 4, 8, 9
0x294, // 2, 4, 7, 9
0x3bd, // 0, 2, 3, 4, 5, 7, 8, 9
0x3ff, // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
};
for (i = 0; i < au->nb_units; i++) {
if (au->units[i].type == H264_NAL_SLICE ||
au->units[i].type == H264_NAL_IDR_SLICE) {
H264RawSlice *slice = au->units[i].content;
for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++) {
if (!(primary_pic_type_table[j] &
(1 << slice->header.slice_type)))
primary_pic_type_mask &= ~(1 << j);
}
}
}
for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++)
if (primary_pic_type_mask & (1 << j))
break;
if (j >= FF_ARRAY_ELEMS(primary_pic_type_table)) {
av_log(bsf, AV_LOG_ERROR, "No usable primary_pic_type: "
"invalid slice types?\n");
return AVERROR_INVALIDDATA;
}
ctx->aud_nal = (H264RawAUD) {
.nal_unit_header.nal_unit_type = H264_NAL_AUD,
.primary_pic_type = j,
};
err = ff_cbs_insert_unit_content(au, 0, H264_NAL_AUD,
&ctx->aud_nal, NULL);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n");
return err;
}
return 0;
}
static int h264_metadata_update_sps(AVBSFContext *bsf,
H264RawSPS *sps)
{
H264MetadataContext *ctx = bsf->priv_data;
int need_vui = 0;
int crop_unit_x, crop_unit_y;
if (ctx->sample_aspect_ratio.num && ctx->sample_aspect_ratio.den) {
int num, den, i;
av_reduce(&num, &den, ctx->sample_aspect_ratio.num,
ctx->sample_aspect_ratio.den, 65535);
for (i = 1; i < FF_ARRAY_ELEMS(ff_h2645_pixel_aspect); i++) {
if (num == ff_h2645_pixel_aspect[i].num &&
den == ff_h2645_pixel_aspect[i].den)
break;
}
if (i == FF_ARRAY_ELEMS(ff_h2645_pixel_aspect)) {
sps->vui.aspect_ratio_idc = 255;
sps->vui.sar_width = num;
sps->vui.sar_height = den;
} else {
sps->vui.aspect_ratio_idc = i;
}
sps->vui.aspect_ratio_info_present_flag = 1;
need_vui = 1;
}
#define SET_VUI_FIELD(field) do { \
if (ctx->field >= 0) { \
sps->vui.field = ctx->field; \
need_vui = 1; \
} \
} while (0)
if (ctx->overscan_appropriate_flag >= 0) {
SET_VUI_FIELD(overscan_appropriate_flag);
sps->vui.overscan_info_present_flag = 1;
}
if (ctx->video_format >= 0 ||
ctx->video_full_range_flag >= 0 ||
ctx->colour_primaries >= 0 ||
ctx->transfer_characteristics >= 0 ||
ctx->matrix_coefficients >= 0) {
SET_VUI_FIELD(video_format);
SET_VUI_FIELD(video_full_range_flag);
if (ctx->colour_primaries >= 0 ||
ctx->transfer_characteristics >= 0 ||
ctx->matrix_coefficients >= 0) {
SET_VUI_FIELD(colour_primaries);
SET_VUI_FIELD(transfer_characteristics);
SET_VUI_FIELD(matrix_coefficients);
sps->vui.colour_description_present_flag = 1;
}
sps->vui.video_signal_type_present_flag = 1;
}
if (ctx->chroma_sample_loc_type >= 0) {
sps->vui.chroma_sample_loc_type_top_field =
ctx->chroma_sample_loc_type;
sps->vui.chroma_sample_loc_type_bottom_field =
ctx->chroma_sample_loc_type;
sps->vui.chroma_loc_info_present_flag = 1;
need_vui = 1;
}
if (ctx->tick_rate.num && ctx->tick_rate.den) {
int num, den;
av_reduce(&num, &den, ctx->tick_rate.num, ctx->tick_rate.den,
UINT32_MAX > INT_MAX ? UINT32_MAX : INT_MAX);
sps->vui.time_scale = num;
sps->vui.num_units_in_tick = den;
sps->vui.timing_info_present_flag = 1;
need_vui = 1;
}
SET_VUI_FIELD(fixed_frame_rate_flag);
if (ctx->zero_new_constraint_set_flags) {
sps->constraint_set4_flag = 0;
sps->constraint_set5_flag = 0;
}
if (sps->separate_colour_plane_flag || sps->chroma_format_idc == 0) {
crop_unit_x = 1;
crop_unit_y = 2 - sps->frame_mbs_only_flag;
} else {
crop_unit_x = 1 + (sps->chroma_format_idc < 3);
crop_unit_y = (1 + (sps->chroma_format_idc < 2)) *
(2 - sps->frame_mbs_only_flag);
}
#define CROP(border, unit) do { \
if (ctx->crop_ ## border >= 0) { \
if (ctx->crop_ ## border % unit != 0) { \
av_log(bsf, AV_LOG_ERROR, "Invalid value for crop_%s: " \
"must be a multiple of %d.\n", #border, unit); \
return AVERROR(EINVAL); \
} \
sps->frame_crop_ ## border ## _offset = \
ctx->crop_ ## border / unit; \
sps->frame_cropping_flag = 1; \
} \
} while (0)
CROP(left, crop_unit_x);
CROP(right, crop_unit_x);
CROP(top, crop_unit_y);
CROP(bottom, crop_unit_y);
#undef CROP
if (ctx->level != LEVEL_UNSET) {
int level_idc;
if (ctx->level == LEVEL_AUTO) {
const H264LevelDescriptor *desc;
int64_t bit_rate;
int width, height, dpb_frames;
int framerate;
if (sps->vui.nal_hrd_parameters_present_flag) {
bit_rate = (sps->vui.nal_hrd_parameters.bit_rate_value_minus1[0] + 1) *
(INT64_C(1) << (sps->vui.nal_hrd_parameters.bit_rate_scale + 6));
} else if (sps->vui.vcl_hrd_parameters_present_flag) {
bit_rate = (sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] + 1) *
(INT64_C(1) << (sps->vui.vcl_hrd_parameters.bit_rate_scale + 6));
// Adjust for VCL vs. NAL limits.
bit_rate = bit_rate * 6 / 5;
} else {
bit_rate = 0;
}
// Don't use max_dec_frame_buffering if it is only inferred.
dpb_frames = sps->vui.bitstream_restriction_flag ?
sps->vui.max_dec_frame_buffering : H264_MAX_DPB_FRAMES;
width = 16 * (sps->pic_width_in_mbs_minus1 + 1);
height = 16 * (sps->pic_height_in_map_units_minus1 + 1) *
(2 - sps->frame_mbs_only_flag);
if (sps->vui.timing_info_present_flag)
framerate = sps->vui.time_scale / sps->vui.num_units_in_tick / 2;
else
framerate = 0;
desc = ff_h264_guess_level(sps->profile_idc, bit_rate, framerate,
width, height, dpb_frames);
if (desc) {
level_idc = desc->level_idc;
} else {
av_log(bsf, AV_LOG_WARNING, "Stream does not appear to "
"conform to any level: using level 6.2.\n");
level_idc = 62;
}
} else {
level_idc = ctx->level;
}
if (level_idc == 9) {
if (sps->profile_idc == 66 ||
sps->profile_idc == 77 ||
sps->profile_idc == 88) {
sps->level_idc = 11;
sps->constraint_set3_flag = 1;
} else {
sps->level_idc = 9;
}
} else {
sps->level_idc = level_idc;
}
}
if (need_vui)
sps->vui_parameters_present_flag = 1;
return 0;
}
static int h264_metadata_handle_display_orientation(AVBSFContext *bsf,
AVPacket *pkt,
CodedBitstreamFragment *au,
int seek_point)
{
H264MetadataContext *ctx = bsf->priv_data;
SEIRawMessage *message;
int err;
message = NULL;
while (ff_cbs_sei_find_message(ctx->common.output, au,
SEI_TYPE_DISPLAY_ORIENTATION,
&message) == 0) {
H264RawSEIDisplayOrientation *disp = message->payload;
double angle = disp->anticlockwise_rotation * 180.0 / 65536.0;
int32_t *matrix;
matrix = av_malloc(9 * sizeof(int32_t));
if (!matrix)
return AVERROR(ENOMEM);
/* av_display_rotation_set() expects the angle in the clockwise
* direction, hence the first minus.
* The below code applies the flips after the rotation, yet
* the H.2645 specs require flipping to be applied first.
* Because of R O(phi) = O(-phi) R (where R is flipping around
* an arbitatry axis and O(phi) is the proper rotation by phi)
* we can create display matrices as desired by negating
* the degree once for every flip applied. */
angle = -angle * (1 - 2 * !!disp->hor_flip) * (1 - 2 * !!disp->ver_flip);
av_display_rotation_set(matrix, angle);
av_display_matrix_flip(matrix, disp->hor_flip, disp->ver_flip);
// If there are multiple display orientation messages in an
// access unit, then the last one added to the packet (i.e.
// the first one in the access unit) will prevail.
err = av_packet_add_side_data(pkt, AV_PKT_DATA_DISPLAYMATRIX,
(uint8_t*)matrix,
9 * sizeof(int32_t));
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to attach extracted "
"displaymatrix side data to packet.\n");
av_free(matrix);
return AVERROR(ENOMEM);
}
}
if (ctx->display_orientation == BSF_ELEMENT_REMOVE ||
ctx->display_orientation == BSF_ELEMENT_INSERT) {
ff_cbs_sei_delete_message_type(ctx->common.output, au,
SEI_TYPE_DISPLAY_ORIENTATION);
}
if (ctx->display_orientation == BSF_ELEMENT_INSERT) {
H264RawSEIDisplayOrientation *disp =
&ctx->display_orientation_payload;
uint8_t *data;
size_t size;
int write = 0;
data = av_packet_get_side_data(pkt, AV_PKT_DATA_DISPLAYMATRIX, &size);
if (data && size >= 9 * sizeof(int32_t)) {
int32_t matrix[9];
double dmatrix[9];
int hflip, vflip, i;
double scale_x, scale_y, angle;
memcpy(matrix, data, sizeof(matrix));
for (i = 0; i < 9; i++)
dmatrix[i] = matrix[i] / 65536.0;
// Extract scale factors.
scale_x = hypot(dmatrix[0], dmatrix[3]);
scale_y = hypot(dmatrix[1], dmatrix[4]);
// Select flips to make the main diagonal positive.
hflip = dmatrix[0] < 0.0;
vflip = dmatrix[4] < 0.0;
if (hflip)
scale_x = -scale_x;
if (vflip)
scale_y = -scale_y;
// Rescale.
for (i = 0; i < 9; i += 3) {
dmatrix[i] /= scale_x;
dmatrix[i + 1] /= scale_y;
}
// Extract rotation.
angle = atan2(dmatrix[3], dmatrix[0]);
if (!(angle >= -M_PI && angle <= M_PI) ||
matrix[2] != 0.0 || matrix[5] != 0.0 ||
matrix[6] != 0.0 || matrix[7] != 0.0) {
av_log(bsf, AV_LOG_WARNING, "Input display matrix is not "
"representable in H.264 parameters.\n");
} else {
disp->hor_flip = hflip;
disp->ver_flip = vflip;
disp->anticlockwise_rotation =
(uint16_t)rint((angle >= 0.0 ? angle
: angle + 2 * M_PI) *
32768.0 / M_PI);
write = 1;
}
}
if (seek_point) {
if (!isnan(ctx->rotate)) {
disp->anticlockwise_rotation =
(uint16_t)rint((ctx->rotate >= 0.0 ? ctx->rotate
: ctx->rotate + 360.0) *
65536.0 / 360.0);
write = 1;
}
if (ctx->flip) {
disp->hor_flip = !!(ctx->flip & FLIP_HORIZONTAL);
disp->ver_flip = !!(ctx->flip & FLIP_VERTICAL);
write = 1;
}
}
if (write) {
disp->display_orientation_repetition_period = 1;
err = ff_cbs_sei_add_message(ctx->common.output, au, 1,
SEI_TYPE_DISPLAY_ORIENTATION,
disp, NULL);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to add display orientation "
"SEI message to access unit.\n");
return err;
}
}
}
return 0;
}
static int h264_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt,
CodedBitstreamFragment *au)
{
H264MetadataContext *ctx = bsf->priv_data;
int err, i, has_sps, seek_point;
if (ctx->aud == BSF_ELEMENT_REMOVE) {
for (i = au->nb_units - 1; i >= 0; i--) {
if (au->units[i].type == H264_NAL_AUD)
ff_cbs_delete_unit(au, i);
}
} else if (ctx->aud == BSF_ELEMENT_INSERT) {
if (pkt) {
err = h264_metadata_insert_aud(bsf, au);
if (err < 0)
return err;
}
}
has_sps = 0;
for (i = 0; i < au->nb_units; i++) {
if (au->units[i].type == H264_NAL_SPS) {
err = h264_metadata_update_sps(bsf, au->units[i].content);
if (err < 0)
return err;
has_sps = 1;
}
}
if (pkt) {
// The current packet should be treated as a seek point for metadata
// insertion if any of:
// - It is the first packet in the stream.
// - It contains an SPS, indicating that a sequence might start here.
// - It is marked as containing a key frame.
seek_point = !ctx->done_first_au || has_sps ||
(pkt->flags & AV_PKT_FLAG_KEY);
} else {
seek_point = 0;
}
if (ctx->sei_user_data && seek_point) {
err = ff_cbs_sei_add_message(ctx->common.output, au, 1,
SEI_TYPE_USER_DATA_UNREGISTERED,
&ctx->sei_user_data_payload, NULL);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to add user data SEI "
"message to access unit.\n");
return err;
}
}
if (ctx->delete_filler) {
for (i = au->nb_units - 1; i >= 0; i--) {
if (au->units[i].type == H264_NAL_FILLER_DATA) {
ff_cbs_delete_unit(au, i);
continue;
}
}
ff_cbs_sei_delete_message_type(ctx->common.output, au,
SEI_TYPE_FILLER_PAYLOAD);
}
if (pkt && ctx->display_orientation != BSF_ELEMENT_PASS) {
err = h264_metadata_handle_display_orientation(bsf, pkt, au,
seek_point);
if (err < 0)
return err;
}
if (pkt)
ctx->done_first_au = 1;
return 0;
}
static const CBSBSFType h264_metadata_type = {
.codec_id = AV_CODEC_ID_H264,
.fragment_name = "access unit",
.unit_name = "NAL unit",
.update_fragment = &h264_metadata_update_fragment,
};
static int h264_metadata_init(AVBSFContext *bsf)
{
H264MetadataContext *ctx = bsf->priv_data;
if (ctx->sei_user_data) {
SEIRawUserDataUnregistered *udu = &ctx->sei_user_data_payload;
int i, j;
// Parse UUID. It must be a hex string of length 32, possibly
// containing '-'s between hex digits (which we ignore).
for (i = j = 0; j < 32 && i < 64 && ctx->sei_user_data[i]; i++) {
int c, v;
c = ctx->sei_user_data[i];
if (c == '-') {
continue;
} else if (av_isxdigit(c)) {
c = av_tolower(c);
v = (c <= '9' ? c - '0' : c - 'a' + 10);
} else {
break;
}
if (j & 1)
udu->uuid_iso_iec_11578[j / 2] |= v;
else
udu->uuid_iso_iec_11578[j / 2] = v << 4;
++j;
}
if (j == 32 && ctx->sei_user_data[i] == '+') {
udu->data = (uint8_t*)ctx->sei_user_data + i + 1;
udu->data_length = strlen(udu->data) + 1;
} else {
av_log(bsf, AV_LOG_ERROR, "Invalid user data: "
"must be \"UUID+string\".\n");
return AVERROR(EINVAL);
}
}
return ff_cbs_bsf_generic_init(bsf, &h264_metadata_type);
}
#define OFFSET(x) offsetof(H264MetadataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption h264_metadata_options[] = {
BSF_ELEMENT_OPTIONS_PIR("aud", "Access Unit Delimiter NAL units",
aud, FLAGS),
{ "sample_aspect_ratio", "Set sample aspect ratio (table E-1)",
OFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL,
{ .dbl = 0.0 }, 0, 65535, FLAGS },
{ "overscan_appropriate_flag", "Set VUI overscan appropriate flag",
OFFSET(overscan_appropriate_flag), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 1, FLAGS },
{ "video_format", "Set video format (table E-2)",
OFFSET(video_format), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 7, FLAGS},
{ "video_full_range_flag", "Set video full range flag",
OFFSET(video_full_range_flag), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 1, FLAGS },
{ "colour_primaries", "Set colour primaries (table E-3)",
OFFSET(colour_primaries), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "transfer_characteristics", "Set transfer characteristics (table E-4)",
OFFSET(transfer_characteristics), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "matrix_coefficients", "Set matrix coefficients (table E-5)",
OFFSET(matrix_coefficients), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "chroma_sample_loc_type", "Set chroma sample location type (figure E-1)",
OFFSET(chroma_sample_loc_type), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 5, FLAGS },
{ "tick_rate", "Set VUI tick rate (time_scale / num_units_in_tick)",
OFFSET(tick_rate), AV_OPT_TYPE_RATIONAL,
{ .dbl = 0.0 }, 0, UINT_MAX, FLAGS },
{ "fixed_frame_rate_flag", "Set VUI fixed frame rate flag",
OFFSET(fixed_frame_rate_flag), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 1, FLAGS },
{ "zero_new_constraint_set_flags", "Set constraint_set4_flag / constraint_set5_flag to zero",
OFFSET(zero_new_constraint_set_flags), AV_OPT_TYPE_BOOL,
{ .i64 = 0 }, 0, 1, FLAGS },
{ "crop_left", "Set left border crop offset",
OFFSET(crop_left), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, H264_MAX_WIDTH, FLAGS },
{ "crop_right", "Set right border crop offset",
OFFSET(crop_right), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, H264_MAX_WIDTH, FLAGS },
{ "crop_top", "Set top border crop offset",
OFFSET(crop_top), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, H264_MAX_HEIGHT, FLAGS },
{ "crop_bottom", "Set bottom border crop offset",
OFFSET(crop_bottom), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, H264_MAX_HEIGHT, FLAGS },
{ "sei_user_data", "Insert SEI user data (UUID+string)",
OFFSET(sei_user_data), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS },
{ "delete_filler", "Delete all filler (both NAL and SEI)",
OFFSET(delete_filler), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS},
BSF_ELEMENT_OPTIONS_PIRE("display_orientation",
"Display orientation SEI",
display_orientation, FLAGS),
{ "rotate", "Set rotation in display orientation SEI (anticlockwise angle in degrees)",
OFFSET(rotate), AV_OPT_TYPE_DOUBLE,
{ .dbl = NAN }, -360.0, +360.0, FLAGS },
{ "flip", "Set flip in display orientation SEI",
OFFSET(flip), AV_OPT_TYPE_FLAGS,
{ .i64 = 0 }, 0, FLIP_HORIZONTAL | FLIP_VERTICAL, FLAGS, .unit = "flip" },
{ "horizontal", "Set hor_flip",
0, AV_OPT_TYPE_CONST,
{ .i64 = FLIP_HORIZONTAL }, .flags = FLAGS, .unit = "flip" },
{ "vertical", "Set ver_flip",
0, AV_OPT_TYPE_CONST,
{ .i64 = FLIP_VERTICAL }, .flags = FLAGS, .unit = "flip" },
{ "level", "Set level (table A-1)",
OFFSET(level), AV_OPT_TYPE_INT,
{ .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, .unit = "level" },
{ "auto", "Attempt to guess level from stream properties",
0, AV_OPT_TYPE_CONST,
{ .i64 = LEVEL_AUTO }, .flags = FLAGS, .unit = "level" },
#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, .flags = FLAGS, .unit = "level"
{ LEVEL("1", 10) },
{ LEVEL("1b", 9) },
{ LEVEL("1.1", 11) },
{ LEVEL("1.2", 12) },
{ LEVEL("1.3", 13) },
{ LEVEL("2", 20) },
{ LEVEL("2.1", 21) },
{ LEVEL("2.2", 22) },
{ LEVEL("3", 30) },
{ LEVEL("3.1", 31) },
{ LEVEL("3.2", 32) },
{ LEVEL("4", 40) },
{ LEVEL("4.1", 41) },
{ LEVEL("4.2", 42) },
{ LEVEL("5", 50) },
{ LEVEL("5.1", 51) },
{ LEVEL("5.2", 52) },
{ LEVEL("6", 60) },
{ LEVEL("6.1", 61) },
{ LEVEL("6.2", 62) },
#undef LEVEL
{ NULL }
};
static const AVClass h264_metadata_class = {
.class_name = "h264_metadata_bsf",
.item_name = av_default_item_name,
.option = h264_metadata_options,
.version = LIBAVUTIL_VERSION_INT,
};
static const enum AVCodecID h264_metadata_codec_ids[] = {
AV_CODEC_ID_H264, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_h264_metadata_bsf = {
.p.name = "h264_metadata",
.p.codec_ids = h264_metadata_codec_ids,
.p.priv_class = &h264_metadata_class,
.priv_data_size = sizeof(H264MetadataContext),
.init = &h264_metadata_init,
.close = &ff_cbs_bsf_generic_close,
.filter = &ff_cbs_bsf_generic_filter,
};
+473
View File
@@ -0,0 +1,473 @@
/*
* H.264 MP4 to Annex B byte stream format filter
* Copyright (c) 2007 Benoit Fouet <benoit.fouet@free.fr>
*
* 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 <string.h>
#include "libavutil/avassert.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "bytestream.h"
#include "defs.h"
#include "h264.h"
#include "sei.h"
typedef struct H264BSFContext {
uint8_t *sps;
uint8_t *pps;
int sps_size;
int pps_size;
unsigned sps_buf_size;
unsigned pps_buf_size;
uint8_t length_size;
uint8_t new_idr;
uint8_t idr_sps_seen;
uint8_t idr_pps_seen;
int extradata_parsed;
} H264BSFContext;
enum PsSource {
PS_OUT_OF_BAND = -1,
PS_NONE = 0,
PS_IN_BAND = 1,
};
static void count_or_copy(uint8_t **out, uint64_t *out_size,
const uint8_t *in, int in_size, enum PsSource ps, int copy)
{
uint8_t start_code_size;
if (ps == PS_OUT_OF_BAND)
/* start code already present in out-of-band ps data, so don't need to
* add it manually again
*/
start_code_size = 0;
else if (ps == PS_IN_BAND || *out_size == 0)
start_code_size = 4;
else
start_code_size = 3;
if (copy) {
memcpy(*out + start_code_size, in, in_size);
if (start_code_size == 4) {
AV_WB32(*out, 1);
} else if (start_code_size) {
(*out)[0] =
(*out)[1] = 0;
(*out)[2] = 1;
}
*out += start_code_size + in_size;
}
*out_size += start_code_size + in_size;
}
static int h264_extradata_to_annexb(AVBSFContext *ctx,
uint8_t *extradata, int extradata_size)
{
H264BSFContext *s = ctx->priv_data;
GetByteContext ogb, *gb = &ogb;
uint16_t unit_size;
uint32_t total_size = 0;
uint8_t *out = NULL, unit_nb, sps_done = 0;
static const uint8_t nalu_header[4] = { 0, 0, 0, 1 };
const int padding = AV_INPUT_BUFFER_PADDING_SIZE;
int length_size, pps_offset = 0;
if (extradata_size < 7) {
av_log(ctx, AV_LOG_ERROR, "Invalid extradata size: %d\n", extradata_size);
return AVERROR_INVALIDDATA;
}
bytestream2_init(gb, extradata, extradata_size);
bytestream2_skipu(gb, 4);
/* retrieve length coded size */
length_size = (bytestream2_get_byteu(gb) & 0x3) + 1;
/* retrieve sps and pps unit(s) */
unit_nb = bytestream2_get_byteu(gb) & 0x1f; /* number of sps unit(s) */
if (!unit_nb) {
goto pps;
}
while (unit_nb--) {
int err;
/* possible overread ok due to padding */
unit_size = bytestream2_get_be16u(gb);
total_size += unit_size + 4;
av_assert1(total_size <= INT_MAX - padding);
if (bytestream2_get_bytes_left(gb) < unit_size + !sps_done) {
av_log(ctx, AV_LOG_ERROR, "Global extradata truncated, "
"corrupted stream or invalid MP4/AVCC bitstream\n");
av_free(out);
return AVERROR_INVALIDDATA;
}
if ((err = av_reallocp(&out, total_size + padding)) < 0)
return err;
memcpy(out + total_size - unit_size - 4, nalu_header, 4);
bytestream2_get_bufferu(gb, out + total_size - unit_size, unit_size);
pps:
if (!unit_nb && !sps_done++) {
unit_nb = bytestream2_get_byteu(gb); /* number of pps unit(s) */
pps_offset = total_size;
}
}
if (out)
memset(out + total_size, 0, padding);
if (pps_offset) {
uint8_t *sps;
s->sps_size = pps_offset;
sps = av_fast_realloc(s->sps, &s->sps_buf_size, s->sps_size);
if (!sps) {
av_free(out);
return AVERROR(ENOMEM);
}
s->sps = sps;
memcpy(s->sps, out, s->sps_size);
} else {
av_log(ctx, AV_LOG_WARNING,
"Warning: SPS NALU missing or invalid. "
"The resulting stream may not play.\n");
}
if (pps_offset < total_size) {
uint8_t *pps;
s->pps_size = total_size - pps_offset;
pps = av_fast_realloc(s->pps, &s->pps_buf_size, s->pps_size);
if (!pps) {
av_freep(&s->sps);
av_free(out);
return AVERROR(ENOMEM);
}
s->pps = pps;
memcpy(s->pps, out + pps_offset, s->pps_size);
} else {
av_log(ctx, AV_LOG_WARNING,
"Warning: PPS NALU missing or invalid. "
"The resulting stream may not play.\n");
}
av_freep(&ctx->par_out->extradata);
ctx->par_out->extradata = out;
ctx->par_out->extradata_size = total_size;
s->length_size = length_size;
s->new_idr = 1;
s->idr_sps_seen = 0;
s->idr_pps_seen = 0;
s->extradata_parsed = 1;
return 0;
}
static int h264_mp4toannexb_save_ps(uint8_t **dst, int *dst_size,
unsigned *dst_buf_size,
const uint8_t *nal, uint32_t nal_size,
int first)
{
static const uint8_t nalu_header[4] = { 0, 0, 0, 1 };
const int start_code_size = sizeof(nalu_header);
uint8_t *ptr;
uint32_t size;
if (first)
size = 0;
else
size = *dst_size;
ptr = av_fast_realloc(*dst, dst_buf_size, size + nal_size + start_code_size);
if (!ptr)
return AVERROR(ENOMEM);
memcpy(ptr + size, nalu_header, start_code_size);
size += start_code_size;
memcpy(ptr + size, nal, nal_size);
size += nal_size;
*dst = ptr;
*dst_size = size;
return 0;
}
static int h264_mp4toannexb_filter_ps(H264BSFContext *s,
const uint8_t *buf,
const uint8_t *buf_end)
{
int sps_count = 0;
int pps_count = 0;
uint8_t unit_type;
do {
uint32_t nal_size = 0;
/* possible overread ok due to padding */
for (int i = 0; i < s->length_size; i++)
nal_size = (nal_size << 8) | buf[i];
buf += s->length_size;
/* This check requires the cast as the right side might
* otherwise be promoted to an unsigned value. */
if ((int64_t)nal_size > buf_end - buf)
return AVERROR_INVALIDDATA;
if (!nal_size)
continue;
unit_type = *buf & 0x1f;
if (unit_type == H264_NAL_SPS) {
h264_mp4toannexb_save_ps(&s->sps, &s->sps_size, &s->sps_buf_size, buf,
nal_size, !sps_count);
sps_count++;
} else if (unit_type == H264_NAL_PPS) {
h264_mp4toannexb_save_ps(&s->pps, &s->pps_size, &s->pps_buf_size, buf,
nal_size, !pps_count);
pps_count++;
}
buf += nal_size;
} while (buf < buf_end);
return 0;
}
static int h264_mp4toannexb_init(AVBSFContext *ctx)
{
int extra_size = ctx->par_in->extradata_size;
/* retrieve sps and pps NAL units from extradata */
if (!extra_size ||
(extra_size >= 3 && AV_RB24(ctx->par_in->extradata) == 1) ||
(extra_size >= 4 && AV_RB32(ctx->par_in->extradata) == 1)) {
av_log(ctx, AV_LOG_VERBOSE,
"The input looks like it is Annex B already\n");
return 0;
}
return h264_extradata_to_annexb(ctx,
ctx->par_in->extradata,
ctx->par_in->extradata_size);
}
static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt)
{
H264BSFContext *s = ctx->priv_data;
AVPacket *in;
uint8_t unit_type, new_idr, sps_seen, pps_seen;
const uint8_t *buf;
const uint8_t *buf_end;
uint8_t *out;
uint64_t out_size;
int ret;
size_t extradata_size;
uint8_t *extradata;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
extradata = av_packet_get_side_data(in, AV_PKT_DATA_NEW_EXTRADATA,
&extradata_size);
if (extradata && extradata[0] == 1) {
ret = h264_extradata_to_annexb(ctx, extradata, extradata_size);
if (ret < 0)
goto fail;
av_packet_side_data_remove(in->side_data, &in->side_data_elems,
AV_PKT_DATA_NEW_EXTRADATA);
}
/* nothing to filter */
if (!s->extradata_parsed) {
av_packet_move_ref(opkt, in);
av_packet_free(&in);
return 0;
}
buf_end = in->data + in->size;
ret = h264_mp4toannexb_filter_ps(s, in->data, buf_end);
if (ret < 0)
goto fail;
#define LOG_ONCE(...) \
if (j) \
av_log(__VA_ARGS__)
for (int j = 0; j < 2; j++) {
buf = in->data;
new_idr = s->new_idr;
sps_seen = s->idr_sps_seen;
pps_seen = s->idr_pps_seen;
out_size = 0;
do {
uint32_t nal_size = 0;
enum PsSource ps;
/* possible overread ok due to padding */
for (int i = 0; i < s->length_size; i++)
nal_size = (nal_size << 8) | buf[i];
buf += s->length_size;
/* This check requires the cast as the right side might
* otherwise be promoted to an unsigned value. */
if ((int64_t)nal_size > buf_end - buf) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
if (!nal_size)
continue;
unit_type = *buf & 0x1f;
if (unit_type == H264_NAL_SPS) {
sps_seen = new_idr = 1;
} else if (unit_type == H264_NAL_PPS) {
pps_seen = new_idr = 1;
/* if SPS has not been seen yet, prepend the AVCC one to PPS */
if (!sps_seen) {
if (!s->sps_size) {
LOG_ONCE(ctx, AV_LOG_WARNING, "SPS not present in the stream, nor in AVCC, stream may be unreadable\n");
} else {
count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j);
sps_seen = 1;
}
}
}
/* If this is a new IDR picture following an IDR picture, reset the idr flag.
* Just check first_mb_in_slice to be 0 as this is the simplest solution.
* This could be checking idr_pic_id instead, but would complexify the parsing. */
if (!new_idr && unit_type == H264_NAL_IDR_SLICE && (buf[1] & 0x80))
new_idr = 1;
/* If this is a buffering period SEI without a corresponding sps/pps
* then prepend any existing sps/pps before the SEI */
if (unit_type == H264_NAL_SEI && buf[1] == SEI_TYPE_BUFFERING_PERIOD &&
!sps_seen && !pps_seen) {
if (s->sps_size) {
count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j);
sps_seen = 1;
}
if (s->pps_size) {
count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j);
pps_seen = 1;
}
}
/* prepend only to the first type 5 NAL unit of an IDR picture, if no sps/pps are already present */
if (new_idr && unit_type == H264_NAL_IDR_SLICE && !sps_seen && !pps_seen) {
if (s->sps_size)
count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j);
if (s->pps_size)
count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j);
new_idr = 0;
/* if only SPS has been seen, also insert PPS */
} else if (new_idr && unit_type == H264_NAL_IDR_SLICE && sps_seen && !pps_seen) {
if (!s->pps_size) {
LOG_ONCE(ctx, AV_LOG_WARNING, "PPS not present in the stream, nor in AVCC, stream may be unreadable\n");
} else {
count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j);
}
}
if (unit_type == H264_NAL_SPS || unit_type == H264_NAL_PPS)
ps = PS_IN_BAND;
else
ps = PS_NONE;
count_or_copy(&out, &out_size, buf, nal_size, ps, j);
if (unit_type == H264_NAL_SLICE) {
new_idr = 1;
sps_seen = 0;
pps_seen = 0;
}
buf += nal_size;
} while (buf < buf_end);
if (!j) {
if (out_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
ret = av_new_packet(opkt, out_size);
if (ret < 0)
goto fail;
out = opkt->data;
}
}
#undef LOG_ONCE
av_assert1(out_size == opkt->size);
s->new_idr = new_idr;
s->idr_sps_seen = sps_seen;
s->idr_pps_seen = pps_seen;
ret = av_packet_copy_props(opkt, in);
if (ret < 0)
goto fail;
fail:
if (ret < 0)
av_packet_unref(opkt);
av_packet_free(&in);
return ret;
}
static void h264_mp4toannexb_close(AVBSFContext *ctx)
{
H264BSFContext *s = ctx->priv_data;
av_freep(&s->sps);
av_freep(&s->pps);
}
static void h264_mp4toannexb_flush(AVBSFContext *ctx)
{
H264BSFContext *s = ctx->priv_data;
s->idr_sps_seen = 0;
s->idr_pps_seen = 0;
s->new_idr = s->extradata_parsed;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_H264, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_h264_mp4toannexb_bsf = {
.p.name = "h264_mp4toannexb",
.p.codec_ids = codec_ids,
.priv_data_size = sizeof(H264BSFContext),
.init = h264_mp4toannexb_init,
.filter = h264_mp4toannexb_filter,
.close = h264_mp4toannexb_close,
.flush = h264_mp4toannexb_flush,
};
+126
View File
@@ -0,0 +1,126 @@
/*
* 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 <inttypes.h>
#include "libavutil/log.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_bsf.h"
#include "cbs_h264.h"
#include "codec_id.h"
#include "h264.h"
#include "packet.h"
#define NEW_GLOBAL_PIC_INIT_QP 26
typedef struct H264RedundantPPSContext {
CBSBSFContext common;
} H264RedundantPPSContext;
static int h264_redundant_pps_fixup_pps(H264RedundantPPSContext *ctx,
CodedBitstreamUnit *unit)
{
H264RawPPS *pps;
int err;
// The changes we are about to perform affect the parsing process,
// so we must make sure that the PPS is writable, otherwise the
// parsing of future slices will be incorrect and even raise errors.
err = ff_cbs_make_unit_writable(ctx->common.input, unit);
if (err < 0)
return err;
pps = unit->content;
// Overwrite pic_init_qp with the global value.
pps->pic_init_qp_minus26 = NEW_GLOBAL_PIC_INIT_QP - 26;
// Some PPSs have this set, so it must be set in all of them.
// (Slices which do not use such a PPS on input will still have
// *_weight_l*flag as zero and therefore write equivalently.)
pps->weighted_pred_flag = 1;
return 0;
}
static int h264_redundant_pps_fixup_slice(H264RedundantPPSContext *ctx,
H264RawSliceHeader *slice)
{
const CodedBitstreamH264Context *const in = ctx->common.input->priv_data;
const H264RawPPS *const pps = in->pps[slice->pic_parameter_set_id];
// We modified the PPS's qp value, now offset this by applying
// the negative offset to the slices.
slice->slice_qp_delta += pps->pic_init_qp_minus26
- (NEW_GLOBAL_PIC_INIT_QP - 26);
return 0;
}
static int h264_redundant_pps_update_fragment(AVBSFContext *bsf,
AVPacket *pkt,
CodedBitstreamFragment *au)
{
H264RedundantPPSContext *ctx = bsf->priv_data;
int err, i;
for (i = 0; i < au->nb_units; i++) {
CodedBitstreamUnit *nal = &au->units[i];
if (nal->type == H264_NAL_PPS) {
err = h264_redundant_pps_fixup_pps(ctx, nal);
if (err < 0)
return err;
}
if (nal->type == H264_NAL_SLICE ||
nal->type == H264_NAL_IDR_SLICE) {
H264RawSlice *slice = nal->content;
h264_redundant_pps_fixup_slice(ctx, &slice->header);
}
}
return 0;
}
static const CBSBSFType h264_redundant_pps_type = {
.codec_id = AV_CODEC_ID_H264,
.fragment_name = "access unit",
.unit_name = "NAL unit",
.update_fragment = &h264_redundant_pps_update_fragment,
};
static int h264_redundant_pps_init(AVBSFContext *bsf)
{
return ff_cbs_bsf_generic_init(bsf, &h264_redundant_pps_type);
}
static const enum AVCodecID h264_redundant_pps_codec_ids[] = {
AV_CODEC_ID_H264, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_h264_redundant_pps_bsf = {
.p.name = "h264_redundant_pps",
.p.codec_ids = h264_redundant_pps_codec_ids,
.priv_data_size = sizeof(H264RedundantPPSContext),
.init = &h264_redundant_pps_init,
.close = &ff_cbs_bsf_generic_close,
.filter = &ff_cbs_bsf_generic_filter,
};
+599
View File
@@ -0,0 +1,599 @@
/*
* 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 "libavutil/common.h"
#include "libavutil/opt.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_bsf.h"
#include "cbs_h265.h"
#include "h2645data.h"
#include "h265_profile_level.h"
#include "hevc/hevc.h"
enum {
LEVEL_UNSET = -2,
LEVEL_AUTO = -1,
};
typedef struct H265MetadataContext {
CBSBSFContext common;
H265RawAUD aud_nal;
int aud;
AVRational sample_aspect_ratio;
int video_format;
int video_full_range_flag;
int colour_primaries;
int transfer_characteristics;
int matrix_coefficients;
int chroma_sample_loc_type;
AVRational tick_rate;
int poc_proportional_to_timing_flag;
int num_ticks_poc_diff_one;
int crop_left;
int crop_right;
int crop_top;
int crop_bottom;
int width;
int height;
int level;
int level_guess;
int level_warned;
} H265MetadataContext;
static void h265_metadata_guess_level(AVBSFContext *bsf,
const CodedBitstreamFragment *au)
{
H265MetadataContext *ctx = bsf->priv_data;
const H265LevelDescriptor *desc;
const H265RawProfileTierLevel *ptl = NULL;
const H265RawHRDParameters *hrd = NULL;
int64_t bit_rate = 0;
int width = 0, height = 0;
int tile_cols = 0, tile_rows = 0;
int max_dec_pic_buffering = 0;
int i;
for (i = 0; i < au->nb_units; i++) {
const CodedBitstreamUnit *unit = &au->units[i];
if (unit->type == HEVC_NAL_VPS) {
const H265RawVPS *vps = unit->content;
ptl = &vps->profile_tier_level;
max_dec_pic_buffering = vps->vps_max_dec_pic_buffering_minus1[0] + 1;
if (vps->vps_num_hrd_parameters > 0)
hrd = &vps->hrd_parameters[0];
} else if (unit->type == HEVC_NAL_SPS) {
const H265RawSPS *sps = unit->content;
ptl = &sps->profile_tier_level;
max_dec_pic_buffering = sps->sps_max_dec_pic_buffering_minus1[0] + 1;
width = sps->pic_width_in_luma_samples;
height = sps->pic_height_in_luma_samples;
if (sps->vui.vui_hrd_parameters_present_flag)
hrd = &sps->vui.hrd_parameters;
} else if (unit->type == HEVC_NAL_PPS) {
const H265RawPPS *pps = unit->content;
if (pps->tiles_enabled_flag) {
tile_cols = pps->num_tile_columns_minus1 + 1;
tile_rows = pps->num_tile_rows_minus1 + 1;
}
}
}
if (hrd) {
if (hrd->nal_hrd_parameters_present_flag) {
bit_rate = (hrd->nal_sub_layer_hrd_parameters[0].bit_rate_value_minus1[0] + 1) *
(INT64_C(1) << hrd->bit_rate_scale + 6);
} else if (hrd->vcl_hrd_parameters_present_flag) {
bit_rate = (hrd->vcl_sub_layer_hrd_parameters[0].bit_rate_value_minus1[0] + 1) *
(INT64_C(1) << hrd->bit_rate_scale + 6);
// Adjust for VCL vs. NAL limits.
bit_rate = bit_rate * 11 / 10;
}
}
desc = ff_h265_guess_level(ptl, bit_rate, width, height,
0, tile_rows, tile_cols,
max_dec_pic_buffering);
if (desc) {
av_log(bsf, AV_LOG_DEBUG, "Stream appears to conform to "
"level %s.\n", desc->name);
ctx->level_guess = desc->level_idc;
}
}
static void h265_metadata_update_level(AVBSFContext *bsf,
uint8_t *level_idc)
{
H265MetadataContext *ctx = bsf->priv_data;
if (ctx->level != LEVEL_UNSET) {
if (ctx->level == LEVEL_AUTO) {
if (ctx->level_guess) {
*level_idc = ctx->level_guess;
} else {
if (!ctx->level_warned) {
av_log(bsf, AV_LOG_WARNING, "Unable to determine level "
"of stream: using level 8.5.\n");
ctx->level_warned = 1;
}
*level_idc = 255;
}
} else {
*level_idc = ctx->level;
}
}
}
static int h265_metadata_update_vps(AVBSFContext *bsf,
H265RawVPS *vps)
{
H265MetadataContext *ctx = bsf->priv_data;
if (ctx->tick_rate.num && ctx->tick_rate.den) {
int num, den;
av_reduce(&num, &den, ctx->tick_rate.num, ctx->tick_rate.den,
UINT32_MAX > INT_MAX ? UINT32_MAX : INT_MAX);
vps->vps_time_scale = num;
vps->vps_num_units_in_tick = den;
vps->vps_timing_info_present_flag = 1;
if (ctx->num_ticks_poc_diff_one > 0) {
vps->vps_num_ticks_poc_diff_one_minus1 =
ctx->num_ticks_poc_diff_one - 1;
vps->vps_poc_proportional_to_timing_flag = 1;
} else if (ctx->num_ticks_poc_diff_one == 0) {
vps->vps_poc_proportional_to_timing_flag = 0;
}
}
h265_metadata_update_level(bsf, &vps->profile_tier_level.general_level_idc);
return 0;
}
static int h265_metadata_deduce_crop(AVBSFContext *bsf, const H265RawSPS *sps,
int *crop_left, int *crop_right,
int *crop_top, int *crop_bottom)
{
const H265MetadataContext *ctx = bsf->priv_data;
int left = ctx->crop_left;
int right = ctx->crop_right;
int top = ctx->crop_top;
int bottom = ctx->crop_bottom;
if (ctx->width > 0) {
if (ctx->width > sps->pic_width_in_luma_samples) {
av_log(bsf, AV_LOG_ERROR,
"The width option value %d is larger than picture width %d\n",
ctx->width, sps->pic_width_in_luma_samples);
return AVERROR(EINVAL);
}
if (left < 0) {
if (right > 0)
left = sps->pic_width_in_luma_samples - ctx->width - right;
else
left = 0;
}
if (right < 0)
right = sps->pic_width_in_luma_samples - ctx->width - left;
if (left < 0 || right < 0 || (left + right + ctx->width) !=
sps->pic_width_in_luma_samples) {
av_log(bsf, AV_LOG_ERROR,
"Invalid value for crop_left %d, crop_right %d, width after "
"crop %d, with picture width %d\n",
ctx->crop_left, ctx->crop_right, ctx->width,
sps->pic_width_in_luma_samples);
return AVERROR(EINVAL);
}
}
if (ctx->height > 0) {
if (ctx->height > sps->pic_height_in_luma_samples) {
av_log(bsf, AV_LOG_ERROR,
"The height option value %d is larger than picture height %d\n",
ctx->height, sps->pic_height_in_luma_samples);
return AVERROR(EINVAL);
}
if (top < 0) {
if (bottom > 0)
top = sps->pic_height_in_luma_samples - ctx->height - bottom;
else
top = 0;
}
if (bottom < 0)
bottom = sps->pic_height_in_luma_samples - ctx->height - top;
if (top < 0 || bottom < 0 || (top + bottom + ctx->height) !=
sps->pic_height_in_luma_samples) {
av_log(bsf, AV_LOG_ERROR,
"Invalid value for crop_top %d, crop_bottom %d, height after "
"crop %d, with picture height %d\n",
ctx->crop_top, ctx->crop_bottom, ctx->height,
sps->pic_height_in_luma_samples);
return AVERROR(EINVAL);
}
}
*crop_left = left;
*crop_right = right;
*crop_top = top;
*crop_bottom = bottom;
return 0;
}
static int h265_metadata_update_sps(AVBSFContext *bsf,
H265RawSPS *sps)
{
H265MetadataContext *ctx = bsf->priv_data;
int need_vui = 0;
int crop_unit_x, crop_unit_y;
/* Use local variables to avoid modifying context fields in case of video
* resolution changed. Crop doesn't work well with resolution change, this
* is the best we can do.
*/
int crop_left, crop_right, crop_top, crop_bottom;
int ret;
if (ctx->sample_aspect_ratio.num && ctx->sample_aspect_ratio.den) {
int num, den, i;
av_reduce(&num, &den, ctx->sample_aspect_ratio.num,
ctx->sample_aspect_ratio.den, 65535);
for (i = 1; i < FF_ARRAY_ELEMS(ff_h2645_pixel_aspect); i++) {
if (num == ff_h2645_pixel_aspect[i].num &&
den == ff_h2645_pixel_aspect[i].den)
break;
}
if (i == FF_ARRAY_ELEMS(ff_h2645_pixel_aspect)) {
sps->vui.aspect_ratio_idc = 255;
sps->vui.sar_width = num;
sps->vui.sar_height = den;
} else {
sps->vui.aspect_ratio_idc = i;
}
sps->vui.aspect_ratio_info_present_flag = 1;
need_vui = 1;
}
#define SET_OR_INFER(field, value, present_flag, infer) do { \
if (value >= 0) { \
field = value; \
need_vui = 1; \
} else if (!present_flag) \
field = infer; \
} while (0)
if (ctx->video_format >= 0 ||
ctx->video_full_range_flag >= 0 ||
ctx->colour_primaries >= 0 ||
ctx->transfer_characteristics >= 0 ||
ctx->matrix_coefficients >= 0) {
SET_OR_INFER(sps->vui.video_format, ctx->video_format,
sps->vui.video_signal_type_present_flag, 5);
SET_OR_INFER(sps->vui.video_full_range_flag,
ctx->video_full_range_flag,
sps->vui.video_signal_type_present_flag, 0);
if (ctx->colour_primaries >= 0 ||
ctx->transfer_characteristics >= 0 ||
ctx->matrix_coefficients >= 0) {
SET_OR_INFER(sps->vui.colour_primaries,
ctx->colour_primaries,
sps->vui.colour_description_present_flag, 2);
SET_OR_INFER(sps->vui.transfer_characteristics,
ctx->transfer_characteristics,
sps->vui.colour_description_present_flag, 2);
SET_OR_INFER(sps->vui.matrix_coefficients,
ctx->matrix_coefficients,
sps->vui.colour_description_present_flag, 2);
sps->vui.colour_description_present_flag = 1;
}
sps->vui.video_signal_type_present_flag = 1;
need_vui = 1;
}
if (ctx->chroma_sample_loc_type >= 0) {
sps->vui.chroma_sample_loc_type_top_field =
ctx->chroma_sample_loc_type;
sps->vui.chroma_sample_loc_type_bottom_field =
ctx->chroma_sample_loc_type;
sps->vui.chroma_loc_info_present_flag = 1;
need_vui = 1;
}
if (ctx->tick_rate.num && ctx->tick_rate.den) {
int num, den;
av_reduce(&num, &den, ctx->tick_rate.num, ctx->tick_rate.den,
UINT32_MAX > INT_MAX ? UINT32_MAX : INT_MAX);
sps->vui.vui_time_scale = num;
sps->vui.vui_num_units_in_tick = den;
sps->vui.vui_timing_info_present_flag = 1;
need_vui = 1;
if (ctx->num_ticks_poc_diff_one > 0) {
sps->vui.vui_num_ticks_poc_diff_one_minus1 =
ctx->num_ticks_poc_diff_one - 1;
sps->vui.vui_poc_proportional_to_timing_flag = 1;
} else if (ctx->num_ticks_poc_diff_one == 0) {
sps->vui.vui_poc_proportional_to_timing_flag = 0;
}
}
ret = h265_metadata_deduce_crop(bsf, sps, &crop_left, &crop_right,
&crop_top, &crop_bottom);
if (ret < 0)
return ret;
if (sps->separate_colour_plane_flag || sps->chroma_format_idc == 0) {
crop_unit_x = 1;
crop_unit_y = 1;
} else {
crop_unit_x = 1 + (sps->chroma_format_idc < 3);
crop_unit_y = 1 + (sps->chroma_format_idc < 2);
}
#define CROP(border, unit) do { \
if (crop_ ## border >= 0) { \
if (crop_ ## border % unit != 0) { \
av_log(bsf, AV_LOG_ERROR, "Invalid value for crop_%s: " \
"must be a multiple of %d.\n", #border, unit); \
return AVERROR(EINVAL); \
} \
sps->conf_win_ ## border ## _offset = \
crop_ ## border / unit; \
sps->conformance_window_flag = 1; \
} \
} while (0)
CROP(left, crop_unit_x);
CROP(right, crop_unit_x);
CROP(top, crop_unit_y);
CROP(bottom, crop_unit_y);
#undef CROP
if (need_vui)
sps->vui_parameters_present_flag = 1;
h265_metadata_update_level(bsf, &sps->profile_tier_level.general_level_idc);
return 0;
}
static int h265_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt,
CodedBitstreamFragment *au)
{
H265MetadataContext *ctx = bsf->priv_data;
int err, i;
// If an AUD is present, it must be the first NAL unit.
if (au->nb_units && au->units[0].type == HEVC_NAL_AUD) {
if (ctx->aud == BSF_ELEMENT_REMOVE)
ff_cbs_delete_unit(au, 0);
} else {
if (pkt && ctx->aud == BSF_ELEMENT_INSERT) {
H265RawAUD *aud = &ctx->aud_nal;
int pic_type = 0, temporal_id = 8, layer_id = 0;
for (i = 0; i < au->nb_units; i++) {
const H265RawNALUnitHeader *nal = au->units[i].content;
if (!nal)
continue;
if (nal->nuh_temporal_id_plus1 < temporal_id + 1)
temporal_id = nal->nuh_temporal_id_plus1 - 1;
if (au->units[i].type <= HEVC_NAL_RSV_VCL31) {
const H265RawSlice *slice = au->units[i].content;
layer_id = nal->nuh_layer_id;
if (slice->header.slice_type == HEVC_SLICE_B &&
pic_type < 2)
pic_type = 2;
if (slice->header.slice_type == HEVC_SLICE_P &&
pic_type < 1)
pic_type = 1;
}
}
aud->nal_unit_header = (H265RawNALUnitHeader) {
.nal_unit_type = HEVC_NAL_AUD,
.nuh_layer_id = layer_id,
.nuh_temporal_id_plus1 = temporal_id + 1,
};
aud->pic_type = pic_type;
err = ff_cbs_insert_unit_content(au, 0, HEVC_NAL_AUD, aud, NULL);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n");
return err;
}
}
}
if (ctx->level == LEVEL_AUTO && !ctx->level_guess)
h265_metadata_guess_level(bsf, au);
for (i = 0; i < au->nb_units; i++) {
if (au->units[i].type == HEVC_NAL_VPS) {
err = h265_metadata_update_vps(bsf, au->units[i].content);
if (err < 0)
return err;
}
if (au->units[i].type == HEVC_NAL_SPS) {
err = h265_metadata_update_sps(bsf, au->units[i].content);
if (err < 0)
return err;
}
}
return 0;
}
static const CBSBSFType h265_metadata_type = {
.codec_id = AV_CODEC_ID_HEVC,
.fragment_name = "access unit",
.unit_name = "NAL unit",
.update_fragment = &h265_metadata_update_fragment,
};
static int h265_metadata_init(AVBSFContext *bsf)
{
return ff_cbs_bsf_generic_init(bsf, &h265_metadata_type);
}
#define OFFSET(x) offsetof(H265MetadataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption h265_metadata_options[] = {
BSF_ELEMENT_OPTIONS_PIR("aud", "Access Unit Delimiter NAL units",
aud, FLAGS),
{ "sample_aspect_ratio", "Set sample aspect ratio (table E-1)",
OFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL,
{ .dbl = 0.0 }, 0, 65535, FLAGS },
{ "video_format", "Set video format (table E-2)",
OFFSET(video_format), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 7, FLAGS },
{ "video_full_range_flag", "Set video full range flag",
OFFSET(video_full_range_flag), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 1, FLAGS },
{ "colour_primaries", "Set colour primaries (table E-3)",
OFFSET(colour_primaries), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "transfer_characteristics", "Set transfer characteristics (table E-4)",
OFFSET(transfer_characteristics), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "matrix_coefficients", "Set matrix coefficients (table E-5)",
OFFSET(matrix_coefficients), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "chroma_sample_loc_type", "Set chroma sample location type (figure E-1)",
OFFSET(chroma_sample_loc_type), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 5, FLAGS },
{ "tick_rate",
"Set VPS and VUI tick rate (time_scale / num_units_in_tick)",
OFFSET(tick_rate), AV_OPT_TYPE_RATIONAL,
{ .dbl = 0.0 }, 0, UINT_MAX, FLAGS },
{ "num_ticks_poc_diff_one",
"Set VPS and VUI number of ticks per POC increment",
OFFSET(num_ticks_poc_diff_one), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, INT_MAX, FLAGS },
{ "crop_left", "Set left border crop offset",
OFFSET(crop_left), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, HEVC_MAX_WIDTH, FLAGS },
{ "crop_right", "Set right border crop offset",
OFFSET(crop_right), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, HEVC_MAX_WIDTH, FLAGS },
{ "crop_top", "Set top border crop offset",
OFFSET(crop_top), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, HEVC_MAX_HEIGHT, FLAGS },
{ "crop_bottom", "Set bottom border crop offset",
OFFSET(crop_bottom), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, HEVC_MAX_HEIGHT, FLAGS },
{ "width", "Set width after crop",
OFFSET(width), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, HEVC_MAX_WIDTH, FLAGS },
{ "height", "Set height after crop",
OFFSET(height), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, HEVC_MAX_HEIGHT, FLAGS },
{ "level", "Set level (tables A.6 and A.7)",
OFFSET(level), AV_OPT_TYPE_INT,
{ .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, .unit = "level" },
{ "auto", "Attempt to guess level from stream properties",
0, AV_OPT_TYPE_CONST,
{ .i64 = LEVEL_AUTO }, .flags = FLAGS, .unit = "level" },
#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, .flags = FLAGS, .unit = "level"
{ LEVEL("1", 30) },
{ LEVEL("2", 60) },
{ LEVEL("2.1", 63) },
{ LEVEL("3", 90) },
{ LEVEL("3.1", 93) },
{ LEVEL("4", 120) },
{ LEVEL("4.1", 123) },
{ LEVEL("5", 150) },
{ LEVEL("5.1", 153) },
{ LEVEL("5.2", 156) },
{ LEVEL("6", 180) },
{ LEVEL("6.1", 183) },
{ LEVEL("6.2", 186) },
{ LEVEL("8.5", 255) },
#undef LEVEL
{ NULL }
};
static const AVClass h265_metadata_class = {
.class_name = "h265_metadata_bsf",
.item_name = av_default_item_name,
.option = h265_metadata_options,
.version = LIBAVUTIL_VERSION_INT,
};
static const enum AVCodecID h265_metadata_codec_ids[] = {
AV_CODEC_ID_HEVC, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_hevc_metadata_bsf = {
.p.name = "hevc_metadata",
.p.codec_ids = h265_metadata_codec_ids,
.p.priv_class = &h265_metadata_class,
.priv_data_size = sizeof(H265MetadataContext),
.init = &h265_metadata_init,
.close = &ff_cbs_bsf_generic_close,
.filter = &ff_cbs_bsf_generic_filter,
};
+150
View File
@@ -0,0 +1,150 @@
/*
* 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 "libavutil/common.h"
#include "libavutil/opt.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_bsf.h"
#include "cbs_h266.h"
#include "vvc.h"
#define IS_H266_SLICE(nut) (nut <= VVC_RASL_NUT || (nut >= VVC_IDR_W_RADL && nut <= VVC_GDR_NUT))
typedef struct H266MetadataContext {
CBSBSFContext common;
H266RawAUD aud_nal;
int aud;
} H266MetadataContext;
static int h266_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt,
CodedBitstreamFragment *pu)
{
H266MetadataContext *ctx = bsf->priv_data;
int err, i;
// If an AUD is present, it must be the first NAL unit.
if (pu->nb_units && pu->units[0].type == VVC_AUD_NUT) {
if (ctx->aud == BSF_ELEMENT_REMOVE)
ff_cbs_delete_unit(pu, 0);
} else if ( pkt && ctx->aud == BSF_ELEMENT_INSERT) {
const H266RawSlice *first_slice = NULL;
const H266RawPictureHeader *ph = NULL;
H266RawAUD *aud = &ctx->aud_nal;
int pic_type = 0, temporal_id = 8, layer_id = 0;
for (i = 0; i < pu->nb_units; i++) {
const H266RawNALUnitHeader *nal = pu->units[i].content;
if (!nal)
continue;
if (nal->nuh_temporal_id_plus1 < temporal_id + 1)
temporal_id = nal->nuh_temporal_id_plus1 - 1;
if ( nal->nal_unit_type == VVC_PH_NUT ) {
const H266RawPH *header = pu->units[i].content;
ph = &header->ph_picture_header;
} else if (IS_H266_SLICE(nal->nal_unit_type)) {
const H266RawSlice *slice = pu->units[i].content;
layer_id = nal->nuh_layer_id;
if (slice->header.sh_slice_type == VVC_SLICE_TYPE_B &&
pic_type < 2)
pic_type = 2;
if (slice->header.sh_slice_type == VVC_SLICE_TYPE_P &&
pic_type < 1)
pic_type = 1;
if (!first_slice) {
first_slice = slice;
if (first_slice->header.
sh_picture_header_in_slice_header_flag)
ph = &first_slice->header.sh_picture_header;
else if (!ph)
break;
}
}
}
if (!ph) {
av_log(bsf, AV_LOG_ERROR, "no available picture header");
return AVERROR_INVALIDDATA;
}
aud->nal_unit_header = (H266RawNALUnitHeader) {
.nal_unit_type = VVC_AUD_NUT,
.nuh_layer_id = layer_id,
.nuh_temporal_id_plus1 = temporal_id + 1,
};
aud->aud_pic_type = pic_type;
aud->aud_irap_or_gdr_flag = ph->ph_gdr_or_irap_pic_flag;
err = ff_cbs_insert_unit_content(pu, 0, VVC_AUD_NUT, aud, NULL);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n");
return err;
}
}
/* TODO: implement more metadata parsing, like VUI, Levels etc. */
//for (i = 0; i < pu->nb_units; i++) {
// if (pu->units[i].type == VVC_SPS_NUT) {
// }
//}
return 0;
}
static const CBSBSFType h266_metadata_type = {
.codec_id = AV_CODEC_ID_VVC,
.fragment_name = "access unit",
.unit_name = "NAL unit",
.update_fragment = &h266_metadata_update_fragment,
};
static int h266_metadata_init(AVBSFContext *bsf)
{
return ff_cbs_bsf_generic_init(bsf, &h266_metadata_type);
}
#define OFFSET(x) offsetof(H266MetadataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption h266_metadata_options[] = {
BSF_ELEMENT_OPTIONS_PIR("aud", "Access Unit Delimiter NAL units",
aud, FLAGS),
{ NULL }
};
static const AVClass h266_metadata_class = {
.class_name = "h266_metadata_bsf",
.item_name = av_default_item_name,
.option = h266_metadata_options,
.version = LIBAVUTIL_VERSION_INT,
};
static const enum AVCodecID h266_metadata_codec_ids[] = {
AV_CODEC_ID_VVC, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_vvc_metadata_bsf = {
.p.name = "vvc_metadata",
.p.codec_ids = h266_metadata_codec_ids,
.p.priv_class = &h266_metadata_class,
.priv_data_size = sizeof(H266MetadataContext),
.init = &h266_metadata_init,
.close = &ff_cbs_bsf_generic_close,
.filter = &ff_cbs_bsf_generic_filter,
};
+136
View File
@@ -0,0 +1,136 @@
/*
* HAPQA extract bitstream filter
* Copyright (c) 2017 Jokyo Images
*
* 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
*/
/**
* @file
* HAPQA extract bitstream filter
* extract one of the two textures of the HAQA
*/
#include "bsf.h"
#include "bsf_internal.h"
#include "bytestream.h"
#include "hap.h"
#include "libavutil/opt.h"
typedef struct HapqaExtractContext {
const AVClass *class;
int texture;/* index of the texture to keep (0 for rgb or 1 for alpha) */
} HapqaExtractContext;
static int check_texture(HapqaExtractContext *ctx, int section_type) {
if (((ctx->texture == 0)&&((section_type & 0x0F) == 0x0F)) || /* HapQ texture and rgb extract */
((ctx->texture == 1)&&((section_type & 0x0F) == 0x01))) /* HapAlphaOnly texture and alpha extract */
{
return 1; /* the texture is the one to keep */
} else {
return 0;
}
}
static int hapqa_extract(AVBSFContext *bsf, AVPacket *pkt)
{
HapqaExtractContext *ctx = bsf->priv_data;
GetByteContext gbc;
int section_size;
enum HapSectionType section_type;
int start_section_size;
int target_packet_size = 0;
int ret = 0;
ret = ff_bsf_get_packet_ref(bsf, pkt);
if (ret < 0)
return ret;
bytestream2_init(&gbc, pkt->data, pkt->size);
ret = ff_hap_parse_section_header(&gbc, &section_size, &section_type);
if (ret != 0)
goto fail;
if ((section_type & 0x0F) != 0x0D) {
av_log(bsf, AV_LOG_ERROR, "Invalid section type for HAPQA %#04x.\n", section_type & 0x0F);
ret = AVERROR_INVALIDDATA;
goto fail;
}
start_section_size = 4;
bytestream2_seek(&gbc, start_section_size, SEEK_SET);/* go to start of the first texture */
ret = ff_hap_parse_section_header(&gbc, &section_size, &section_type);
if (ret != 0)
goto fail;
target_packet_size = section_size + 4;
if (check_texture(ctx, section_type) == 0) { /* the texture is not the one to keep */
start_section_size += 4 + section_size;
bytestream2_seek(&gbc, start_section_size, SEEK_SET);/* go to start of the second texture */
ret = ff_hap_parse_section_header(&gbc, &section_size, &section_type);
if (ret != 0)
goto fail;
target_packet_size = section_size + 4;
if (check_texture(ctx, section_type) == 0){ /* the second texture is not the one to keep */
av_log(bsf, AV_LOG_ERROR, "No valid texture found.\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
}
pkt->data += start_section_size;
pkt->size = target_packet_size;
fail:
if (ret < 0)
av_packet_unref(pkt);
return ret;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_HAP, AV_CODEC_ID_NONE,
};
#define OFFSET(x) offsetof(HapqaExtractContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_BSF_PARAM)
static const AVOption options[] = {
{ "texture", "texture to keep", OFFSET(texture), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, .unit = "texture" },
{ "color", "keep HapQ texture", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, .unit = "texture" },
{ "alpha", "keep HapAlphaOnly texture", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, .unit = "texture" },
{ NULL },
};
static const AVClass hapqa_extract_class = {
.class_name = "hapqa_extract_bsf",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFBitStreamFilter ff_hapqa_extract_bsf = {
.p.name = "hapqa_extract",
.p.codec_ids = codec_ids,
.p.priv_class = &hapqa_extract_class,
.priv_data_size = sizeof(HapqaExtractContext),
.filter = hapqa_extract,
};
+238
View File
@@ -0,0 +1,238 @@
/*
* HEVC MP4 to Annex B byte stream format filter
* copyright (c) 2015 Anton Khirnov
*
* 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 <string.h>
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "bytestream.h"
#include "defs.h"
#include "hevc/hevc.h"
#define MIN_HEVCC_LENGTH 23
typedef struct HEVCBSFContext {
uint8_t length_size;
int extradata_parsed;
} HEVCBSFContext;
static int hevc_extradata_to_annexb(AVBSFContext *ctx)
{
GetByteContext gb;
int length_size, num_arrays, i, j;
int ret = 0;
uint8_t *new_extradata = NULL;
size_t new_extradata_size = 0;
bytestream2_init(&gb, ctx->par_in->extradata, ctx->par_in->extradata_size);
bytestream2_skip(&gb, 21);
length_size = (bytestream2_get_byte(&gb) & 3) + 1;
num_arrays = bytestream2_get_byte(&gb);
for (i = 0; i < num_arrays; i++) {
int type = bytestream2_get_byte(&gb) & 0x3f;
int cnt = bytestream2_get_be16(&gb);
if (!(type == HEVC_NAL_VPS || type == HEVC_NAL_SPS || type == HEVC_NAL_PPS ||
type == HEVC_NAL_SEI_PREFIX || type == HEVC_NAL_SEI_SUFFIX)) {
av_log(ctx, AV_LOG_ERROR, "Invalid NAL unit type in extradata: %d\n",
type);
ret = AVERROR_INVALIDDATA;
goto fail;
}
for (j = 0; j < cnt; j++) {
const int nalu_len = bytestream2_get_be16(&gb);
if (!nalu_len ||
nalu_len > bytestream2_get_bytes_left(&gb) ||
4 + AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > SIZE_MAX - new_extradata_size) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
ret = av_reallocp(&new_extradata, new_extradata_size + nalu_len + 4 + AV_INPUT_BUFFER_PADDING_SIZE);
if (ret < 0)
goto fail;
AV_WB32(new_extradata + new_extradata_size, 1); // add the startcode
bytestream2_get_buffer(&gb, new_extradata + new_extradata_size + 4, nalu_len);
new_extradata_size += 4 + nalu_len;
memset(new_extradata + new_extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
}
av_freep(&ctx->par_out->extradata);
ctx->par_out->extradata = new_extradata;
ctx->par_out->extradata_size = new_extradata_size;
if (!new_extradata_size)
av_log(ctx, AV_LOG_WARNING, "No parameter sets in the extradata\n");
return length_size;
fail:
av_freep(&new_extradata);
return ret;
}
static int hevc_mp4toannexb_init(AVBSFContext *ctx)
{
HEVCBSFContext *s = ctx->priv_data;
int ret;
if (ctx->par_in->extradata_size < MIN_HEVCC_LENGTH ||
AV_RB24(ctx->par_in->extradata) == 1 ||
AV_RB32(ctx->par_in->extradata) == 1) {
av_log(ctx, AV_LOG_VERBOSE,
"The input looks like it is Annex B already\n");
} else {
ret = hevc_extradata_to_annexb(ctx);
if (ret < 0)
return ret;
s->length_size = ret;
s->extradata_parsed = 1;
}
return 0;
}
static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
{
HEVCBSFContext *s = ctx->priv_data;
AVPacket *in;
GetByteContext gb;
int got_irap = 0;
int got_ps = 0, seen_irap_ps = 0;
int i, ret = 0;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
if (!s->extradata_parsed) {
av_packet_move_ref(out, in);
av_packet_free(&in);
return 0;
}
bytestream2_init(&gb, in->data, in->size);
while (!got_irap && bytestream2_get_bytes_left(&gb)) {
uint32_t nalu_size = 0;
int nalu_type;
if (bytestream2_get_bytes_left(&gb) < s->length_size) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
for (i = 0; i < s->length_size; i++)
nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;
bytestream2_skip(&gb, nalu_size);
got_irap |= nalu_type >= HEVC_NAL_BLA_W_LP &&
nalu_type <= HEVC_NAL_RSV_IRAP_VCL23;
got_ps |= nalu_type >= HEVC_NAL_VPS && nalu_type <= HEVC_NAL_PPS;
}
seen_irap_ps = got_irap && got_ps;
got_irap = got_ps = 0;
bytestream2_init(&gb, in->data, in->size);
while (bytestream2_get_bytes_left(&gb)) {
uint32_t nalu_size = 0;
int nalu_type;
int is_irap, is_ps, add_extradata, extra_size, prev_size;
if (bytestream2_get_bytes_left(&gb) < s->length_size) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
for (i = 0; i < s->length_size; i++)
nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f;
/* prepend extradata to IRAP frames */
is_irap = nalu_type >= HEVC_NAL_BLA_W_LP &&
nalu_type <= HEVC_NAL_RSV_IRAP_VCL23;
is_ps = nalu_type >= HEVC_NAL_VPS && nalu_type <= HEVC_NAL_PPS && seen_irap_ps;
add_extradata = (is_ps || is_irap) && !got_ps && !got_irap;
extra_size = add_extradata * ctx->par_out->extradata_size;
got_irap |= is_irap;
got_ps |= is_ps;
if (FFMIN(INT_MAX, SIZE_MAX) < 4ULL + nalu_size + extra_size) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
prev_size = out->size;
ret = av_grow_packet(out, 4 + nalu_size + extra_size);
if (ret < 0)
goto fail;
if (extra_size)
memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size);
AV_WB32(out->data + prev_size + extra_size, 1);
bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size);
}
ret = av_packet_copy_props(out, in);
if (ret < 0)
goto fail;
fail:
if (ret < 0)
av_packet_unref(out);
av_packet_free(&in);
return ret;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_HEVC, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_hevc_mp4toannexb_bsf = {
.p.name = "hevc_mp4toannexb",
.p.codec_ids = codec_ids,
.priv_data_size = sizeof(HEVCBSFContext),
.init = hevc_mp4toannexb_init,
.filter = hevc_mp4toannexb_filter,
};
+76
View File
@@ -0,0 +1,76 @@
/*
* imx dump header bitstream filter
* Copyright (c) 2007 Baptiste Coudurier
*
* 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
*/
/**
* @file
* imx dump header bitstream filter
* modifies bitstream to fit in mov and be decoded by final cut pro decoder
*/
#include "bsf.h"
#include "bsf_internal.h"
#include "bytestream.h"
static int imx_dump_header(AVBSFContext *ctx, AVPacket *out)
{
/* MXF essence element key */
static const uint8_t imx_header[16] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x05,0x01,0x01,0x00 };
AVPacket *in;
int ret = 0;
uint8_t *out_buf;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
ret = av_new_packet(out, in->size + 20);
if (ret < 0)
goto fail;
out_buf = out->data;
bytestream_put_buffer(&out_buf, imx_header, 16);
bytestream_put_byte(&out_buf, 0x83); /* KLV BER long form */
bytestream_put_be24(&out_buf, in->size);
bytestream_put_buffer(&out_buf, in->data, in->size);
ret = av_packet_copy_props(out, in);
if (ret < 0)
goto fail;
fail:
if (ret < 0)
av_packet_unref(out);
av_packet_free(&in);
return ret;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_MPEG2VIDEO, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_imx_dump_header_bsf = {
.p.name = "imxdump",
.p.codec_ids = codec_ids,
.filter = imx_dump_header,
};
+169
View File
@@ -0,0 +1,169 @@
/*
* Media 100 to MJPEGB bitstream filter
* Copyright (c) 2023 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
*/
/**
* @file
* Media 100 to MJPEGB bitstream filter.
*/
#include "libavutil/intreadwrite.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "bytestream.h"
static av_cold int init(AVBSFContext *ctx)
{
ctx->par_out->codec_id = AV_CODEC_ID_MJPEGB;
return 0;
}
static int filter(AVBSFContext *ctx, AVPacket *out)
{
unsigned second_field_offset = 0;
unsigned next_field = 0;
unsigned dht_offset[2];
unsigned dqt_offset[2];
unsigned sod_offset[2];
unsigned sof_offset[2];
unsigned sos_offset[2];
unsigned field = 0;
GetByteContext gb;
PutByteContext pb;
AVPacket *in;
int ret;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
ret = av_new_packet(out, in->size + 1024);
if (ret < 0)
goto fail;
bytestream2_init(&gb, in->data, in->size);
bytestream2_init_writer(&pb, out->data, out->size);
second_field:
bytestream2_put_be32(&pb, 0);
bytestream2_put_be32(&pb, AV_RB32("mjpg"));
bytestream2_put_be32(&pb, 0);
bytestream2_put_be32(&pb, 0);
for (int i = 0; i < 6; i++)
bytestream2_put_be32(&pb, 0);
sof_offset[field] = bytestream2_tell_p(&pb);
bytestream2_put_be16(&pb, 17);
bytestream2_put_byte(&pb, 8);
bytestream2_put_be16(&pb, ctx->par_in->height / 2);
bytestream2_put_be16(&pb, ctx->par_in->width);
bytestream2_put_byte(&pb, 3);
bytestream2_put_byte(&pb, 1);
bytestream2_put_byte(&pb, 0x21);
bytestream2_put_byte(&pb, 0);
bytestream2_put_byte(&pb, 2);
bytestream2_put_byte(&pb, 0x11);
bytestream2_put_byte(&pb, 1);
bytestream2_put_byte(&pb, 3);
bytestream2_put_byte(&pb, 0x11);
bytestream2_put_byte(&pb, 1);
sos_offset[field] = bytestream2_tell_p(&pb);
bytestream2_put_be16(&pb, 12);
bytestream2_put_byte(&pb, 3);
bytestream2_put_byte(&pb, 1);
bytestream2_put_byte(&pb, 0);
bytestream2_put_byte(&pb, 2);
bytestream2_put_byte(&pb, 0x11);
bytestream2_put_byte(&pb, 3);
bytestream2_put_byte(&pb, 0x11);
bytestream2_put_byte(&pb, 0);
bytestream2_put_byte(&pb, 0);
bytestream2_put_byte(&pb, 0);
dqt_offset[field] = bytestream2_tell_p(&pb);
bytestream2_put_be16(&pb, 132);
bytestream2_put_byte(&pb, 0);
bytestream2_skip(&gb, 4);
for (int i = 0; i < 64; i++)
bytestream2_put_byte(&pb, bytestream2_get_be32(&gb));
bytestream2_put_byte(&pb, 1);
for (int i = 0; i < 64; i++)
bytestream2_put_byte(&pb, bytestream2_get_be32(&gb));
dht_offset[field] = 0;
sod_offset[field] = bytestream2_tell_p(&pb);
for (int i = bytestream2_tell(&gb) + 8; next_field == 0 && i < in->size - 4; i++) {
if (AV_RB32(in->data + i) == 0x00000001) {
next_field = i;
break;
}
}
bytestream2_skip(&gb, 8);
bytestream2_copy_buffer(&pb, &gb, next_field - bytestream2_tell(&gb));
bytestream2_put_be64(&pb, 0);
if (field == 0) {
field = 1;
second_field_offset = bytestream2_tell_p(&pb);
next_field = in->size;
goto second_field;
}
AV_WB32(out->data + 8, second_field_offset);
AV_WB32(out->data + 12, second_field_offset);
AV_WB32(out->data + 16, second_field_offset);
AV_WB32(out->data + 20, dqt_offset[0]);
AV_WB32(out->data + 24, dht_offset[0]);
AV_WB32(out->data + 28, sof_offset[0]);
AV_WB32(out->data + 32, sos_offset[0]);
AV_WB32(out->data + 36, sod_offset[0]);
AV_WB32(out->data + second_field_offset + 8, bytestream2_tell_p(&pb) - second_field_offset);
AV_WB32(out->data + second_field_offset + 12, bytestream2_tell_p(&pb) - second_field_offset);
AV_WB32(out->data + second_field_offset + 16, 0);
AV_WB32(out->data + second_field_offset + 20, dqt_offset[1] - second_field_offset);
AV_WB32(out->data + second_field_offset + 24, dht_offset[1]);
AV_WB32(out->data + second_field_offset + 28, sof_offset[1] - second_field_offset);
AV_WB32(out->data + second_field_offset + 32, sos_offset[1] - second_field_offset);
AV_WB32(out->data + second_field_offset + 36, sod_offset[1] - second_field_offset);
out->size = bytestream2_tell_p(&pb);
memset(out->data + out->size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
ret = av_packet_copy_props(out, in);
if (ret < 0)
goto fail;
fail:
if (ret < 0)
av_packet_unref(out);
av_packet_free(&in);
return ret;
}
const FFBitStreamFilter ff_media100_to_mjpegb_bsf = {
.p.name = "media100_to_mjpegb",
.p.codec_ids = (const enum AVCodecID []){ AV_CODEC_ID_MEDIA100, AV_CODEC_ID_NONE },
.init = init,
.filter = filter,
};
+142
View File
@@ -0,0 +1,142 @@
/*
* MJPEG/AVI1 to JPEG/JFIF bitstream format filter
* Copyright (c) 2010 Adrian Daerr and Nicolas George
*
* 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
*/
/*
* Adapted from mjpeg2jpeg.c, with original copyright:
* Paris 2010 Adrian Daerr, public domain
*/
#include <string.h>
#include "libavutil/error.h"
#include "libavutil/intreadwrite.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "jpegtables.h"
#include "mjpeg.h"
static const uint8_t jpeg_header[] = {
0xff, 0xd8, // SOI
0xff, 0xe0, // APP0
0x00, 0x10, // APP0 header size (including
// this field, but excluding preceding)
0x4a, 0x46, 0x49, 0x46, 0x00, // ID string 'JFIF\0'
0x01, 0x01, // version
0x00, // bits per type
0x00, 0x00, // X density
0x00, 0x00, // Y density
0x00, // X thumbnail size
0x00, // Y thumbnail size
};
static const int dht_segment_size = 420;
static const uint8_t dht_segment_head[] = { 0xFF, 0xC4, 0x01, 0xA2, 0x00 };
static const uint8_t dht_segment_frag[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static uint8_t *append(uint8_t *buf, const uint8_t *src, int size)
{
memcpy(buf, src, size);
return buf + size;
}
static uint8_t *append_dht_segment(uint8_t *buf)
{
buf = append(buf, dht_segment_head, sizeof(dht_segment_head));
buf = append(buf, ff_mjpeg_bits_dc_luminance + 1, 16);
buf = append(buf, dht_segment_frag, sizeof(dht_segment_frag));
buf = append(buf, ff_mjpeg_val_dc, 12);
*(buf++) = 0x10;
buf = append(buf, ff_mjpeg_bits_ac_luminance + 1, 16);
buf = append(buf, ff_mjpeg_val_ac_luminance, 162);
*(buf++) = 0x11;
buf = append(buf, ff_mjpeg_bits_ac_chrominance + 1, 16);
buf = append(buf, ff_mjpeg_val_ac_chrominance, 162);
return buf;
}
static int mjpeg2jpeg_filter(AVBSFContext *ctx, AVPacket *out)
{
AVPacket *in;
int ret = 0;
int input_skip, output_size;
uint8_t *output;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
if (in->size < 12) {
av_log(ctx, AV_LOG_ERROR, "input is truncated\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
if (AV_RB16(in->data) != 0xffd8) {
av_log(ctx, AV_LOG_ERROR, "input is not MJPEG\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
if (in->data[2] == 0xff && in->data[3] == APP0) {
input_skip = (in->data[4] << 8) + in->data[5] + 4;
} else {
input_skip = 2;
}
if (in->size < input_skip) {
av_log(ctx, AV_LOG_ERROR, "input is truncated\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
output_size = in->size - input_skip +
sizeof(jpeg_header) + dht_segment_size;
ret = av_new_packet(out, output_size);
if (ret < 0)
goto fail;
output = out->data;
output = append(output, jpeg_header, sizeof(jpeg_header));
output = append_dht_segment(output);
output = append(output, in->data + input_skip, in->size - input_skip);
ret = av_packet_copy_props(out, in);
if (ret < 0)
goto fail;
fail:
if (ret < 0)
av_packet_unref(out);
av_packet_free(&in);
return ret;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_MJPEG, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_mjpeg2jpeg_bsf = {
.p.name = "mjpeg2jpeg",
.p.codec_ids = codec_ids,
.filter = mjpeg2jpeg_filter,
};
+108
View File
@@ -0,0 +1,108 @@
/*
* MJPEG A dump header bitstream filter
* Copyright (c) 2006 Baptiste Coudurier
*
* 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
*/
/**
* @file
* MJPEG A dump header bitstream filter
* modifies bitstream to be decoded by quicktime
*/
#include "bsf.h"
#include "bsf_internal.h"
#include "bytestream.h"
#include "mjpeg.h"
static int mjpega_dump_header(AVBSFContext *ctx, AVPacket *out)
{
AVPacket *in;
uint8_t *out_buf;
unsigned dqt = 0, dht = 0, sof0 = 0;
int ret = 0, i;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
ret = av_new_packet(out, in->size + 44);
if (ret < 0)
goto fail;
ret = av_packet_copy_props(out, in);
if (ret < 0)
goto fail;
out_buf = out->data;
bytestream_put_byte(&out_buf, 0xff);
bytestream_put_byte(&out_buf, SOI);
bytestream_put_byte(&out_buf, 0xff);
bytestream_put_byte(&out_buf, APP1);
bytestream_put_be16(&out_buf, 42); /* size */
bytestream_put_be32(&out_buf, 0);
bytestream_put_buffer(&out_buf, "mjpg", 4);
bytestream_put_be32(&out_buf, in->size + 44); /* field size */
bytestream_put_be32(&out_buf, in->size + 44); /* pad field size */
bytestream_put_be32(&out_buf, 0); /* next ptr */
for (i = 0; i < in->size - 1; i++) {
if (in->data[i] == 0xff) {
switch (in->data[i + 1]) {
case DQT: dqt = i + 46; break;
case DHT: dht = i + 46; break;
case SOF0: sof0 = i + 46; break;
case SOS:
bytestream_put_be32(&out_buf, dqt); /* quant off */
bytestream_put_be32(&out_buf, dht); /* huff off */
bytestream_put_be32(&out_buf, sof0); /* image off */
bytestream_put_be32(&out_buf, i + 46); /* scan off */
bytestream_put_be32(&out_buf, i + 46 + AV_RB16(in->data + i + 2)); /* data off */
bytestream_put_buffer(&out_buf, in->data + 2, in->size - 2); /* skip already written SOI */
out->size = out_buf - out->data;
av_packet_free(&in);
return 0;
case APP1:
if (i + 8 < in->size && AV_RL32(in->data + i + 8) == AV_RL32("mjpg")) {
av_log(ctx, AV_LOG_ERROR, "bitstream already formatted\n");
av_packet_unref(out);
av_packet_move_ref(out, in);
av_packet_free(&in);
return 0;
}
}
}
}
av_log(ctx, AV_LOG_ERROR, "could not find SOS marker in bitstream\n");
fail:
av_packet_unref(out);
av_packet_free(&in);
return AVERROR_INVALIDDATA;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_MJPEG, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_mjpega_dump_header_bsf = {
.p.name = "mjpegadump",
.p.codec_ids = codec_ids,
.filter = mjpega_dump_header,
};
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2008 Reimar Döffinger
*
* 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 "libavutil/common.h"
#include "libavutil/intreadwrite.h"
#include "bsf.h"
#include "bsf_internal.h"
static int text2movsub(AVBSFContext *ctx, AVPacket *out)
{
AVPacket *in;
int ret = 0;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
if (in->size > 0xffff) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
ret = av_new_packet(out, in->size + 2);
if (ret < 0) {
ret = AVERROR(ENOMEM);
goto fail;
}
ret = av_packet_copy_props(out, in);
if (ret < 0)
goto fail;
AV_WB16(out->data, in->size);
memcpy(out->data + 2, in->data, in->size);
fail:
if (ret < 0)
av_packet_unref(out);
av_packet_free(&in);
return ret;
}
const FFBitStreamFilter ff_text2movsub_bsf = {
.p.name = "text2movsub",
.filter = text2movsub,
};
static int mov2textsub(AVBSFContext *ctx, AVPacket *pkt)
{
int ret = 0;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0)
return ret;
if (pkt->size < 2) {
av_packet_unref(pkt);
return AVERROR_INVALIDDATA;
}
pkt->size = FFMIN(pkt->size - 2, AV_RB16(pkt->data));
pkt->data += 2;
return 0;
}
const FFBitStreamFilter ff_mov2textsub_bsf = {
.p.name = "mov2textsub",
.filter = mov2textsub,
};
+243
View File
@@ -0,0 +1,243 @@
/*
* 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 "libavutil/common.h"
#include "libavutil/opt.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_bsf.h"
#include "cbs_mpeg2.h"
#include "mpeg12.h"
typedef struct MPEG2MetadataContext {
CBSBSFContext common;
MPEG2RawExtensionData sequence_display_extension;
AVRational display_aspect_ratio;
AVRational frame_rate;
int video_format;
int colour_primaries;
int transfer_characteristics;
int matrix_coefficients;
int mpeg1_warned;
} MPEG2MetadataContext;
static int mpeg2_metadata_update_fragment(AVBSFContext *bsf,
AVPacket *pkt,
CodedBitstreamFragment *frag)
{
MPEG2MetadataContext *ctx = bsf->priv_data;
MPEG2RawSequenceHeader *sh = NULL;
MPEG2RawSequenceExtension *se = NULL;
MPEG2RawSequenceDisplayExtension *sde = NULL;
int i, se_pos;
for (i = 0; i < frag->nb_units; i++) {
if (frag->units[i].type == MPEG2_START_SEQUENCE_HEADER) {
sh = frag->units[i].content;
} else if (frag->units[i].type == MPEG2_START_EXTENSION) {
MPEG2RawExtensionData *ext = frag->units[i].content;
if (ext->extension_start_code_identifier ==
MPEG2_EXTENSION_SEQUENCE) {
se = &ext->data.sequence;
se_pos = i;
} else if (ext->extension_start_code_identifier ==
MPEG2_EXTENSION_SEQUENCE_DISPLAY) {
sde = &ext->data.sequence_display;
}
}
}
if (!sh || !se) {
// No sequence header and sequence extension: not an MPEG-2 video
// sequence.
if (sh && !ctx->mpeg1_warned) {
av_log(bsf, AV_LOG_WARNING, "Stream contains a sequence "
"header but not a sequence extension: maybe it's "
"actually MPEG-1?\n");
ctx->mpeg1_warned = 1;
}
return 0;
}
if (ctx->display_aspect_ratio.num && ctx->display_aspect_ratio.den) {
int num, den;
av_reduce(&num, &den, ctx->display_aspect_ratio.num,
ctx->display_aspect_ratio.den, 65535);
if (num == 4 && den == 3)
sh->aspect_ratio_information = 2;
else if (num == 16 && den == 9)
sh->aspect_ratio_information = 3;
else if (num == 221 && den == 100)
sh->aspect_ratio_information = 4;
else
sh->aspect_ratio_information = 1;
}
if (ctx->frame_rate.num && ctx->frame_rate.den) {
int code, ext_n, ext_d;
ff_mpeg12_find_best_frame_rate(ctx->frame_rate,
&code, &ext_n, &ext_d, 0);
sh->frame_rate_code = code;
se->frame_rate_extension_n = ext_n;
se->frame_rate_extension_d = ext_d;
}
if (ctx->video_format >= 0 ||
ctx->colour_primaries >= 0 ||
ctx->transfer_characteristics >= 0 ||
ctx->matrix_coefficients >= 0) {
if (!sde) {
int err;
ctx->sequence_display_extension.extension_start_code =
MPEG2_START_EXTENSION;
ctx->sequence_display_extension.extension_start_code_identifier =
MPEG2_EXTENSION_SEQUENCE_DISPLAY;
sde = &ctx->sequence_display_extension.data.sequence_display;
*sde = (MPEG2RawSequenceDisplayExtension) {
.video_format = 5,
.colour_description = 0,
.colour_primaries = 2,
.transfer_characteristics = 2,
.matrix_coefficients = 2,
.display_horizontal_size =
se->horizontal_size_extension << 12 | sh->horizontal_size_value,
.display_vertical_size =
se->vertical_size_extension << 12 | sh->vertical_size_value,
};
err = ff_cbs_insert_unit_content(frag, se_pos + 1,
MPEG2_START_EXTENSION,
&ctx->sequence_display_extension,
NULL);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to insert new sequence "
"display extension.\n");
return err;
}
}
if (ctx->video_format >= 0)
sde->video_format = ctx->video_format;
if (ctx->colour_primaries >= 0 ||
ctx->transfer_characteristics >= 0 ||
ctx->matrix_coefficients >= 0) {
sde->colour_description = 1;
if (ctx->colour_primaries >= 0)
sde->colour_primaries = ctx->colour_primaries;
if (ctx->transfer_characteristics >= 0)
sde->transfer_characteristics = ctx->transfer_characteristics;
if (ctx->matrix_coefficients >= 0)
sde->matrix_coefficients = ctx->matrix_coefficients;
}
}
return 0;
}
static const CBSBSFType mpeg2_metadata_type = {
.codec_id = AV_CODEC_ID_MPEG2VIDEO,
.fragment_name = "frame",
.unit_name = "start code",
.update_fragment = &mpeg2_metadata_update_fragment,
};
static int mpeg2_metadata_init(AVBSFContext *bsf)
{
MPEG2MetadataContext *ctx = bsf->priv_data;
#define VALIDITY_CHECK(name) do { \
if (!ctx->name) { \
av_log(bsf, AV_LOG_ERROR, "The value 0 for %s is " \
"forbidden.\n", #name); \
return AVERROR(EINVAL); \
} \
} while (0)
VALIDITY_CHECK(colour_primaries);
VALIDITY_CHECK(transfer_characteristics);
VALIDITY_CHECK(matrix_coefficients);
#undef VALIDITY_CHECK
return ff_cbs_bsf_generic_init(bsf, &mpeg2_metadata_type);
}
#define OFFSET(x) offsetof(MPEG2MetadataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption mpeg2_metadata_options[] = {
{ "display_aspect_ratio", "Set display aspect ratio (table 6-3)",
OFFSET(display_aspect_ratio), AV_OPT_TYPE_RATIONAL,
{ .dbl = 0.0 }, 0, 65535, FLAGS },
{ "frame_rate", "Set frame rate",
OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL,
{ .dbl = 0.0 }, 0, UINT_MAX, FLAGS },
{ "video_format", "Set video format (table 6-6)",
OFFSET(video_format), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 7, FLAGS },
{ "colour_primaries", "Set colour primaries (table 6-7)",
OFFSET(colour_primaries), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "transfer_characteristics", "Set transfer characteristics (table 6-8)",
OFFSET(transfer_characteristics), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ "matrix_coefficients", "Set matrix coefficients (table 6-9)",
OFFSET(matrix_coefficients), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 255, FLAGS },
{ NULL }
};
static const AVClass mpeg2_metadata_class = {
.class_name = "mpeg2_metadata_bsf",
.item_name = av_default_item_name,
.option = mpeg2_metadata_options,
.version = LIBAVUTIL_VERSION_INT,
};
static const enum AVCodecID mpeg2_metadata_codec_ids[] = {
AV_CODEC_ID_MPEG2VIDEO, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_mpeg2_metadata_bsf = {
.p.name = "mpeg2_metadata",
.p.codec_ids = mpeg2_metadata_codec_ids,
.p.priv_class = &mpeg2_metadata_class,
.priv_data_size = sizeof(MPEG2MetadataContext),
.init = &mpeg2_metadata_init,
.close = &ff_cbs_bsf_generic_close,
.filter = &ff_cbs_bsf_generic_filter,
};
@@ -0,0 +1,166 @@
/*
* Bitstream filter for unpacking DivX-style packed B-frames in MPEG-4 (divx_packed)
* Copyright (c) 2015 Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
*
* 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 "bsf.h"
#include "bsf_internal.h"
#include "mpeg4videodefs.h"
#include "startcode.h"
typedef struct UnpackBFramesBSFContext {
AVBufferRef *b_frame_ref;
} UnpackBFramesBSFContext;
/* determine the position of the packed marker in the userdata,
* the number of VOPs and the position of the second VOP */
static void scan_buffer(const uint8_t *buf, int buf_size,
int *pos_p, int *nb_vop, int *pos_vop2) {
uint32_t startcode;
const uint8_t *end = buf + buf_size, *pos = buf;
while (pos < end) {
startcode = -1;
pos = avpriv_find_start_code(pos, end, &startcode);
if (startcode == USER_DATA_STARTCODE && pos_p) {
/* check if the (DivX) userdata string ends with 'p' (packed) */
for (int i = 0; i < 255 && pos + i + 1 < end; i++) {
if (pos[i] == 'p' && pos[i + 1] == '\0') {
*pos_p = pos + i - buf;
break;
}
}
} else if (startcode == VOP_STARTCODE && nb_vop) {
*nb_vop += 1;
if (*nb_vop == 2 && pos_vop2) {
*pos_vop2 = pos - buf - 4; /* subtract 4 bytes startcode */
}
}
}
}
static int mpeg4_unpack_bframes_filter(AVBSFContext *ctx, AVPacket *pkt)
{
UnpackBFramesBSFContext *s = ctx->priv_data;
int pos_p = -1, nb_vop = 0, pos_vop2 = -1, ret = 0;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0)
return ret;
scan_buffer(pkt->data, pkt->size, &pos_p, &nb_vop, &pos_vop2);
av_log(ctx, AV_LOG_DEBUG, "Found %d VOP startcode(s) in this packet.\n", nb_vop);
if (pos_vop2 >= 0) {
if (s->b_frame_ref) {
av_log(ctx, AV_LOG_WARNING,
"Missing one N-VOP packet, discarding one B-frame.\n");
av_buffer_unref(&s->b_frame_ref);
}
/* store a reference to the packed B-frame's data in the BSFContext */
s->b_frame_ref = av_buffer_ref(pkt->buf);
if (!s->b_frame_ref) {
ret = AVERROR(ENOMEM);
goto fail;
}
s->b_frame_ref->data = pkt->data + pos_vop2;
s->b_frame_ref->size = pkt->size - pos_vop2;
}
if (nb_vop > 2) {
av_log(ctx, AV_LOG_WARNING,
"Found %d VOP headers in one packet, only unpacking one.\n", nb_vop);
}
if (nb_vop == 1 && s->b_frame_ref) {
AVBufferRef *tmp = pkt->buf;
/* make tmp accurately reflect the packet's data */
tmp->data = pkt->data;
tmp->size = pkt->size;
/* replace data in packet with stored data */
pkt->buf = s->b_frame_ref;
pkt->data = s->b_frame_ref->data;
pkt->size = s->b_frame_ref->size;
/* store reference to data into BSFContext */
s->b_frame_ref = tmp;
if (s->b_frame_ref->size <= MAX_NVOP_SIZE) {
/* N-VOP - discard stored data */
av_log(ctx, AV_LOG_DEBUG, "Skipping N-VOP.\n");
av_buffer_unref(&s->b_frame_ref);
}
} else if (nb_vop >= 2) {
/* use first frame of the packet */
pkt->size = pos_vop2;
} else if (pos_p >= 0) {
ret = av_packet_make_writable(pkt);
if (ret < 0)
goto fail;
av_log(ctx, AV_LOG_DEBUG, "Updating DivX userdata (remove trailing 'p').\n");
/* remove 'p' (packed) from the end of the (DivX) userdata string */
pkt->data[pos_p] = '\0';
} else {
/* use packet as is */
}
fail:
if (ret < 0)
av_packet_unref(pkt);
return ret;
}
static int mpeg4_unpack_bframes_init(AVBSFContext *ctx)
{
if (ctx->par_in->extradata) {
int pos_p_ext = -1;
scan_buffer(ctx->par_in->extradata, ctx->par_in->extradata_size, &pos_p_ext, NULL, NULL);
if (pos_p_ext >= 0) {
av_log(ctx, AV_LOG_DEBUG,
"Updating DivX userdata (remove trailing 'p') in extradata.\n");
ctx->par_out->extradata[pos_p_ext] = '\0';
}
}
return 0;
}
static void mpeg4_unpack_bframes_close_flush(AVBSFContext *bsfc)
{
UnpackBFramesBSFContext *ctx = bsfc->priv_data;
av_buffer_unref(&ctx->b_frame_ref);
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_MPEG4, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_mpeg4_unpack_bframes_bsf = {
.p.name = "mpeg4_unpack_bframes",
.p.codec_ids = codec_ids,
.priv_data_size = sizeof(UnpackBFramesBSFContext),
.init = mpeg4_unpack_bframes_init,
.filter = mpeg4_unpack_bframes_filter,
.flush = mpeg4_unpack_bframes_close_flush,
.close = mpeg4_unpack_bframes_close_flush,
};
+236
View File
@@ -0,0 +1,236 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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 "bsf.h"
#include "bsf_internal.h"
#include "libavutil/log.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavutil/eval.h"
static const char *const var_names[] = {
"n", ///< packet index, starting from zero
"tb", ///< timebase
"pts", ///< packet presentation timestamp
"dts", ///< packet decoding timestamp
"nopts", ///< AV_NOPTS_VALUE
"startpts", ///< first seen non-AV_NOPTS_VALUE packet timestamp
"startdts", ///< first seen non-AV_NOPTS_VALUE packet timestamp
"duration", "d", ///< packet duration
"pos", ///< original position of packet in its source
"size", ///< packet size
"key" , ///< packet keyframe flag
"state", ///< random-ish state
NULL
};
enum var_name {
VAR_N,
VAR_TB,
VAR_PTS,
VAR_DTS,
VAR_NOPTS,
VAR_STARTPTS,
VAR_STARTDTS,
VAR_DURATION, VAR_D,
VAR_POS,
VAR_SIZE,
VAR_KEY,
VAR_STATE,
VAR_VARS_NB
};
typedef struct NoiseContext {
const AVClass *class;
char *amount_str;
char *drop_str;
int dropamount;
AVExpr *amount_pexpr;
AVExpr *drop_pexpr;
double var_values[VAR_VARS_NB];
unsigned int state;
unsigned int pkt_idx;
} NoiseContext;
static int noise_init(AVBSFContext *ctx)
{
NoiseContext *s = ctx->priv_data;
int ret;
if (!s->amount_str) {
s->amount_str = (!s->drop_str && !s->dropamount) ? av_strdup("-1") : av_strdup("0");
if (!s->amount_str)
return AVERROR(ENOMEM);
}
if (ctx->par_in->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME &&
strcmp(s->amount_str, "0")) {
av_log(ctx, AV_LOG_ERROR, "Wrapped AVFrame noising is unsupported\n");
return AVERROR_PATCHWELCOME;
}
ret = av_expr_parse(&s->amount_pexpr, s->amount_str,
var_names, NULL, NULL, NULL, NULL, 0, ctx);
if (ret < 0) {
av_log(ctx, AV_LOG_ERROR, "Error in parsing expr for amount: %s\n", s->amount_str);
return ret;
}
if (s->drop_str && s->dropamount) {
av_log(ctx, AV_LOG_WARNING, "Both drop '%s' and dropamount=%d set. Ignoring dropamount.\n",
s->drop_str, s->dropamount);
s->dropamount = 0;
}
if (s->drop_str) {
ret = av_expr_parse(&s->drop_pexpr, s->drop_str,
var_names, NULL, NULL, NULL, NULL, 0, ctx);
if (ret < 0) {
av_log(ctx, AV_LOG_ERROR, "Error in parsing expr for drop: %s\n", s->drop_str);
return ret;
}
}
s->var_values[VAR_TB] = ctx->time_base_out.den ? av_q2d(ctx->time_base_out) : 0;
s->var_values[VAR_NOPTS] = AV_NOPTS_VALUE;
s->var_values[VAR_STARTPTS] = AV_NOPTS_VALUE;
s->var_values[VAR_STARTDTS] = AV_NOPTS_VALUE;
s->var_values[VAR_STATE] = 0;
return 0;
}
static int noise(AVBSFContext *ctx, AVPacket *pkt)
{
NoiseContext *s = ctx->priv_data;
int i, ret, amount, drop = 0;
double res;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0)
return ret;
s->var_values[VAR_N] = s->pkt_idx++;
s->var_values[VAR_PTS] = pkt->pts;
s->var_values[VAR_DTS] = pkt->dts;
s->var_values[VAR_DURATION] =
s->var_values[VAR_D] = pkt->duration;
s->var_values[VAR_SIZE] = pkt->size;
s->var_values[VAR_KEY] = !!(pkt->flags & AV_PKT_FLAG_KEY);
s->var_values[VAR_POS] = pkt->pos;
if (s->var_values[VAR_STARTPTS] == AV_NOPTS_VALUE)
s->var_values[VAR_STARTPTS] = pkt->pts;
if (s->var_values[VAR_STARTDTS] == AV_NOPTS_VALUE)
s->var_values[VAR_STARTDTS] = pkt->dts;
res = av_expr_eval(s->amount_pexpr, s->var_values, NULL);
if (isnan(res))
amount = 0;
else if (res < 0)
amount = (s->state % 10001 + 1);
else
amount = (int)res;
if (s->drop_str) {
res = av_expr_eval(s->drop_pexpr, s->var_values, NULL);
if (isnan(res))
drop = 0;
else if (res < 0)
drop = !(s->state % FFABS((int)res));
else
drop = !!res;
}
if(s->dropamount) {
drop = !(s->state % s->dropamount);
}
av_log(ctx, AV_LOG_VERBOSE, "Stream #%d packet %d pts %"PRId64" - amount %d drop %d\n",
pkt->stream_index, (unsigned int)s->var_values[VAR_N], pkt->pts, amount, drop);
if (drop) {
s->var_values[VAR_STATE] = ++s->state;
av_packet_unref(pkt);
return AVERROR(EAGAIN);
}
if (amount) {
ret = av_packet_make_writable(pkt);
if (ret < 0) {
av_packet_unref(pkt);
return ret;
}
}
for (i = 0; i < pkt->size; i++) {
s->state += pkt->data[i] + 1;
if (amount && s->state % amount == 0)
pkt->data[i] = s->state;
}
s->var_values[VAR_STATE] = s->state;
return 0;
}
static void noise_close(AVBSFContext *bsf)
{
NoiseContext *s = bsf->priv_data;
av_expr_free(s->amount_pexpr);
av_expr_free(s->drop_pexpr);
s->amount_pexpr = s->drop_pexpr = NULL;
}
#define OFFSET(x) offsetof(NoiseContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption options[] = {
{ "amount", NULL, OFFSET(amount_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
{ "drop", NULL, OFFSET(drop_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
{ "dropamount", NULL, OFFSET(dropamount), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
{ NULL },
};
static const AVClass noise_class = {
.class_name = "noise",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFBitStreamFilter ff_noise_bsf = {
.p.name = "noise",
.p.priv_class = &noise_class,
.priv_data_size = sizeof(NoiseContext),
.init = noise_init,
.close = noise_close,
.filter = noise,
};
+29
View File
@@ -0,0 +1,29 @@
/*
* 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
*/
/**
* @file
* Null bitstream filter -- pass the input through unchanged.
*/
#include "bsf_internal.h"
const FFBitStreamFilter ff_null_bsf = {
.p.name = "null",
.filter = ff_bsf_get_packet_ref,
};
+68
View File
@@ -0,0 +1,68 @@
/*
* 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 "bsf.h"
#include "bsf_internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/opt.h"
typedef struct OpusBSFContext {
const AVClass *class;
int gain;
} OpusBSFContext;
static int opus_metadata_init(AVBSFContext *bsfc)
{
OpusBSFContext *s = bsfc->priv_data;
if (bsfc->par_out->extradata_size < 19)
return AVERROR_INVALIDDATA;
AV_WL16(bsfc->par_out->extradata + 16, s->gain);
return 0;
}
#define OFFSET(x) offsetof(OpusBSFContext, x)
#define FLAGS (AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption opus_metadata_options[] = {
{ "gain", "Gain, actual amplification is pow(10, gain/(20.0*256))", OFFSET(gain),
AV_OPT_TYPE_INT, { .i64 = 0 }, -(INT16_MAX + 1), INT16_MAX, .flags = FLAGS },
{ NULL },
};
static const AVClass opus_metadata_class = {
.class_name = "opus_metadata_bsf",
.item_name = av_default_item_name,
.option = opus_metadata_options,
.version = LIBAVUTIL_VERSION_INT,
};
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_OPUS, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_opus_metadata_bsf = {
.p.name = "opus_metadata",
.p.codec_ids = codec_ids,
.p.priv_class = &opus_metadata_class,
.priv_data_size = sizeof(OpusBSFContext),
.init = &opus_metadata_init,
.filter = &ff_bsf_get_packet_ref,
};
+241
View File
@@ -0,0 +1,241 @@
/*
* Copyright (c) 2020 Marton Balint
*
* 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 "bsf.h"
#include "bsf_internal.h"
#include "libavutil/avassert.h"
#include "libavutil/opt.h"
typedef struct PCMContext {
const AVClass *class;
int nb_out_samples;
int pad;
AVRational frame_rate;
AVPacket *in_pkt;
AVPacket *out_pkt;
int sample_size;
int64_t n;
} PCMContext;
static int init(AVBSFContext *ctx)
{
PCMContext *s = ctx->priv_data;
AVRational sr = av_make_q(ctx->par_in->sample_rate, 1);
int64_t min_samples;
if (ctx->par_in->ch_layout.nb_channels <= 0 || ctx->par_in->sample_rate <= 0)
return AVERROR(EINVAL);
ctx->time_base_out = av_inv_q(sr);
s->sample_size = ctx->par_in->ch_layout.nb_channels *
av_get_bits_per_sample(ctx->par_in->codec_id) / 8;
if (s->frame_rate.num) {
min_samples = av_rescale_q_rnd(1, sr, s->frame_rate, AV_ROUND_DOWN);
} else {
min_samples = s->nb_out_samples;
}
if (min_samples <= 0 || min_samples > INT_MAX / s->sample_size - 1)
return AVERROR(EINVAL);
s->in_pkt = av_packet_alloc();
s->out_pkt = av_packet_alloc();
if (!s->in_pkt || !s->out_pkt)
return AVERROR(ENOMEM);
return 0;
}
static void uninit(AVBSFContext *ctx)
{
PCMContext *s = ctx->priv_data;
av_packet_free(&s->in_pkt);
av_packet_free(&s->out_pkt);
}
static void flush(AVBSFContext *ctx)
{
PCMContext *s = ctx->priv_data;
av_packet_unref(s->in_pkt);
av_packet_unref(s->out_pkt);
s->n = 0;
}
static int send_packet(PCMContext *s, int nb_samples, AVPacket *pkt)
{
pkt->duration = nb_samples;
s->n++;
return 0;
}
static void drain_packet(AVPacket *pkt, int drain_data, int drain_samples)
{
pkt->size -= drain_data;
pkt->data += drain_data;
if (pkt->dts != AV_NOPTS_VALUE)
pkt->dts += drain_samples;
if (pkt->pts != AV_NOPTS_VALUE)
pkt->pts += drain_samples;
}
static int get_next_nb_samples(AVBSFContext *ctx)
{
PCMContext *s = ctx->priv_data;
if (s->frame_rate.num) {
AVRational sr = av_make_q(ctx->par_in->sample_rate, 1);
return av_rescale_q(s->n + 1, sr, s->frame_rate) - av_rescale_q(s->n, sr, s->frame_rate);
} else {
return s->nb_out_samples;
}
}
static void set_silence(AVCodecParameters *par, uint8_t *buf, size_t size)
{
int c = 0;
switch (par->codec_id) {
case AV_CODEC_ID_PCM_ALAW: c = 0xd5; break;
case AV_CODEC_ID_PCM_MULAW:
case AV_CODEC_ID_PCM_VIDC: c = 0xff; break;
case AV_CODEC_ID_PCM_U8: c = 0x80; break;
}
memset(buf, c, size);
}
static int rechunk_filter(AVBSFContext *ctx, AVPacket *pkt)
{
PCMContext *s = ctx->priv_data;
int nb_samples = get_next_nb_samples(ctx);
int data_size = nb_samples * s->sample_size;
int ret;
do {
if (s->in_pkt->size) {
if (s->out_pkt->size || s->in_pkt->size < data_size) {
int drain = FFMIN(s->in_pkt->size, data_size - s->out_pkt->size);
if (!s->out_pkt->size) {
ret = av_new_packet(s->out_pkt, data_size);
if (ret < 0)
return ret;
ret = av_packet_copy_props(s->out_pkt, s->in_pkt);
if (ret < 0) {
av_packet_unref(s->out_pkt);
return ret;
}
s->out_pkt->size = 0;
}
memcpy(s->out_pkt->data + s->out_pkt->size, s->in_pkt->data, drain);
s->out_pkt->size += drain;
drain_packet(s->in_pkt, drain, drain / s->sample_size);
if (!s->in_pkt->size)
av_packet_unref(s->in_pkt);
if (s->out_pkt->size == data_size) {
av_packet_move_ref(pkt, s->out_pkt);
return send_packet(s, nb_samples, pkt);
}
av_assert0(!s->in_pkt->size);
} else if (s->in_pkt->size > data_size) {
ret = av_packet_ref(pkt, s->in_pkt);
if (ret < 0)
return ret;
pkt->size = data_size;
drain_packet(s->in_pkt, data_size, nb_samples);
return send_packet(s, nb_samples, pkt);
} else {
av_assert0(s->in_pkt->size == data_size);
av_packet_move_ref(pkt, s->in_pkt);
return send_packet(s, nb_samples, pkt);
}
} else
av_packet_unref(s->in_pkt);
ret = ff_bsf_get_packet_ref(ctx, s->in_pkt);
if (ret == AVERROR_EOF && s->out_pkt->size) {
if (s->pad) {
set_silence(ctx->par_in, s->out_pkt->data + s->out_pkt->size, data_size - s->out_pkt->size);
s->out_pkt->size = data_size;
} else {
nb_samples = s->out_pkt->size / s->sample_size;
}
av_packet_move_ref(pkt, s->out_pkt);
return send_packet(s, nb_samples, pkt);
}
if (ret >= 0)
av_packet_rescale_ts(s->in_pkt, ctx->time_base_in, ctx->time_base_out);
} while (ret >= 0);
return ret;
}
#define OFFSET(x) offsetof(PCMContext, x)
#define FLAGS (AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_BSF_PARAM)
static const AVOption options[] = {
{ "nb_out_samples", "set the number of per-packet output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS },
{ "n", "set the number of per-packet output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS },
{ "pad", "pad last packet with zeros", OFFSET(pad), AV_OPT_TYPE_BOOL, {.i64=1} , 0, 1, FLAGS },
{ "p", "pad last packet with zeros", OFFSET(pad), AV_OPT_TYPE_BOOL, {.i64=1} , 0, 1, FLAGS },
{ "frame_rate", "set number of packets per second", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS },
{ "r", "set number of packets per second", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS },
{ NULL },
};
static const AVClass pcm_rechunk_class = {
.class_name = "pcm_rechunk_bsf",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_PCM_ALAW,
AV_CODEC_ID_PCM_F16LE,
AV_CODEC_ID_PCM_F24LE,
AV_CODEC_ID_PCM_F32BE,
AV_CODEC_ID_PCM_F32LE,
AV_CODEC_ID_PCM_F64BE,
AV_CODEC_ID_PCM_F64LE,
AV_CODEC_ID_PCM_MULAW,
AV_CODEC_ID_PCM_S16BE,
AV_CODEC_ID_PCM_S16LE,
AV_CODEC_ID_PCM_S24BE,
AV_CODEC_ID_PCM_S24DAUD,
AV_CODEC_ID_PCM_S24LE,
AV_CODEC_ID_PCM_S32BE,
AV_CODEC_ID_PCM_S32LE,
AV_CODEC_ID_PCM_S64BE,
AV_CODEC_ID_PCM_S64LE,
AV_CODEC_ID_PCM_S8,
AV_CODEC_ID_PCM_SGA,
AV_CODEC_ID_PCM_U8,
AV_CODEC_ID_PCM_VIDC,
AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_pcm_rechunk_bsf = {
.p.name = "pcm_rechunk",
.p.codec_ids = codec_ids,
.p.priv_class = &pcm_rechunk_class,
.priv_data_size = sizeof(PCMContext),
.filter = rechunk_filter,
.init = init,
.flush = flush,
.close = uninit,
};
+191
View File
@@ -0,0 +1,191 @@
/*
* Copyright (c) 2020 John Stebbins <jstebbins.hb@gmail.com>
*
* 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
*/
/**
* @file
* This bitstream filter merges PGS subtitle packets containing incomplete
* set of segments into a single packet
*
* Packets already containing a complete set of segments will be passed through
* unchanged.
*/
#include "libavutil/attributes.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "bsf.h"
#include "bsf_internal.h"
enum PGSSegmentType {
PALETTE_SEGMENT = 0x14,
OBJECT_SEGMENT = 0x15,
PRESENTATION_SEGMENT = 0x16,
WINDOW_SEGMENT = 0x17,
END_DISPLAY_SET_SEGMENT = 0x80,
};
typedef struct PGSMergeContext {
AVPacket *buffer_pkt, *in;
int presentation_found;
int pkt_flags;
} PGSMergeContext;
static av_cold void frame_merge_flush(AVBSFContext *bsf)
{
PGSMergeContext *ctx = bsf->priv_data;
ctx->presentation_found = ctx->pkt_flags = 0;
av_packet_unref(ctx->in);
av_packet_unref(ctx->buffer_pkt);
}
static int frame_merge_output(PGSMergeContext *ctx, AVPacket *dst, AVPacket *src)
{
if (!ctx->presentation_found)
ctx->pkt_flags |= AV_PKT_FLAG_CORRUPT;
ctx->presentation_found = 0;
src->flags |= ctx->pkt_flags;
ctx->pkt_flags = 0;
av_packet_move_ref(dst, src);
return 0;
}
static int frame_merge_filter(AVBSFContext *bsf, AVPacket *out)
{
PGSMergeContext *ctx = bsf->priv_data;
AVPacket *in = ctx->in, *pkt = ctx->buffer_pkt;
int ret, size, pos, display = 0, presentation = 0;
unsigned int i;
if (!in->data) {
ret = ff_bsf_get_packet_ref(bsf, in);
if (ret == AVERROR_EOF && pkt->data) {
// Output remaining data
ctx->pkt_flags |= AV_PKT_FLAG_CORRUPT;
return frame_merge_output(ctx, out, pkt);
}
if (ret < 0)
return ret;
}
if (!in->size) {
av_packet_unref(in);
return AVERROR(EAGAIN);
}
in->flags &= ~AV_PKT_FLAG_KEY; // Will be detected in the stream
// Validate packet data and find display_end segment
size = in->size;
i = 0;
while (i + 3 <= in->size) {
uint8_t segment_type = in->data[i];
int segment_len = AV_RB16(in->data + i + 1) + 3;
if (i + segment_len > in->size)
break; // Invalid, segments can't span packets
if (segment_type == PRESENTATION_SEGMENT && ctx->presentation_found)
break; // Invalid, there can be only one
if (segment_type == PRESENTATION_SEGMENT) {
uint8_t state;
if (segment_len < 11)
break; // Invalid presentation segment length
ctx->presentation_found = presentation = 1;
state = in->data[i + 10] & 0xc0;
if (state)
ctx->pkt_flags |= AV_PKT_FLAG_KEY;
else
ctx->pkt_flags &= ~AV_PKT_FLAG_KEY;
}
i += segment_len;
if (segment_type == END_DISPLAY_SET_SEGMENT) {
size = i;
display = 1;
break;
}
}
if (display && pkt->size == 0 && size == in->size) // passthrough
return frame_merge_output(ctx, out, in);
if (!display && i != in->size) {
av_log(bsf, AV_LOG_WARNING, "Failed to parse PGS segments.\n");
// force output what we have
size = in->size;
display = 1;
ctx->pkt_flags |= AV_PKT_FLAG_CORRUPT;
}
if (presentation) {
ret = av_packet_copy_props(pkt, in);
if (ret < 0)
goto fail;
}
pos = pkt->size;
ret = av_grow_packet(pkt, size);
if (ret < 0)
goto fail;
memcpy(pkt->data + pos, in->data, size);
if (size == in->size)
av_packet_unref(in);
else {
in->data += size;
in->size -= size;
}
if (display)
return frame_merge_output(ctx, out, pkt);
return AVERROR(EAGAIN);
fail:
frame_merge_flush(bsf);
return ret;
}
static av_cold int frame_merge_init(AVBSFContext *bsf)
{
PGSMergeContext *ctx = bsf->priv_data;
ctx->in = av_packet_alloc();
ctx->buffer_pkt = av_packet_alloc();
if (!ctx->in || !ctx->buffer_pkt)
return AVERROR(ENOMEM);
return 0;
}
static av_cold void frame_merge_close(AVBSFContext *bsf)
{
PGSMergeContext *ctx = bsf->priv_data;
av_packet_free(&ctx->in);
av_packet_free(&ctx->buffer_pkt);
}
static const enum AVCodecID frame_merge_codec_ids[] = {
AV_CODEC_ID_HDMV_PGS_SUBTITLE, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_pgs_frame_merge_bsf = {
.p.name = "pgs_frame_merge",
.p.codec_ids = frame_merge_codec_ids,
.priv_data_size = sizeof(PGSMergeContext),
.init = frame_merge_init,
.flush = frame_merge_flush,
.close = frame_merge_close,
.filter = frame_merge_filter,
};
+176
View File
@@ -0,0 +1,176 @@
/*
* Prores Metadata bitstream filter
* Copyright (c) 2018 Jokyo Images
*
* 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
*/
/**
* @file
* Prores Metadata bitstream filter
* set frame colorspace property
*/
#include "libavutil/common.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/opt.h"
#include "bsf.h"
#include "bsf_internal.h"
typedef struct ProresMetadataContext {
const AVClass *class;
int color_primaries;
int transfer_characteristics;
int matrix_coefficients;
} ProresMetadataContext;
static int prores_metadata(AVBSFContext *bsf, AVPacket *pkt)
{
ProresMetadataContext *ctx = bsf->priv_data;
int ret = 0;
int buf_size;
uint8_t *buf;
ret = ff_bsf_get_packet_ref(bsf, pkt);
if (ret < 0)
return ret;
ret = av_packet_make_writable(pkt);
if (ret < 0)
goto fail;
buf = pkt->data;
buf_size = pkt->size;
/* check start of the prores frame */
if (buf_size < 28) {
av_log(bsf, AV_LOG_ERROR, "not enough data in prores frame\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
if (AV_RL32(buf + 4) != AV_RL32("icpf")) {
av_log(bsf, AV_LOG_ERROR, "invalid frame header\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
if (AV_RB16(buf + 8) < 28) {
av_log(bsf, AV_LOG_ERROR, "invalid frame header size\n");
ret = AVERROR_INVALIDDATA;
goto fail;
}
/* set the new values */
if (ctx->color_primaries != -1)
buf[8+14] = ctx->color_primaries;
if (ctx->transfer_characteristics != -1)
buf[8+15] = ctx->transfer_characteristics;
if (ctx->matrix_coefficients != -1)
buf[8+16] = ctx->matrix_coefficients;
fail:
if (ret < 0)
av_packet_unref(pkt);
return ret;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_PRORES, AV_CODEC_ID_NONE,
};
static int prores_metadata_init(AVBSFContext *bsf)
{
ProresMetadataContext *ctx = bsf->priv_data;
/*! check options */
switch (ctx->color_primaries) {
case -1:
case 0:
case AVCOL_PRI_BT709:
case AVCOL_PRI_BT470BG:
case AVCOL_PRI_SMPTE170M:
case AVCOL_PRI_BT2020:
case AVCOL_PRI_SMPTE431:
case AVCOL_PRI_SMPTE432:
break;
default:
av_log(bsf, AV_LOG_ERROR, "Color primaries %d is not a valid value\n", ctx->color_primaries);
return AVERROR(EINVAL);
}
switch (ctx->matrix_coefficients) {
case -1:
case 0:
case AVCOL_SPC_BT709:
case AVCOL_SPC_SMPTE170M:
case AVCOL_SPC_BT2020_NCL:
break;
default:
av_log(bsf, AV_LOG_ERROR, "Colorspace %d is not a valid value\n", ctx->matrix_coefficients);
return AVERROR(EINVAL);
}
return 0;
}
#define OFFSET(x) offsetof(ProresMetadataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_BSF_PARAM)
static const AVOption options[] = {
{"color_primaries", "select color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_PRI_SMPTE432, FLAGS, .unit = "color_primaries"},
{"auto", "keep the same color primaries", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"},
{"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"},
{"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"},
{"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"},
{"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"},
{"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"},
{"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"},
{"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"},
{"color_trc", "select color transfer", OFFSET(transfer_characteristics), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_TRC_NB - 1, FLAGS, .unit = "color_trc"},
{"auto", "keep the same color transfer", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"},
{"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"},
{"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"},
{"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"},
{"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"},
{"colorspace", "select colorspace", OFFSET(matrix_coefficients), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_SPC_BT2020_NCL, FLAGS, .unit = "colorspace"},
{"auto", "keep the same colorspace", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"},
{"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"},
{"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT709}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"},
{"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"},
{"bt2020nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_NCL}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"},
{ NULL },
};
static const AVClass prores_metadata_class = {
.class_name = "prores_metadata_bsf",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFBitStreamFilter ff_prores_metadata_bsf = {
.p.name = "prores_metadata",
.p.codec_ids = codec_ids,
.p.priv_class = &prores_metadata_class,
.priv_data_size = sizeof(ProresMetadataContext),
.init = prores_metadata_init,
.filter = prores_metadata,
};
+254
View File
@@ -0,0 +1,254 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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 "libavutil/log.h"
#include "libavutil/opt.h"
#include "av1_parse.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "h264.h"
#include "startcode.h"
#include "vc1_common.h"
#include "hevc/hevc.h"
enum RemoveFreq {
REMOVE_FREQ_KEYFRAME,
REMOVE_FREQ_ALL,
REMOVE_FREQ_NONKEYFRAME,
};
#define START_CODE 0x000001
typedef struct RemoveExtradataContext {
const AVClass *class;
int freq;
} RemoveExtradataContext;
static int av1_split(const uint8_t *buf, int buf_size, void *logctx)
{
AV1OBU obu;
const uint8_t *ptr = buf, *end = buf + buf_size;
while (ptr < end) {
int len = ff_av1_extract_obu(&obu, ptr, buf_size, logctx);
if (len < 0)
break;
if (obu.type == AV1_OBU_FRAME_HEADER ||
obu.type == AV1_OBU_FRAME) {
return ptr - buf;
}
ptr += len;
buf_size -= len;
}
return 0;
}
static int h264_split(const uint8_t *buf, int buf_size)
{
const uint8_t *ptr = buf, *end = buf + buf_size;
uint32_t state = -1;
int has_sps = 0;
int has_pps = 0;
int nalu_type;
while (ptr < end) {
ptr = avpriv_find_start_code(ptr, end, &state);
if ((state & 0xFFFFFF00) != 0x100)
break;
nalu_type = state & 0x1F;
if (nalu_type == H264_NAL_SPS) {
has_sps = 1;
} else if (nalu_type == H264_NAL_PPS)
has_pps = 1;
/* else if (nalu_type == 0x01 ||
* nalu_type == 0x02 ||
* nalu_type == 0x05) {
* }
*/
else if ((nalu_type != H264_NAL_SEI || has_pps) &&
nalu_type != H264_NAL_AUD && nalu_type != H264_NAL_SPS_EXT &&
nalu_type != 0x0f) {
if (has_sps) {
while (ptr - 4 > buf && ptr[-5] == 0)
ptr--;
return ptr - 4 - buf;
}
}
}
return 0;
}
// Split after the parameter sets at the beginning of the stream if they exist.
static int hevc_split(const uint8_t *buf, int buf_size)
{
const uint8_t *ptr = buf, *end = buf + buf_size;
uint32_t state = -1;
int has_vps = 0;
int has_sps = 0;
int has_pps = 0;
int nut;
while (ptr < end) {
ptr = avpriv_find_start_code(ptr, end, &state);
if ((state >> 8) != START_CODE)
break;
nut = (state >> 1) & 0x3F;
if (nut == HEVC_NAL_VPS)
has_vps = 1;
else if (nut == HEVC_NAL_SPS)
has_sps = 1;
else if (nut == HEVC_NAL_PPS)
has_pps = 1;
else if ((nut != HEVC_NAL_SEI_PREFIX || has_pps) &&
nut != HEVC_NAL_AUD) {
if (has_vps && has_sps) {
while (ptr - 4 > buf && ptr[-5] == 0)
ptr--;
return ptr - 4 - buf;
}
}
}
return 0;
}
static int mpegvideo_split(const uint8_t *buf, int buf_size)
{
uint32_t state = -1;
int found = 0;
for (int i = 0; i < buf_size; i++) {
state = (state << 8) | buf[i];
if (state == 0x1B3) {
found = 1;
} else if (found && state != 0x1B5 && state < 0x200 && state >= 0x100)
return i - 3;
}
return 0;
}
static int mpeg4video_split(const uint8_t *buf, int buf_size)
{
const uint8_t *ptr = buf, *end = buf + buf_size;
uint32_t state = -1;
while (ptr < end) {
ptr = avpriv_find_start_code(ptr, end, &state);
if (state == 0x1B3 || state == 0x1B6)
return ptr - 4 - buf;
}
return 0;
}
static int vc1_split(const uint8_t *buf, int buf_size)
{
const uint8_t *ptr = buf, *end = buf + buf_size;
uint32_t state = -1;
int charged = 0;
while (ptr < end) {
ptr = avpriv_find_start_code(ptr, end, &state);
if (state == VC1_CODE_SEQHDR || state == VC1_CODE_ENTRYPOINT) {
charged = 1;
} else if (charged && IS_MARKER(state))
return ptr - 4 - buf;
}
return 0;
}
static int remove_extradata(AVBSFContext *ctx, AVPacket *pkt)
{
RemoveExtradataContext *s = ctx->priv_data;
int ret;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0)
return ret;
if (s->freq == REMOVE_FREQ_ALL ||
(s->freq == REMOVE_FREQ_NONKEYFRAME && !(pkt->flags & AV_PKT_FLAG_KEY)) ||
(s->freq == REMOVE_FREQ_KEYFRAME && pkt->flags & AV_PKT_FLAG_KEY)) {
int i;
switch (ctx->par_in->codec_id) {
case AV_CODEC_ID_AV1:
i = av1_split(pkt->data, pkt->size, ctx);
break;
case AV_CODEC_ID_AVS2:
case AV_CODEC_ID_AVS3:
case AV_CODEC_ID_CAVS:
case AV_CODEC_ID_MPEG4:
i = mpeg4video_split(pkt->data, pkt->size);
break;
case AV_CODEC_ID_H264:
i = h264_split(pkt->data, pkt->size);
break;
case AV_CODEC_ID_HEVC:
i = hevc_split(pkt->data, pkt->size);
break;
case AV_CODEC_ID_MPEG1VIDEO:
case AV_CODEC_ID_MPEG2VIDEO:
i = mpegvideo_split(pkt->data, pkt->size);
break;
case AV_CODEC_ID_VC1:
i = vc1_split(pkt->data, pkt->size);
break;
default:
i = 0;
}
pkt->data += i;
pkt->size -= i;
}
return 0;
}
#define OFFSET(x) offsetof(RemoveExtradataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption options[] = {
{ "freq", NULL, OFFSET(freq), AV_OPT_TYPE_INT, { .i64 = REMOVE_FREQ_KEYFRAME }, REMOVE_FREQ_KEYFRAME, REMOVE_FREQ_NONKEYFRAME, FLAGS, .unit = "freq" },
{ "k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = REMOVE_FREQ_NONKEYFRAME }, .flags = FLAGS, .unit = "freq" },
{ "keyframe", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = REMOVE_FREQ_KEYFRAME }, .flags = FLAGS, .unit = "freq" },
{ "e", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = REMOVE_FREQ_ALL }, .flags = FLAGS, .unit = "freq" },
{ "all", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = REMOVE_FREQ_ALL }, .flags = FLAGS, .unit = "freq" },
{ NULL },
};
static const AVClass remove_extradata_class = {
.class_name = "remove_extradata",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFBitStreamFilter ff_remove_extradata_bsf = {
.p.name = "remove_extra",
.p.priv_class = &remove_extradata_class,
.priv_data_size = sizeof(RemoveExtradataContext),
.filter = remove_extradata,
};
+277
View File
@@ -0,0 +1,277 @@
/*
* Copyright (c) 2021 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
*/
/**
* @file
* Change the PTS/DTS timestamps.
*/
#include "libavutil/opt.h"
#include "libavutil/eval.h"
#include "bsf.h"
#include "bsf_internal.h"
static const char *const var_names[] = {
"N", ///< frame number (starting at zero)
"TS",
"POS", ///< original position in the file of the frame
"PREV_INPTS", ///< previous input PTS
"PREV_INDTS", ///< previous input DTS
"PREV_INDURATION", ///< previous input duration
"PREV_OUTPTS", ///< previous output PTS
"PREV_OUTDTS", ///< previous output DTS
"PREV_OUTDURATION", ///< previous output duration
"NEXT_PTS", ///< next input PTS
"NEXT_DTS", ///< next input DTS
"NEXT_DURATION", ///< next input duration
"PTS", ///< original PTS in the file of the frame
"DTS", ///< original DTS in the file of the frame
"DURATION", ///< original duration in the file of the frame
"STARTPTS", ///< PTS at start of movie
"STARTDTS", ///< DTS at start of movie
"TB", ///< input timebase of the stream
"TB_OUT", ///< output timebase of the stream
"SR", ///< sample rate of the stream
"NOPTS", ///< The AV_NOPTS_VALUE constant
NULL
};
enum var_name {
VAR_N,
VAR_TS,
VAR_POS,
VAR_PREV_INPTS,
VAR_PREV_INDTS,
VAR_PREV_INDUR,
VAR_PREV_OUTPTS,
VAR_PREV_OUTDTS,
VAR_PREV_OUTDUR,
VAR_NEXT_PTS,
VAR_NEXT_DTS,
VAR_NEXT_DUR,
VAR_PTS,
VAR_DTS,
VAR_DURATION,
VAR_STARTPTS,
VAR_STARTDTS,
VAR_TB,
VAR_TB_OUT,
VAR_SR,
VAR_NOPTS,
VAR_VARS_NB
};
typedef struct SetTSContext {
const AVClass *class;
char *ts_str;
char *pts_str;
char *dts_str;
char *duration_str;
AVRational time_base;
int64_t frame_number;
double var_values[VAR_VARS_NB];
AVExpr *ts_expr;
AVExpr *pts_expr;
AVExpr *dts_expr;
AVExpr *duration_expr;
AVPacket *prev_inpkt;
AVPacket *prev_outpkt;
AVPacket *cur_pkt;
} SetTSContext;
static int setts_init(AVBSFContext *ctx)
{
SetTSContext *s = ctx->priv_data;
int ret;
s->prev_inpkt = av_packet_alloc();
s->prev_outpkt = av_packet_alloc();
s->cur_pkt = av_packet_alloc();
if (!s->prev_inpkt || !s->prev_outpkt || !s->cur_pkt)
return AVERROR(ENOMEM);
if ((ret = av_expr_parse(&s->ts_expr, s->ts_str,
var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
av_log(ctx, AV_LOG_ERROR, "Error while parsing ts expression '%s'\n", s->ts_str);
return ret;
}
if ((ret = av_expr_parse(&s->duration_expr, s->duration_str,
var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
av_log(ctx, AV_LOG_ERROR, "Error while parsing duration expression '%s'\n", s->duration_str);
return ret;
}
if (s->pts_str) {
if ((ret = av_expr_parse(&s->pts_expr, s->pts_str,
var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
av_log(ctx, AV_LOG_ERROR, "Error while parsing pts expression '%s'\n", s->pts_str);
return ret;
}
}
if (s->dts_str) {
if ((ret = av_expr_parse(&s->dts_expr, s->dts_str,
var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
av_log(ctx, AV_LOG_ERROR, "Error while parsing dts expression '%s'\n", s->dts_str);
return ret;
}
}
if (s->time_base.num > 0 && s->time_base.den > 0)
ctx->time_base_out = s->time_base;
s->frame_number= 0;
s->var_values[VAR_STARTPTS] = AV_NOPTS_VALUE;
s->var_values[VAR_STARTDTS] = AV_NOPTS_VALUE;
s->var_values[VAR_NOPTS] = AV_NOPTS_VALUE;
s->var_values[VAR_TB] = ctx->time_base_in.den ? av_q2d(ctx->time_base_in) : 0;
s->var_values[VAR_TB_OUT]= ctx->time_base_out.den ? av_q2d(ctx->time_base_out) : 0;
s->var_values[VAR_SR] = ctx->par_in->sample_rate;
return 0;
}
static int setts_filter(AVBSFContext *ctx, AVPacket *pkt)
{
SetTSContext *s = ctx->priv_data;
int64_t new_ts, new_pts, new_dts, new_duration;
int ret;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0 && (ret != AVERROR_EOF || !s->cur_pkt->data))
return ret;
if (!s->cur_pkt->data) {
av_packet_move_ref(s->cur_pkt, pkt);
return AVERROR(EAGAIN);
}
if (s->var_values[VAR_STARTPTS] == AV_NOPTS_VALUE)
s->var_values[VAR_STARTPTS] = s->cur_pkt->pts;
if (s->var_values[VAR_STARTDTS] == AV_NOPTS_VALUE)
s->var_values[VAR_STARTDTS] = s->cur_pkt->dts;
s->var_values[VAR_N] = s->frame_number++;
s->var_values[VAR_TS] = s->cur_pkt->dts;
s->var_values[VAR_POS] = s->cur_pkt->pos;
s->var_values[VAR_PTS] = s->cur_pkt->pts;
s->var_values[VAR_DTS] = s->cur_pkt->dts;
s->var_values[VAR_DURATION] = s->cur_pkt->duration;
s->var_values[VAR_PREV_INPTS] = s->prev_inpkt->pts;
s->var_values[VAR_PREV_INDTS] = s->prev_inpkt->dts;
s->var_values[VAR_PREV_INDUR] = s->prev_inpkt->duration;
s->var_values[VAR_PREV_OUTPTS] = s->prev_outpkt->pts;
s->var_values[VAR_PREV_OUTDTS] = s->prev_outpkt->dts;
s->var_values[VAR_PREV_OUTDUR] = s->prev_outpkt->duration;
s->var_values[VAR_NEXT_PTS] = pkt->pts;
s->var_values[VAR_NEXT_DTS] = pkt->dts;
s->var_values[VAR_NEXT_DUR] = pkt->duration;
new_ts = llrint(av_expr_eval(s->ts_expr, s->var_values, NULL));
new_duration = llrint(av_expr_eval(s->duration_expr, s->var_values, NULL));
if (s->pts_str) {
s->var_values[VAR_TS] = s->cur_pkt->pts;
new_pts = llrint(av_expr_eval(s->pts_expr, s->var_values, NULL));
} else {
new_pts = new_ts;
}
if (s->dts_str) {
s->var_values[VAR_TS] = s->cur_pkt->dts;
new_dts = llrint(av_expr_eval(s->dts_expr, s->var_values, NULL));
} else {
new_dts = new_ts;
}
av_packet_unref(s->prev_inpkt);
av_packet_unref(s->prev_outpkt);
av_packet_move_ref(s->prev_inpkt, s->cur_pkt);
av_packet_move_ref(s->cur_pkt, pkt);
ret = av_packet_ref(pkt, s->prev_inpkt);
if (ret < 0)
return ret;
pkt->pts = new_pts;
pkt->dts = new_dts;
pkt->duration = new_duration;
ret = av_packet_ref(s->prev_outpkt, pkt);
if (ret < 0)
av_packet_unref(pkt);
return ret;
}
static void setts_close(AVBSFContext *bsf)
{
SetTSContext *s = bsf->priv_data;
av_packet_free(&s->prev_inpkt);
av_packet_free(&s->prev_outpkt);
av_packet_free(&s->cur_pkt);
av_expr_free(s->ts_expr);
s->ts_expr = NULL;
av_expr_free(s->pts_expr);
s->pts_expr = NULL;
av_expr_free(s->dts_expr);
s->dts_expr = NULL;
av_expr_free(s->duration_expr);
s->duration_expr = NULL;
}
#define OFFSET(x) offsetof(SetTSContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption options[] = {
{ "ts", "set expression for packet PTS and DTS", OFFSET(ts_str), AV_OPT_TYPE_STRING, {.str="TS"}, 0, 0, FLAGS },
{ "pts", "set expression for packet PTS", OFFSET(pts_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
{ "dts", "set expression for packet DTS", OFFSET(dts_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
{ "duration", "set expression for packet duration", OFFSET(duration_str), AV_OPT_TYPE_STRING, {.str="DURATION"}, 0, 0, FLAGS },
{ "time_base", "set output timebase", OFFSET(time_base), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS },
{ NULL },
};
static const AVClass setts_class = {
.class_name = "setts_bsf",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFBitStreamFilter ff_setts_bsf = {
.p.name = "setts",
.p.priv_class = &setts_class,
.priv_data_size = sizeof(SetTSContext),
.init = setts_init,
.close = setts_close,
.filter = setts_filter,
};
+67
View File
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2024 Anton Khirnov <anton@khirnov.net>
*
* 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 <inttypes.h>
#include "bsf.h"
#include "bsf_internal.h"
#include "libavutil/adler32.h"
#include "libavutil/log.h"
#include "libavutil/timestamp.h"
typedef struct ShowinfoContext {
uint64_t nb_packets;
} ShowinfoContext;
static int showinfo_filter(AVBSFContext *ctx, AVPacket *pkt)
{
ShowinfoContext *priv = ctx->priv_data;
uint32_t crc;
int ret;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0)
return ret;
crc = av_adler32_update(0, pkt->data, pkt->size);
av_log(ctx, AV_LOG_INFO,
"n:%7"PRIu64" "
"size:%7d "
"pts:%s pt:%s "
"dts:%s dt:%s "
"ds:%"PRId64" d:%s "
"adler32:0x%08"PRIx32
"\n",
priv->nb_packets, pkt->size,
av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ctx->time_base_in),
av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ctx->time_base_in),
pkt->duration, av_ts2timestr(pkt->duration, &ctx->time_base_in), crc);
priv->nb_packets++;
return 0;
}
const FFBitStreamFilter ff_showinfo_bsf = {
.p.name = "showinfo",
.filter = showinfo_filter,
.priv_data_size = sizeof(ShowinfoContext),
};
@@ -0,0 +1,91 @@
/*
* MXF SMPTE-436M ANC to EIA-608 bitstream filter
* Copyright (c) 2025 Jacob Lifshay
*
* 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 "bsf.h"
#include "bsf_internal.h"
#include "codec_id.h"
#include "libavcodec/smpte_436m.h"
#include "libavutil/error.h"
static av_cold int ff_smpte436m_to_eia608_init(AVBSFContext *ctx)
{
ctx->par_out->codec_type = AVMEDIA_TYPE_SUBTITLE;
ctx->par_out->codec_id = AV_CODEC_ID_EIA_608;
return 0;
}
static int ff_smpte436m_to_eia608_filter(AVBSFContext *ctx, AVPacket *out)
{
AVPacket *in;
int ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
AVSmpte436mAncIterator iter;
ret = av_smpte_436m_anc_iter_init(&iter, in->data, in->size);
if (ret < 0)
goto fail;
AVSmpte436mCodedAnc coded_anc;
while ((ret = av_smpte_436m_anc_iter_next(&iter, &coded_anc)) >= 0) {
AVSmpte291mAnc8bit anc;
ret = av_smpte_291m_anc_8bit_decode(
&anc, coded_anc.payload_sample_coding, coded_anc.payload_sample_count, coded_anc.payload, ctx);
if (ret < 0)
goto fail;
ret = av_smpte_291m_anc_8bit_extract_cta_708(&anc, NULL, ctx);
if (ret == AVERROR(EAGAIN))
continue;
if (ret < 0)
goto fail;
int cc_count = ret;
ret = av_new_packet(out, 3 * cc_count);
if (ret < 0)
goto fail;
ret = av_packet_copy_props(out, in);
if (ret < 0)
goto fail;
// verified it won't fail by running it above
av_smpte_291m_anc_8bit_extract_cta_708(&anc, out->data, ctx);
av_packet_free(&in);
return 0;
}
if (ret != AVERROR_EOF)
return ret;
ret = AVERROR(EAGAIN);
fail:
if (ret < 0)
av_packet_unref(out);
av_packet_free(&in);
return ret;
}
const FFBitStreamFilter ff_smpte436m_to_eia608_bsf = {
.p.name = "smpte436m_to_eia608",
.p.codec_ids = (const enum AVCodecID[]){ AV_CODEC_ID_SMPTE_436M_ANC, AV_CODEC_ID_NONE },
.init = ff_smpte436m_to_eia608_init,
.filter = ff_smpte436m_to_eia608_filter,
};
+129
View File
@@ -0,0 +1,129 @@
/*
* 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 <inttypes.h>
#include "libavutil/avstring.h"
#include "libavutil/avutil.h"
#include "libavutil/log.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
typedef struct TraceHeadersContext {
CodedBitstreamContext *cbc;
CodedBitstreamFragment fragment;
} TraceHeadersContext;
static int trace_headers_init(AVBSFContext *bsf)
{
TraceHeadersContext *ctx = bsf->priv_data;
int err;
err = ff_cbs_init(&ctx->cbc, bsf->par_in->codec_id, bsf);
if (err < 0)
return err;
ctx->cbc->trace_enable = 1;
ctx->cbc->trace_level = AV_LOG_INFO;
ctx->cbc->trace_context = ctx->cbc;
ctx->cbc->trace_read_callback = ff_cbs_trace_read_log;
if (bsf->par_in->extradata) {
CodedBitstreamFragment *frag = &ctx->fragment;
av_log(bsf, AV_LOG_INFO, "Extradata\n");
err = ff_cbs_read_extradata(ctx->cbc, frag, bsf->par_in);
ff_cbs_fragment_reset(frag);
}
return err;
}
static void trace_headers_close(AVBSFContext *bsf)
{
TraceHeadersContext *ctx = bsf->priv_data;
ff_cbs_fragment_free(&ctx->fragment);
ff_cbs_close(&ctx->cbc);
}
static int trace_headers(AVBSFContext *bsf, AVPacket *pkt)
{
TraceHeadersContext *ctx = bsf->priv_data;
CodedBitstreamFragment *frag = &ctx->fragment;
char tmp[256] = { 0 };
int err;
err = ff_bsf_get_packet_ref(bsf, pkt);
if (err < 0)
return err;
if (pkt->flags & AV_PKT_FLAG_KEY)
av_strlcat(tmp, ", key frame", sizeof(tmp));
if (pkt->flags & AV_PKT_FLAG_CORRUPT)
av_strlcat(tmp, ", corrupt", sizeof(tmp));
if (pkt->pts != AV_NOPTS_VALUE)
av_strlcatf(tmp, sizeof(tmp), ", pts %"PRId64, pkt->pts);
else
av_strlcat(tmp, ", no pts", sizeof(tmp));
if (pkt->dts != AV_NOPTS_VALUE)
av_strlcatf(tmp, sizeof(tmp), ", dts %"PRId64, pkt->dts);
else
av_strlcat(tmp, ", no dts", sizeof(tmp));
if (pkt->duration > 0)
av_strlcatf(tmp, sizeof(tmp), ", duration %"PRId64, pkt->duration);
av_log(bsf, AV_LOG_INFO, "Packet: %d bytes%s.\n", pkt->size, tmp);
if (av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, NULL)) {
av_log(bsf, AV_LOG_INFO, "Side data:\n");
err = ff_cbs_read_packet_side_data(ctx->cbc, frag, pkt);
ff_cbs_fragment_reset(frag);
if (err < 0) {
av_packet_unref(pkt);
return err;
}
av_log(bsf, AV_LOG_INFO, "Payload:\n");
}
err = ff_cbs_read_packet(ctx->cbc, frag, pkt);
ff_cbs_fragment_reset(frag);
if (err < 0)
av_packet_unref(pkt);
return err;
}
const FFBitStreamFilter ff_trace_headers_bsf = {
.p.name = "trace_headers",
.p.codec_ids = ff_cbs_all_codec_ids,
.priv_data_size = sizeof(TraceHeadersContext),
.init = &trace_headers_init,
.close = &trace_headers_close,
.filter = &trace_headers,
};
+182
View File
@@ -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 "bsf.h"
#include "bsf_internal.h"
#include "get_bits.h"
#include "mlp_parse.h"
#include "mlp.h"
typedef struct AccessUnit {
uint8_t bits[4];
uint16_t offset;
uint16_t optional;
} AccessUnit;
typedef struct TrueHDCoreContext {
MLPHeaderInfo hdr;
} TrueHDCoreContext;
static int truehd_core_filter(AVBSFContext *ctx, AVPacket *pkt)
{
TrueHDCoreContext *s = ctx->priv_data;
GetBitContext gbc;
AccessUnit units[MAX_SUBSTREAMS];
int ret, i, last_offset = 0;
int in_size, out_size;
int have_header = 0;
int substream_bytes = 0;
int end;
ret = ff_bsf_get_packet_ref(ctx, pkt);
if (ret < 0)
return ret;
if (pkt->size < 4) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
in_size = (AV_RB16(pkt->data) & 0xFFF) * 2;
if (in_size < 4 || in_size > pkt->size) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
ret = init_get_bits8(&gbc, pkt->data + 4, pkt->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;
}
if (s->hdr.num_substreams > MAX_SUBSTREAMS) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
for (i = 0; i < s->hdr.num_substreams; i++) {
for (int j = 0; j < 4; j++)
units[i].bits[j] = get_bits1(&gbc);
units[i].offset = get_bits(&gbc, 12);
if (i < 3) {
last_offset = units[i].offset * 2;
substream_bytes += 2;
}
if (units[i].bits[0]) {
units[i].optional = get_bits(&gbc, 16);
if (i < 3)
substream_bytes += 2;
}
}
end = get_bits_count(&gbc) >> 3;
out_size = end + 4 + last_offset;
if (out_size < in_size) {
int bpos = 0, reduce = end - have_header * 28 - substream_bytes;
uint16_t parity_nibble, dts = AV_RB16(pkt->data + 2);
uint16_t auheader;
uint8_t header[28];
av_assert1(reduce >= 0 && reduce % 2 == 0);
if (have_header) {
memcpy(header, pkt->data + 4, 28);
header[16] = (header[16] & 0x0c) | (FFMIN(s->hdr.num_substreams, 3) << 4);
header[17] &= 0x7f;
header[25] &= 0xfe;
AV_WL16(header + 26, ff_mlp_checksum16(header, 26));
}
pkt->data += reduce;
out_size -= reduce;
pkt->size = out_size;
ret = av_packet_make_writable(pkt);
if (ret < 0)
goto fail;
AV_WB16(pkt->data + 2, dts);
parity_nibble = dts;
parity_nibble ^= out_size / 2;
for (i = 0; i < FFMIN(s->hdr.num_substreams, 3); i++) {
uint16_t substr_hdr = 0;
substr_hdr |= (units[i].bits[0] << 15);
substr_hdr |= (units[i].bits[1] << 14);
substr_hdr |= (units[i].bits[2] << 13);
substr_hdr |= (units[i].bits[3] << 12);
substr_hdr |= units[i].offset;
AV_WB16(pkt->data + have_header * 28 + 4 + bpos, substr_hdr);
parity_nibble ^= substr_hdr;
bpos += 2;
if (units[i].bits[0]) {
AV_WB16(pkt->data + have_header * 28 + 4 + bpos, units[i].optional);
parity_nibble ^= units[i].optional;
bpos += 2;
}
}
parity_nibble ^= parity_nibble >> 8;
parity_nibble ^= parity_nibble >> 4;
parity_nibble &= 0xF;
auheader = (parity_nibble ^ 0xF) << 12;
auheader |= (out_size / 2) & 0x0fff;
AV_WB16(pkt->data, auheader);
if (have_header)
memcpy(pkt->data + 4, header, 28);
}
fail:
if (ret < 0)
av_packet_unref(pkt);
return ret;
}
static void truehd_core_flush(AVBSFContext *ctx)
{
TrueHDCoreContext *s = ctx->priv_data;
memset(&s->hdr, 0, sizeof(s->hdr));
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_TRUEHD, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_truehd_core_bsf = {
.p.name = "truehd_core",
.p.codec_ids = codec_ids,
.priv_data_size = sizeof(TrueHDCoreContext),
.filter = truehd_core_filter,
.flush = truehd_core_flush,
};
+146
View File
@@ -0,0 +1,146 @@
/*
* 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 "libavutil/log.h"
#include "libavutil/opt.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "cbs.h"
#include "cbs_bsf.h"
#include "cbs_vp9.h"
typedef struct VP9MetadataContext {
CBSBSFContext common;
int color_space;
int color_range;
int color_warnings;
} VP9MetadataContext;
static int vp9_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt,
CodedBitstreamFragment *frag)
{
VP9MetadataContext *ctx = bsf->priv_data;
int i;
for (i = 0; i < frag->nb_units; i++) {
VP9RawFrame *frame = frag->units[i].content;
VP9RawFrameHeader *header = &frame->header;
int profile = (header->profile_high_bit << 1) + header->profile_low_bit;
if (header->frame_type == VP9_KEY_FRAME ||
header->intra_only && profile > 0) {
if (ctx->color_space >= 0) {
if (!(profile & 1) && ctx->color_space == VP9_CS_RGB) {
if (!(ctx->color_warnings & 2)) {
av_log(bsf, AV_LOG_WARNING, "Warning: RGB "
"incompatible with profiles 0 and 2.\n");
ctx->color_warnings |= 2;
}
} else
header->color_space = ctx->color_space;
}
if (ctx->color_range >= 0)
header->color_range = ctx->color_range;
if (header->color_space == VP9_CS_RGB) {
if (!(ctx->color_warnings & 1) && !header->color_range) {
av_log(bsf, AV_LOG_WARNING, "Warning: Color space RGB "
"implicitly sets color range to PC range.\n");
ctx->color_warnings |= 1;
}
header->color_range = 1;
}
} else if (!(ctx->color_warnings & 4) && header->intra_only && !profile &&
ctx->color_space >= 0 && ctx->color_space != VP9_CS_BT_601) {
av_log(bsf, AV_LOG_WARNING, "Warning: Intra-only frames in "
"profile 0 are automatically BT.601.\n");
ctx->color_warnings |= 4;
}
}
return 0;
}
static const CBSBSFType vp9_metadata_type = {
.codec_id = AV_CODEC_ID_VP9,
.fragment_name = "superframe",
.unit_name = "frame",
.update_fragment = &vp9_metadata_update_fragment,
};
static int vp9_metadata_init(AVBSFContext *bsf)
{
return ff_cbs_bsf_generic_init(bsf, &vp9_metadata_type);
}
#define OFFSET(x) offsetof(VP9MetadataContext, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
static const AVOption vp9_metadata_options[] = {
{ "color_space", "Set colour space (section 7.2.2)",
OFFSET(color_space), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, VP9_CS_RGB, FLAGS, .unit = "cs" },
{ "unknown", "Unknown/unspecified", 0, AV_OPT_TYPE_CONST,
{ .i64 = VP9_CS_UNKNOWN }, .flags = FLAGS, .unit = "cs" },
{ "bt601", "ITU-R BT.601-7", 0, AV_OPT_TYPE_CONST,
{ .i64 = VP9_CS_BT_601 }, .flags = FLAGS, .unit = "cs" },
{ "bt709", "ITU-R BT.709-6", 0, AV_OPT_TYPE_CONST,
{ .i64 = VP9_CS_BT_709 }, .flags = FLAGS, .unit = "cs" },
{ "smpte170", "SMPTE-170", 0, AV_OPT_TYPE_CONST,
{ .i64 = VP9_CS_SMPTE_170 }, .flags = FLAGS, .unit = "cs" },
{ "smpte240", "SMPTE-240", 0, AV_OPT_TYPE_CONST,
{ .i64 = VP9_CS_SMPTE_240 }, .flags = FLAGS, .unit = "cs" },
{ "bt2020", "ITU-R BT.2020-2", 0, AV_OPT_TYPE_CONST,
{ .i64 = VP9_CS_BT_2020 }, .flags = FLAGS, .unit = "cs" },
{ "rgb", "sRGB / IEC 61966-2-1", 0, AV_OPT_TYPE_CONST,
{ .i64 = VP9_CS_RGB }, .flags = FLAGS, .unit = "cs" },
{ "color_range", "Set colour range (section 7.2.2)",
OFFSET(color_range), AV_OPT_TYPE_INT,
{ .i64 = -1 }, -1, 1, FLAGS, .unit = "cr" },
{ "tv", "TV (limited) range", 0, AV_OPT_TYPE_CONST,
{ .i64 = 0 }, .flags = FLAGS, .unit = "cr" },
{ "pc", "PC (full) range", 0, AV_OPT_TYPE_CONST,
{ .i64 = 1 }, .flags = FLAGS, .unit = "cr" },
{ NULL }
};
static const AVClass vp9_metadata_class = {
.class_name = "vp9_metadata_bsf",
.item_name = av_default_item_name,
.option = vp9_metadata_options,
.version = LIBAVUTIL_VERSION_INT,
};
static const enum AVCodecID vp9_metadata_codec_ids[] = {
AV_CODEC_ID_VP9, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_vp9_metadata_bsf = {
.p.name = "vp9_metadata",
.p.codec_ids = vp9_metadata_codec_ids,
.p.priv_class = &vp9_metadata_class,
.priv_data_size = sizeof(VP9MetadataContext),
.init = &vp9_metadata_init,
.close = &ff_cbs_bsf_generic_close,
.filter = &ff_cbs_bsf_generic_filter,
};
+414
View File
@@ -0,0 +1,414 @@
/*
* 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 "libavutil/avassert.h"
#include "libavutil/intmath.h"
#include "libavutil/log.h"
#include "libavutil/mem.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "get_bits.h"
#include "put_bits.h"
#define FRAME_SLOTS 8
typedef struct VP9RawReorderFrame {
AVPacket *packet;
int needs_output;
int needs_display;
int64_t pts;
int64_t sequence;
unsigned int slots;
unsigned int profile;
unsigned int show_existing_frame;
unsigned int frame_to_show;
unsigned int frame_type;
unsigned int show_frame;
unsigned int refresh_frame_flags;
} VP9RawReorderFrame;
typedef struct VP9RawReorderContext {
int64_t sequence;
VP9RawReorderFrame *slot[FRAME_SLOTS];
VP9RawReorderFrame *next_frame;
} VP9RawReorderContext;
static void vp9_raw_reorder_frame_free(VP9RawReorderFrame **frame)
{
if (*frame)
av_packet_free(&(*frame)->packet);
av_freep(frame);
}
static void vp9_raw_reorder_clear_slot(VP9RawReorderContext *ctx, int s)
{
if (ctx->slot[s]) {
ctx->slot[s]->slots &= ~(1 << s);
if (ctx->slot[s]->slots == 0)
vp9_raw_reorder_frame_free(&ctx->slot[s]);
else
ctx->slot[s] = NULL;
}
}
static int vp9_raw_reorder_frame_parse(AVBSFContext *bsf, VP9RawReorderFrame *frame)
{
GetBitContext bc;
int err;
unsigned int frame_marker;
unsigned int profile_low_bit, profile_high_bit, reserved_zero;
unsigned int error_resilient_mode;
unsigned int frame_sync_code;
err = init_get_bits(&bc, frame->packet->data, 8 * frame->packet->size);
if (err)
return err;
frame_marker = get_bits(&bc, 2);
if (frame_marker != 2) {
av_log(bsf, AV_LOG_ERROR, "Invalid frame marker: %u.\n",
frame_marker);
return AVERROR_INVALIDDATA;
}
profile_low_bit = get_bits1(&bc);
profile_high_bit = get_bits1(&bc);
frame->profile = (profile_high_bit << 1) | profile_low_bit;
if (frame->profile == 3) {
reserved_zero = get_bits1(&bc);
if (reserved_zero != 0) {
av_log(bsf, AV_LOG_ERROR, "Profile reserved_zero bit set: "
"unsupported profile or invalid bitstream.\n");
return AVERROR_INVALIDDATA;
}
}
frame->show_existing_frame = get_bits1(&bc);
if (frame->show_existing_frame) {
frame->frame_to_show = get_bits(&bc, 3);
return 0;
}
frame->frame_type = get_bits1(&bc);
frame->show_frame = get_bits1(&bc);
error_resilient_mode = get_bits1(&bc);
if (frame->frame_type == 0) {
frame_sync_code = get_bits(&bc, 24);
if (frame_sync_code != 0x498342) {
av_log(bsf, AV_LOG_ERROR, "Invalid frame sync code: %06x.\n",
frame_sync_code);
return AVERROR_INVALIDDATA;
}
frame->refresh_frame_flags = 0xff;
} else {
unsigned int intra_only;
if (frame->show_frame == 0)
intra_only = get_bits1(&bc);
else
intra_only = 0;
if (error_resilient_mode == 0) {
// reset_frame_context
skip_bits(&bc, 2);
}
if (intra_only) {
frame_sync_code = get_bits(&bc, 24);
if (frame_sync_code != 0x498342) {
av_log(bsf, AV_LOG_ERROR, "Invalid frame sync code: "
"%06x.\n", frame_sync_code);
return AVERROR_INVALIDDATA;
}
if (frame->profile > 0) {
unsigned int color_space;
if (frame->profile >= 2) {
// ten_or_twelve_bit
skip_bits(&bc, 1);
}
color_space = get_bits(&bc, 3);
if (color_space != 7 /* CS_RGB */) {
// color_range
skip_bits(&bc, 1);
if (frame->profile == 1 || frame->profile == 3) {
// subsampling
skip_bits(&bc, 3);
}
} else {
if (frame->profile == 1 || frame->profile == 3)
skip_bits(&bc, 1);
}
}
frame->refresh_frame_flags = get_bits(&bc, 8);
} else {
frame->refresh_frame_flags = get_bits(&bc, 8);
}
}
return 0;
}
static int vp9_raw_reorder_make_output(AVBSFContext *bsf,
AVPacket *out,
VP9RawReorderFrame *last_frame)
{
VP9RawReorderContext *ctx = bsf->priv_data;
VP9RawReorderFrame *next_output = last_frame,
*next_display = last_frame, *frame;
int s, err;
for (s = 0; s < FRAME_SLOTS; s++) {
frame = ctx->slot[s];
if (!frame)
continue;
if (frame->needs_output && (!next_output ||
frame->sequence < next_output->sequence))
next_output = frame;
if (frame->needs_display && (!next_display ||
frame->pts < next_display->pts))
next_display = frame;
}
if (!next_output && !next_display)
return AVERROR_EOF;
if (!next_display || (next_output &&
next_output->sequence < next_display->sequence))
frame = next_output;
else
frame = next_display;
if (frame->needs_output && frame->needs_display &&
next_output == next_display) {
av_log(bsf, AV_LOG_DEBUG, "Output and display frame "
"%"PRId64" (%"PRId64") in order.\n",
frame->sequence, frame->pts);
av_packet_move_ref(out, frame->packet);
frame->needs_output = frame->needs_display = 0;
} else if (frame->needs_output) {
if (frame->needs_display) {
av_log(bsf, AV_LOG_DEBUG, "Output frame %"PRId64" "
"(%"PRId64") for later display.\n",
frame->sequence, frame->pts);
} else {
av_log(bsf, AV_LOG_DEBUG, "Output unshown frame "
"%"PRId64" (%"PRId64") to keep order.\n",
frame->sequence, frame->pts);
}
av_packet_move_ref(out, frame->packet);
out->pts = out->dts;
frame->needs_output = 0;
} else {
PutBitContext pb;
av_assert0(!frame->needs_output && frame->needs_display);
if (frame->slots == 0) {
av_log(bsf, AV_LOG_ERROR, "Attempting to display frame "
"which is no longer available?\n");
frame->needs_display = 0;
return AVERROR_INVALIDDATA;
}
s = ff_ctz(frame->slots);
av_assert0(s < FRAME_SLOTS);
av_log(bsf, AV_LOG_DEBUG, "Display frame %"PRId64" "
"(%"PRId64") from slot %d.\n",
frame->sequence, frame->pts, s);
err = av_new_packet(out, 2);
if (err < 0)
return err;
init_put_bits(&pb, out->data, 2);
// frame_marker
put_bits(&pb, 2, 2);
// profile_low_bit
put_bits(&pb, 1, frame->profile & 1);
// profile_high_bit
put_bits(&pb, 1, (frame->profile >> 1) & 1);
if (frame->profile == 3) {
// reserved_zero
put_bits(&pb, 1, 0);
}
// show_existing_frame
put_bits(&pb, 1, 1);
// frame_to_show_map_idx
put_bits(&pb, 3, s);
while (put_bits_count(&pb) < 16)
put_bits(&pb, 1, 0);
flush_put_bits(&pb);
out->pts = out->dts = frame->pts;
frame->needs_display = 0;
}
return 0;
}
static int vp9_raw_reorder_filter(AVBSFContext *bsf, AVPacket *out)
{
VP9RawReorderContext *ctx = bsf->priv_data;
VP9RawReorderFrame *frame;
AVPacket *in;
int err, s;
if (ctx->next_frame) {
frame = ctx->next_frame;
} else {
err = ff_bsf_get_packet(bsf, &in);
if (err < 0) {
if (err == AVERROR_EOF)
return vp9_raw_reorder_make_output(bsf, out, NULL);
return err;
}
if (!in->size) {
av_packet_free(&in);
return AVERROR_INVALIDDATA;
}
if ((in->data[in->size - 1] & 0xe0) == 0xc0) {
av_log(bsf, AV_LOG_ERROR, "Input in superframes is not "
"supported.\n");
av_packet_free(&in);
return AVERROR(ENOSYS);
}
frame = av_mallocz(sizeof(*frame));
if (!frame) {
av_packet_free(&in);
return AVERROR(ENOMEM);
}
frame->packet = in;
frame->pts = in->pts;
frame->sequence = ++ctx->sequence;
err = vp9_raw_reorder_frame_parse(bsf, frame);
if (err) {
av_log(bsf, AV_LOG_ERROR, "Failed to parse input "
"frame: %d.\n", err);
goto fail;
}
frame->needs_output = 1;
frame->needs_display = frame->pts != AV_NOPTS_VALUE;
if (frame->show_existing_frame)
av_log(bsf, AV_LOG_DEBUG, "Show frame %"PRId64" "
"(%"PRId64"): show %u.\n", frame->sequence,
frame->pts, frame->frame_to_show);
else
av_log(bsf, AV_LOG_DEBUG, "New frame %"PRId64" "
"(%"PRId64"): type %u show %u refresh %02x.\n",
frame->sequence, frame->pts, frame->frame_type,
frame->show_frame, frame->refresh_frame_flags);
ctx->next_frame = frame;
}
for (s = 0; s < FRAME_SLOTS; s++) {
if (!(frame->refresh_frame_flags & (1 << s)))
continue;
if (ctx->slot[s] && ctx->slot[s]->needs_display &&
ctx->slot[s]->slots == (1 << s)) {
// We are overwriting this slot, which is last reference
// to the frame previously present in it. In order to be
// a valid stream, that frame must already have been
// displayed before the pts of the current frame.
err = vp9_raw_reorder_make_output(bsf, out, ctx->slot[s]);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to create "
"output overwriting slot %d: %d.\n",
s, err);
// Clear the slot anyway, so we don't end up
// in an infinite loop.
vp9_raw_reorder_clear_slot(ctx, s);
return AVERROR_INVALIDDATA;
}
return 0;
}
vp9_raw_reorder_clear_slot(ctx, s);
}
for (s = 0; s < FRAME_SLOTS; s++) {
if (!(frame->refresh_frame_flags & (1 << s)))
continue;
ctx->slot[s] = frame;
}
frame->slots = frame->refresh_frame_flags;
if (!frame->refresh_frame_flags) {
err = vp9_raw_reorder_make_output(bsf, out, frame);
if (err < 0) {
av_log(bsf, AV_LOG_ERROR, "Failed to create output "
"for transient frame.\n");
ctx->next_frame = NULL;
return AVERROR_INVALIDDATA;
}
if (!frame->needs_display) {
vp9_raw_reorder_frame_free(&frame);
ctx->next_frame = NULL;
}
return 0;
}
ctx->next_frame = NULL;
return AVERROR(EAGAIN);
fail:
vp9_raw_reorder_frame_free(&frame);
return err;
}
static av_cold void vp9_raw_reorder_flush_close(AVBSFContext *bsf)
{
VP9RawReorderContext *ctx = bsf->priv_data;
for (int s = 0; s < FRAME_SLOTS; s++)
vp9_raw_reorder_clear_slot(ctx, s);
vp9_raw_reorder_frame_free(&ctx->next_frame);
ctx->sequence = 0;
}
static const enum AVCodecID vp9_raw_reorder_codec_ids[] = {
AV_CODEC_ID_VP9, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_vp9_raw_reorder_bsf = {
.p.name = "vp9_raw_reorder",
.p.codec_ids = vp9_raw_reorder_codec_ids,
.priv_data_size = sizeof(VP9RawReorderContext),
.filter = &vp9_raw_reorder_filter,
.flush = &vp9_raw_reorder_flush_close,
.close = &vp9_raw_reorder_flush_close,
};
+231
View File
@@ -0,0 +1,231 @@
/*
* Vp9 invisible (alt-ref) frame to superframe merge bitstream filter
* Copyright (c) 2016 Ronald S. Bultje <rsbultje@gmail.com>
*
* 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 "libavutil/avassert.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "get_bits.h"
#define MAX_CACHE 8
typedef struct VP9BSFContext {
int n_cache;
AVPacket *cache[MAX_CACHE];
} VP9BSFContext;
static void stats(AVPacket * const *in, int n_in,
unsigned *_max, unsigned *_sum)
{
int n;
unsigned max = 0, sum = 0;
for (n = 0; n < n_in; n++) {
unsigned sz = in[n]->size;
if (sz > max)
max = sz;
sum += sz;
}
*_max = max;
*_sum = sum;
}
static int merge_superframe(AVPacket * const *in, int n_in, AVPacket *out)
{
unsigned max, sum, mag, marker, n, sz;
uint8_t *ptr;
int res;
stats(in, n_in, &max, &sum);
mag = av_log2(max) >> 3;
marker = 0xC0 + (mag << 3) + (n_in - 1);
sz = sum + 2 + (mag + 1) * n_in;
res = av_new_packet(out, sz);
if (res < 0)
return res;
ptr = out->data;
for (n = 0; n < n_in; n++) {
memcpy(ptr, in[n]->data, in[n]->size);
ptr += in[n]->size;
}
#define wloop(mag, wr) \
do { \
for (n = 0; n < n_in; n++) { \
wr; \
ptr += mag + 1; \
} \
} while (0)
// write superframe with marker 110[mag:2][nframes:3]
*ptr++ = marker;
switch (mag) {
case 0:
wloop(mag, *ptr = in[n]->size);
break;
case 1:
wloop(mag, AV_WL16(ptr, in[n]->size));
break;
case 2:
wloop(mag, AV_WL24(ptr, in[n]->size));
break;
case 3:
wloop(mag, AV_WL32(ptr, in[n]->size));
break;
}
*ptr++ = marker;
av_assert0(ptr == &out->data[out->size]);
return 0;
}
static int vp9_superframe_filter(AVBSFContext *ctx, AVPacket *pkt)
{
GetBitContext gb;
VP9BSFContext *s = ctx->priv_data;
int res, invisible, profile, marker, uses_superframe_syntax = 0, n;
res = ff_bsf_get_packet_ref(ctx, pkt);
if (res < 0)
return res;
if (!pkt->size) {
/* In case the cache is empty we can pass side-data-only packets
* through unchanged. Otherwise, such a packet makes no sense. */
if (!s->n_cache)
return 0;
res = AVERROR_INVALIDDATA;
goto done;
}
marker = pkt->data[pkt->size - 1];
if ((marker & 0xe0) == 0xc0) {
int nbytes = 1 + ((marker >> 3) & 0x3);
int n_frames = 1 + (marker & 0x7), idx_sz = 2 + n_frames * nbytes;
uses_superframe_syntax = pkt->size >= idx_sz && pkt->data[pkt->size - idx_sz] == marker;
}
if ((res = init_get_bits8(&gb, pkt->data, pkt->size)) < 0)
goto done;
get_bits(&gb, 2); // frame marker
profile = get_bits1(&gb);
profile |= get_bits1(&gb) << 1;
if (profile == 3) profile += get_bits1(&gb);
if (get_bits1(&gb)) {
invisible = 0;
} else {
get_bits1(&gb); // keyframe
invisible = !get_bits1(&gb);
}
if (uses_superframe_syntax && s->n_cache > 0) {
av_log(ctx, AV_LOG_ERROR,
"Mixing of superframe syntax and naked VP9 frames not supported\n");
res = AVERROR(ENOSYS);
goto done;
} else if ((!invisible || uses_superframe_syntax) && !s->n_cache) {
// passthrough
return 0;
} else if (s->n_cache + 1 >= MAX_CACHE) {
av_log(ctx, AV_LOG_ERROR,
"Too many invisible frames\n");
res = AVERROR_INVALIDDATA;
goto done;
}
av_packet_move_ref(s->cache[s->n_cache++], pkt);
if (invisible) {
return AVERROR(EAGAIN);
}
av_assert0(s->n_cache > 0);
// build superframe
if ((res = merge_superframe(s->cache, s->n_cache, pkt)) < 0)
goto done;
res = av_packet_copy_props(pkt, s->cache[s->n_cache - 1]);
if (res < 0)
goto done;
for (n = 0; n < s->n_cache; n++)
av_packet_unref(s->cache[n]);
s->n_cache = 0;
done:
if (res < 0)
av_packet_unref(pkt);
return res;
}
static int vp9_superframe_init(AVBSFContext *ctx)
{
VP9BSFContext *s = ctx->priv_data;
int n;
// alloc cache packets
for (n = 0; n < MAX_CACHE; n++) {
s->cache[n] = av_packet_alloc();
if (!s->cache[n])
return AVERROR(ENOMEM);
}
return 0;
}
static void vp9_superframe_flush(AVBSFContext *ctx)
{
VP9BSFContext *s = ctx->priv_data;
int n;
// unref cached data
for (n = 0; n < s->n_cache; n++)
av_packet_unref(s->cache[n]);
s->n_cache = 0;
}
static void vp9_superframe_close(AVBSFContext *ctx)
{
VP9BSFContext *s = ctx->priv_data;
int n;
// free cached data
for (n = 0; n < MAX_CACHE; n++)
av_packet_free(&s->cache[n]);
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_VP9, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_vp9_superframe_bsf = {
.p.name = "vp9_superframe",
.p.codec_ids = codec_ids,
.priv_data_size = sizeof(VP9BSFContext),
.filter = vp9_superframe_filter,
.init = vp9_superframe_init,
.flush = vp9_superframe_flush,
.close = vp9_superframe_close,
};
@@ -0,0 +1,170 @@
/*
* 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
*/
/**
* @file
* This bitstream filter splits VP9 superframes into packets containing
* just one frame.
*/
#include <stddef.h>
#include "bsf.h"
#include "bsf_internal.h"
#include "bytestream.h"
#include "get_bits.h"
typedef struct VP9SFSplitContext {
AVPacket *buffer_pkt;
int nb_frames;
int next_frame;
size_t next_frame_offset;
int sizes[8];
} VP9SFSplitContext;
static int vp9_superframe_split_filter(AVBSFContext *ctx, AVPacket *out)
{
VP9SFSplitContext *s = ctx->priv_data;
AVPacket *in;
int i, j, ret, marker;
int is_superframe = !!s->buffer_pkt->data;
if (!s->buffer_pkt->data) {
ret = ff_bsf_get_packet_ref(ctx, s->buffer_pkt);
if (ret < 0)
return ret;
in = s->buffer_pkt;
if (!in->size)
goto passthrough;
marker = in->data[in->size - 1];
if ((marker & 0xe0) == 0xc0) {
int length_size = 1 + ((marker >> 3) & 0x3);
int nb_frames = 1 + (marker & 0x7);
int idx_size = 2 + nb_frames * length_size;
if (in->size >= idx_size && in->data[in->size - idx_size] == marker) {
GetByteContext bc;
int64_t total_size = 0;
bytestream2_init(&bc, in->data + in->size + 1 - idx_size,
nb_frames * length_size);
for (i = 0; i < nb_frames; i++) {
int frame_size = 0;
for (j = 0; j < length_size; j++)
frame_size |= bytestream2_get_byte(&bc) << (j * 8);
total_size += frame_size;
if (frame_size <= 0 || total_size > in->size - idx_size) {
av_log(ctx, AV_LOG_ERROR,
"Invalid frame size in a superframe: %d\n", frame_size);
ret = AVERROR(EINVAL);
goto fail;
}
s->sizes[i] = frame_size;
}
s->nb_frames = nb_frames;
s->next_frame = 0;
s->next_frame_offset = 0;
is_superframe = 1;
}
}
}
if (is_superframe) {
GetBitContext gb;
int profile, invisible = 0;
ret = av_packet_ref(out, s->buffer_pkt);
if (ret < 0)
goto fail;
out->data += s->next_frame_offset;
out->size = s->sizes[s->next_frame];
s->next_frame_offset += out->size;
s->next_frame++;
if (s->next_frame >= s->nb_frames)
av_packet_unref(s->buffer_pkt);
ret = init_get_bits8(&gb, out->data, out->size);
if (ret < 0)
goto fail;
get_bits(&gb, 2); // frame_marker
profile = get_bits1(&gb);
profile |= get_bits1(&gb) << 1;
if (profile == 3)
get_bits1(&gb);
if (!get_bits1(&gb)) {
get_bits1(&gb);
invisible = !get_bits1(&gb);
}
if (invisible)
out->pts = AV_NOPTS_VALUE;
} else {
passthrough:
av_packet_move_ref(out, s->buffer_pkt);
}
return 0;
fail:
if (ret < 0)
av_packet_unref(out);
av_packet_unref(s->buffer_pkt);
return ret;
}
static int vp9_superframe_split_init(AVBSFContext *ctx)
{
VP9SFSplitContext *s = ctx->priv_data;
s->buffer_pkt = av_packet_alloc();
if (!s->buffer_pkt)
return AVERROR(ENOMEM);
return 0;
}
static void vp9_superframe_split_flush(AVBSFContext *ctx)
{
VP9SFSplitContext *s = ctx->priv_data;
av_packet_unref(s->buffer_pkt);
}
static void vp9_superframe_split_uninit(AVBSFContext *ctx)
{
VP9SFSplitContext *s = ctx->priv_data;
av_packet_free(&s->buffer_pkt);
}
const FFBitStreamFilter ff_vp9_superframe_split_bsf = {
.p.name = "vp9_superframe_split",
.p.codec_ids = (const enum AVCodecID []){ AV_CODEC_ID_VP9, AV_CODEC_ID_NONE },
.priv_data_size = sizeof(VP9SFSplitContext),
.init = vp9_superframe_split_init,
.flush = vp9_superframe_split_flush,
.close = vp9_superframe_split_uninit,
.filter = vp9_superframe_split_filter,
};
+328
View File
@@ -0,0 +1,328 @@
/*
* H.266/VVC MP4 to Annex B byte stream format filter
* Copyright (c) 2022, Thomas Siedel
*
* 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 <string.h>
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "bsf.h"
#include "bsf_internal.h"
#include "bytestream.h"
#include "defs.h"
#include "vvc.h"
#define MIN_VVCC_LENGTH 23
typedef struct VVCBSFContext {
uint8_t length_size;
int extradata_parsed;
} VVCBSFContext;
static int vvc_extradata_to_annexb(AVBSFContext *ctx)
{
GetByteContext gb;
int length_size, num_arrays, i, j;
int ret = 0;
int temp = 0;
int ptl_present;
uint8_t *new_extradata = NULL;
size_t new_extradata_size = 0;
int max_picture_width = 0;
int max_picture_height = 0;
int avg_frame_rate = 0;
bytestream2_init(&gb, ctx->par_in->extradata, ctx->par_in->extradata_size);
temp = bytestream2_get_byte(&gb);
length_size = ((temp & 6) >> 1) + 1;
ptl_present = temp & 1;
if (ptl_present) {
int num_bytes_constraint_info;
int general_profile_idc;
int general_tier_flag;
int general_level_idc;
int ptl_frame_only_constraint_flag;
int ptl_multi_layer_enabled_flag;
int ptl_num_sub_profiles;
int temp3, temp4, temp5;
int temp2 = bytestream2_get_be16(&gb);
int ols_idx = (temp2 >> 7) & 0x1ff;
int num_sublayers = (temp2 >> 4) & 0x7;
int constant_frame_rate = (temp2 >> 2) & 0x3;
int chroma_format_idc = temp2 & 0x3;
int bit_depth_minus8 = (bytestream2_get_byte(&gb) >> 5) & 0x7;
av_log(ctx, AV_LOG_DEBUG,
"bit_depth_minus8 %d chroma_format_idc %d\n", bit_depth_minus8,
chroma_format_idc);
av_log(ctx, AV_LOG_DEBUG, "constant_frame_rate %d, ols_idx %d\n",
constant_frame_rate, ols_idx);
// VvcPTLRecord(num_sublayers) native_ptl
temp3 = bytestream2_get_byte(&gb);
num_bytes_constraint_info = (temp3) & 0x3f;
temp4 = bytestream2_get_byte(&gb);
general_profile_idc = (temp4 >> 1) & 0x7f;
general_tier_flag = (temp4) & 1;
general_level_idc = bytestream2_get_byte(&gb);
av_log(ctx, AV_LOG_DEBUG,
"general_profile_idc %d, general_tier_flag %d, general_level_idc %d, num_sublayers %d num_bytes_constraint_info %d\n",
general_profile_idc, general_tier_flag, general_level_idc,
num_sublayers, num_bytes_constraint_info);
temp5 = bytestream2_get_byte(&gb);
ptl_frame_only_constraint_flag = (temp5 >> 7) & 0x1;
ptl_multi_layer_enabled_flag = (temp5 >> 6) & 0x1;
for (i = 0; i < num_bytes_constraint_info - 1; i++) {
// unsigned int(8*num_bytes_constraint_info - 2) general_constraint_info;
bytestream2_get_byte(&gb);
}
av_log(ctx, AV_LOG_DEBUG,
"ptl_multi_layer_enabled_flag %d, ptl_frame_only_constraint_flag %d\n",
ptl_multi_layer_enabled_flag, ptl_frame_only_constraint_flag);
if (num_sublayers > 1) {
int temp6 = bytestream2_get_byte(&gb);
uint8_t ptl_sublayer_level_present_flag[8] = { 0 };
//uint8_t sublayer_level_idc[8] = {0};
for (i = num_sublayers - 2; i >= 0; i--) {
ptl_sublayer_level_present_flag[i] =
(temp6 >> (7 - (num_sublayers - 2 - i))) & 0x01;
}
// for (j=num_sublayers; j<=8 && num_sublayers > 1; j++)
// bit(1) ptl_reserved_zero_bit = 0;
for (i = num_sublayers - 2; i >= 0; i--) {
if (ptl_sublayer_level_present_flag[i]) {
//sublayer_level_idc[i] = bytestream2_get_byte(&gb);
}
}
}
ptl_num_sub_profiles = bytestream2_get_byte(&gb);
for (j = 0; j < ptl_num_sub_profiles; j++) {
// unsigned int(32) general_sub_profile_idc[j];
bytestream2_get_be16(&gb);
bytestream2_get_be16(&gb);
}
max_picture_width = bytestream2_get_be16(&gb); // unsigned_int(16) max_picture_width;
max_picture_height = bytestream2_get_be16(&gb); // unsigned_int(16) max_picture_height;
avg_frame_rate = bytestream2_get_be16(&gb); // unsigned int(16) avg_frame_rate; }
av_log(ctx, AV_LOG_DEBUG,
"max_picture_width %d, max_picture_height %d, avg_frame_rate %d\n",
max_picture_width, max_picture_height, avg_frame_rate);
}
num_arrays = bytestream2_get_byte(&gb);
for (i = 0; i < num_arrays; i++) {
int cnt;
int type = bytestream2_get_byte(&gb) & 0x1f;
if (type == VVC_OPI_NUT || type == VVC_DCI_NUT)
cnt = 1;
else
cnt = bytestream2_get_be16(&gb);
av_log(ctx, AV_LOG_DEBUG, "nalu_type %d cnt %d\n", type, cnt);
if (!(type == VVC_OPI_NUT || type == VVC_DCI_NUT ||
type == VVC_VPS_NUT || type == VVC_SPS_NUT || type == VVC_PPS_NUT
|| type == VVC_PREFIX_SEI_NUT || type == VVC_SUFFIX_SEI_NUT)) {
av_log(ctx, AV_LOG_ERROR,
"Invalid NAL unit type in extradata: %d\n", type);
ret = AVERROR_INVALIDDATA;
goto fail;
}
for (j = 0; j < cnt; j++) {
const int nalu_len = bytestream2_get_be16(&gb);
if (!nalu_len ||
nalu_len > bytestream2_get_bytes_left(&gb) ||
4 + AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > SIZE_MAX - new_extradata_size) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
ret = av_reallocp(&new_extradata, new_extradata_size + nalu_len + 4
+ AV_INPUT_BUFFER_PADDING_SIZE);
if (ret < 0)
goto fail;
AV_WB32(new_extradata + new_extradata_size, 1); // add the startcode
bytestream2_get_buffer(&gb, new_extradata + new_extradata_size + 4,
nalu_len);
new_extradata_size += 4 + nalu_len;
memset(new_extradata + new_extradata_size, 0,
AV_INPUT_BUFFER_PADDING_SIZE);
}
}
av_freep(&ctx->par_out->extradata);
ctx->par_out->extradata = new_extradata;
ctx->par_out->extradata_size = new_extradata_size;
if (!new_extradata_size)
av_log(ctx, AV_LOG_WARNING, "No parameter sets in the extradata\n");
return length_size;
fail:
av_freep(&new_extradata);
return ret;
}
static int vvc_mp4toannexb_init(AVBSFContext *ctx)
{
VVCBSFContext *s = ctx->priv_data;
int ret;
if (ctx->par_in->extradata_size < MIN_VVCC_LENGTH ||
AV_RB24(ctx->par_in->extradata) == 1 ||
AV_RB32(ctx->par_in->extradata) == 1) {
av_log(ctx, AV_LOG_VERBOSE,
"The input looks like it is Annex B already\n");
} else {
ret = vvc_extradata_to_annexb(ctx);
if (ret < 0)
return ret;
s->length_size = ret;
s->extradata_parsed = 1;
}
return 0;
}
static int vvc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
{
VVCBSFContext *s = ctx->priv_data;
AVPacket *in;
GetByteContext gb;
int is_irap = 0;
int added_extra = 0;
int i, ret = 0;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
if (!s->extradata_parsed) {
av_packet_move_ref(out, in);
av_packet_free(&in);
return 0;
}
bytestream2_init(&gb, in->data, in->size);
/* check if this packet contains an IRAP. The extradata will need to be added before any potential PH_NUT */
while (bytestream2_get_bytes_left(&gb)) {
uint32_t nalu_size = 0;
int nalu_type;
if (bytestream2_get_bytes_left(&gb) < s->length_size) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
for (i = 0; i < s->length_size; i++)
nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
nalu_type = (bytestream2_peek_be16(&gb) >> 3) & 0x1f;
is_irap = nalu_type >= VVC_IDR_W_RADL && nalu_type <= VVC_RSV_IRAP_11;
if (is_irap) {
break;
}
bytestream2_seek(&gb, nalu_size, SEEK_CUR);
}
bytestream2_seek(&gb, 0, SEEK_SET);
while (bytestream2_get_bytes_left(&gb)) {
uint32_t nalu_size = 0;
int nalu_type;
int add_extradata, extra_size, prev_size;
if (bytestream2_get_bytes_left(&gb) < s->length_size) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
for (i = 0; i < s->length_size; i++)
nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb);
if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
nalu_type = (bytestream2_peek_be16(&gb) >> 3) & 0x1f;
/* prepend extradata to IRAP frames */
add_extradata = is_irap && nalu_type != VVC_AUD_NUT && !added_extra;
extra_size = add_extradata * ctx->par_out->extradata_size;
added_extra |= add_extradata;
if (FFMIN(INT_MAX, SIZE_MAX) < 4ULL + nalu_size + extra_size) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
prev_size = out->size;
ret = av_grow_packet(out, 4 + nalu_size + extra_size);
if (ret < 0)
goto fail;
if (extra_size)
memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size);
AV_WB32(out->data + prev_size + extra_size, 1);
bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size,
nalu_size);
}
ret = av_packet_copy_props(out, in);
if (ret < 0)
goto fail;
fail:
if (ret < 0)
av_packet_unref(out);
av_packet_free(&in);
return ret;
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_VVC, AV_CODEC_ID_NONE,
};
const FFBitStreamFilter ff_vvc_mp4toannexb_bsf = {
.p.name = "vvc_mp4toannexb",
.p.codec_ids = codec_ids,
.priv_data_size = sizeof(VVCBSFContext),
.init = vvc_mp4toannexb_init,
.filter = vvc_mp4toannexb_filter,
};