Compare commits

...

24 Commits

Author SHA1 Message Date
James Almer 746650dfe5 Changelog: add entry about tiled HEIF support with the CLI
Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 13:46:36 -03:00
James Almer a979c9b935 tests/ffmpeg: add test for HEIF automatic tile merging
Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer 5ff8395e78 fftools/ffmpeg_demux: create a filtegraph to merge HEIF tiles automatically
Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer 433d18a1d9 fftools/ffmpeg_filter: handle metadata from stream group in relevant the filtergraphs
Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer dec3cc0138 fftools/ffmpeg_filter: allow binding unlabeled filtergraphs
Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer e7cf188bb6 fftools/ffmpeg_filter: reindent after the previous commit
Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer 9dcd25b7cd fftools/ffmpeg_filter: allow removing filtergraphs that contain unbound outputs
Actual practical implementation of the previous commit.

Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer cce85642c9 fftools/ffmpeg_sched: add a function to remove a filtergraph from the scheduler
For the purpose of merging streams in a stream group, a filtergraph can't be
created once we know it will be used. Therefore, allow filtergraphs to be
removed from the scheduler after being added.

Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer c751ad2c36 fftools/ffmpeg_mux_init: allow creating streams from filtergraphs created out of stream groups
Several formats are being designed where more than one independent video or
audio stream within a container are part of what should be a single combined
output. This is the case for HEIF (Defining a grid where the decoded output
of several separate video streams are to be placed to generate a single output
image) and IAMF (Defining audio streams where channels are present in separate
coded stream).

AVStreamGroup was designed and implemented in libavformat to convey this
information, but the actual merging is left to the caller.
This change allows the FFmpeg CLI to take said information, parse it, and
create filtergraphs to merge the streams, making the combined output be usable
automatically further in the process.

Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer 99ec0752d7 fftools/ffmpeg_demux: add InputStreamGroup to store stream groups
Preparatory work for upcoming changes. Make ffmpeg keep track of stream groups
internally.

Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer ba0dc3d49e fftools/ffmpeg_opt: add helpers to match stream groups
Will be used to check for specifiers that match a given stream group and not
a stream within one.

Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer 7b18beb477 fftools: pass global side data through using a separate array
This keeps global and per frame side data clearly separated, and actually
propagates the former as it comes out from the buffersink filter.

Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer 7ac1b410e1 fftools/ffmpeg_filter: fix passing certain parameters to inputs bound to a filtergraph output
Certain parameters, like calculated framerate, are unavailable when connecting
the output of a filtergraph to the input of another.

This fixes command lines like

ffmpeg -lavfi "testsrc=rate=1:duration=1[out0]" -filter_complex "[out0]null[out1]" -map [out1] -y out.png

Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 11:02:01 -03:00
James Almer 6879c8ee5d avcodec/avcodec: add helpers to convert between packet and frame side data
They will be used in following commits.

Signed-off-by: James Almer <jamrial@gmail.com>
2025-10-30 10:54:01 -03:00
Andreas Rheinhardt 0242cb36a5 avcodec/rv34dsp: Reduce size of qpel functions arrays
Only size 16 and 8 are used (and set).

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2025-10-30 12:17:25 +01:00
Gyan Doshi 00c23bafb0 doc/filters: add link to rendered doc of drawvg 2025-10-30 15:35:14 +05:30
Zhao Zhili 82c495fd15 avcodec/hevc: fix false alarm when build with enable-small
profile_name is always NULL with --enable-small, which leading to
a warning message "Unknown profile bitstream".
2025-10-30 09:26:17 +00:00
Andreas Rheinhardt 0f105b96a3 avcodec/x86/hevc/idct: Port ff_hevc_idct_4x4_dc_{8,10,12}_mmxext to SSE2
Practically no change in benchmarks (and in codesize).

hevc_idct_4x4_dc_8_c:                                    7.8 ( 1.00x)
hevc_idct_4x4_dc_8_mmxext:                               6.9 ( 1.14x)
hevc_idct_4x4_dc_8_sse2:                                 6.8 ( 1.15x)
hevc_idct_4x4_dc_10_c:                                   7.9 ( 1.00x)
hevc_idct_4x4_dc_10_mmxext:                              6.9 ( 1.16x)
hevc_idct_4x4_dc_10_sse2:                                6.8 ( 1.16x)
hevc_idct_4x4_dc_12_c:                                   7.8 ( 1.00x)
hevc_idct_4x4_dc_12_mmxext:                              7.0 ( 1.13x)
hevc_idct_4x4_dc_12_sse2:                                6.8 ( 1.15x)

Reviewed-by: James Almer <jamrial@gmail.com>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2025-10-30 08:56:45 +01:00
Michael Niedermayer 909af3a571
avcodec/g723_1enc: Make min_err 64bit
This is intending to fix the case described in https://lists.ffmpeg.org/archives/list/ffmpeg-devel@ffmpeg.org/thread/AAZ7GJPPUJI5SCVTDGJ6QL7UUEP56WOM/
Where FCBParam optim is used uninitialized

a min_err of 1<<30, allows the struct to be never initilialized as all
err (which is int32_t) can be larger than min_err. By increasing min_err
above the int32_t range this is no longer possible

Untested, as i do not have the testcase

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>i
2025-10-30 03:41:24 +01:00
Michael Niedermayer d8ffec5bf9
avcodec/vlc: Clear val8/16 in vlc_multi_gen() by av_mallocz()
Fixes: use of uninitialized memory
Fixes: 427814450/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_MAGICYUV_DEC_fuzzer-646512196065689
Fixes: 445961558/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_UTVIDEO_DEC_fuzzer-5515158672965632

the multi vlc code will otherwise return uninitialized data. Now one can argue that this data should
not be used, but on errors this data can remain ...

Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2025-10-30 02:08:14 +01:00
Michael Niedermayer d03483bd26
avformat/rtpenc_h264_hevc: Check space for nal_length_size in ff_rtp_send_h264_hevc()
Fixes: memcpy with negative size
Fixes: momo_trip-poc/input

Reported-by: Momoko Shiraishi <shiraishi@os.is.s.u-tokyo.ac.jp>
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2025-10-30 01:24:23 +01:00
Lynne afd927e0ca prores_raw: skip frames if the discard flag is set
All other decoders do this.
2025-10-28 23:10:15 +01:00
Lynne 0d11c5c2f9 libplacebo: extend allowed range of color primaries and transfer functions
This was left out of the earlier commits.
2025-10-28 22:19:34 +01:00
Lynne 3d12d5682b doc/APIchanges: add AVCOL_PRI_EXT_BASE and AVCOL_PRI_V_GAMUT
They were left out.
2025-10-28 22:19:34 +01:00
32 changed files with 835 additions and 90 deletions

View File

@ -9,6 +9,7 @@ version <next>:
- MPEG-H 3D Audio decoding via mpeghdec
- D3D12 H.264 encoder
- drawvg filter via libcairo
- ffmpeg CLI tiled HEIF support
version 8.0:

View File

@ -2,8 +2,12 @@ The last version increases of all libraries were on 2025-03-28
API changes, most recent first:
2025-10-30 - xxxxxxxxxx - lavc 62.17.100 - packet.h
Add av_packet_side_data_from_frame() and av_packet_side_data_to_frame().
2025-10-xx - xxxxxxxxxx - lavu 60.16.100 - pixfmt.h
Add AVCOL_TRC_EXT_BASE and AVCOL_TRC_L_LOG.
Add AVCOL_TRC_EXT_BASE, AVCOL_TRC_V_LOG,
AVCOL_PRI_EXT_BASE and AVCOL_PRI_V_GAMUT.
2025-10-xx - xxxxxxxxxx - lavu 60.15.100 - pixfmt.h
Add AV_PIX_FMT_GRAY10MSB, AV_PIX_FMT_GRAY12MSB,

View File

@ -13056,7 +13056,9 @@ Draw vector graphics on top of video frames, by executing a script written in
a custom language called VGS (@emph{Vector Graphics Script}).
The documentation for the language can be found in
@ref{,,drawvg - Language Reference,drawvg-reference}.
@ref{,,drawvg - Language Reference,drawvg-reference}. A version of this reference
with rendered examples can be found at the
@uref{https://ayosec.github.io/ffmpeg-drawvg/playground/docs/langref.html, author's site}.
Graphics are rendered using the @uref{https://cairographics.org/,cario 2D
graphics library}.

View File

@ -1350,6 +1350,77 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec)
return ret;
}
unsigned stream_group_specifier_match(const StreamSpecifier *ss,
const AVFormatContext *s, const AVStreamGroup *stg,
void *logctx)
{
int start_stream_group = 0, nb_stream_groups;
int nb_matched = 0;
if (ss->idx >= 0)
return 0;
switch (ss->stream_list) {
case STREAM_LIST_STREAM_ID:
case STREAM_LIST_ALL:
case STREAM_LIST_PROGRAM:
return 0;
case STREAM_LIST_GROUP_ID:
// <n-th> stream with given ID makes no sense and should be impossible to request
av_assert0(ss->idx < 0);
// return early if we know for sure the stream does not match
if (stg->id != ss->list_id)
return 0;
start_stream_group = stg->index;
nb_stream_groups = stg->index + 1;
break;
case STREAM_LIST_GROUP_IDX:
start_stream_group = ss->list_id >= 0 ? 0 : stg->index;
nb_stream_groups = stg->index + 1;
break;
default: av_assert0(0);
}
for (int i = start_stream_group; i < nb_stream_groups; i++) {
const AVStreamGroup *candidate = s->stream_groups[i];
if (ss->meta_key) {
const AVDictionaryEntry *tag = av_dict_get(candidate->metadata,
ss->meta_key, NULL, 0);
if (!tag)
continue;
if (ss->meta_val && strcmp(tag->value, ss->meta_val))
continue;
}
if (ss->usable_only) {
switch (candidate->type) {
case AV_STREAM_GROUP_PARAMS_TILE_GRID: {
const AVStreamGroupTileGrid *tg = candidate->params.tile_grid;
if (!tg->coded_width || !tg->coded_height || !tg->nb_tiles ||
!tg->width || !tg->height || !tg->nb_tiles)
continue;
break;
}
default:
continue;
}
}
if (ss->disposition &&
(candidate->disposition & ss->disposition) != ss->disposition)
continue;
if (stg == candidate)
return ss->list_id < 0 || ss->list_id == nb_matched;
nb_matched++;
}
return 0;
}
int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id,
AVFormatContext *s, AVStream *st, const AVCodec *codec,
AVDictionary **dst, AVDictionary **opts_used)

View File

@ -158,6 +158,10 @@ unsigned stream_specifier_match(const StreamSpecifier *ss,
const AVFormatContext *s, const AVStream *st,
void *logctx);
unsigned stream_group_specifier_match(const StreamSpecifier *ss,
const AVFormatContext *s, const AVStreamGroup *stg,
void *logctx);
void stream_specifier_uninit(StreamSpecifier *ss);
typedef struct SpecifierOpt {

View File

@ -400,6 +400,7 @@ static void frame_data_free(void *opaque, uint8_t *data)
{
FrameData *fd = (FrameData *)data;
av_frame_side_data_free(&fd->side_data, &fd->nb_side_data);
avcodec_parameters_free(&fd->par_enc);
av_free(data);
@ -429,6 +430,8 @@ static int frame_data_ensure(AVBufferRef **dst, int writable)
memcpy(fd, fd_src, sizeof(*fd));
fd->par_enc = NULL;
fd->side_data = NULL;
fd->nb_side_data = 0;
if (fd_src->par_enc) {
int ret = 0;
@ -444,6 +447,16 @@ static int frame_data_ensure(AVBufferRef **dst, int writable)
}
}
if (fd_src->nb_side_data) {
int ret = clone_side_data(&fd->side_data, &fd->nb_side_data,
fd_src->side_data, fd_src->nb_side_data, 0);
if (ret < 0) {
av_buffer_unref(dst);
av_buffer_unref(&src);
return ret;
}
}
av_buffer_unref(&src);
} else {
fd->dec.frame_num = UINT64_MAX;

View File

@ -136,6 +136,7 @@ typedef struct StreamMap {
int disabled; /* 1 is this mapping is disabled by a negative map */
int file_index;
int stream_index;
int group_index;
char *linklabel; /* name of an output link, for mapping lavfi outputs */
ViewSpecifier vs;
@ -299,6 +300,8 @@ enum OFilterFlags {
// produce 24-bit audio
OFILTER_FLAG_AUDIO_24BIT = (1 << 1),
OFILTER_FLAG_AUTOSCALE = (1 << 2),
OFILTER_FLAG_AUTOROTATE = (1 << 3),
OFILTER_FLAG_CROP = (1 << 4),
};
typedef struct OutputFilterOptions {
@ -332,6 +335,11 @@ typedef struct OutputFilterOptions {
enum AVColorRange color_range;
enum AVAlphaMode alpha_mode;
unsigned crop_top;
unsigned crop_bottom;
unsigned crop_left;
unsigned crop_right;
enum VideoSyncMethod vsync_method;
AVRational frame_rate;
AVRational max_frame_rate;
@ -347,6 +355,9 @@ typedef struct OutputFilterOptions {
const enum AVColorRange *color_ranges;
const enum AVAlphaMode *alpha_modes;
AVFrameSideData **side_data;
int nb_side_data;
// for simple filtergraphs only, view specifier passed
// along to the decoder
const ViewSpecifier *vs;
@ -402,6 +413,11 @@ typedef struct FilterGraph {
OutputFilter **outputs;
int nb_outputs;
// true when the filtergraph is created internally for
// purposes like stream group merging. Meant to be freed
// if unbound.
int is_internal;
const char *graph_desc;
struct AVBPrint graph_print_buf;
} FilterGraph;
@ -491,6 +507,18 @@ typedef struct InputStream {
int nb_filters;
} InputStream;
typedef struct InputStreamGroup {
const AVClass *class;
/* parent source */
struct InputFile *file;
int index;
FilterGraph *fg;
AVStreamGroup *stg;
} InputStreamGroup;
typedef struct InputFile {
const AVClass *class;
@ -512,6 +540,10 @@ typedef struct InputFile {
* if new streams appear dynamically during demuxing */
InputStream **streams;
int nb_streams;
/* stream groups that ffmpeg is aware of; */
InputStreamGroup **stream_groups;
int nb_stream_groups;
} InputFile;
enum forced_keyframes_const {
@ -698,6 +730,9 @@ typedef struct FrameData {
int64_t wallclock[LATENCY_PROBE_NB];
AVCodecParameters *par_enc;
AVFrameSideData **side_data;
int nb_side_data;
} FrameData;
extern InputFile **input_files;
@ -802,7 +837,8 @@ int ofilter_bind_enc(OutputFilter *ofilter,
* @param graph_desc Graph description; an av_malloc()ed string, filtergraph
* takes ownership of it.
*/
int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch);
int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch,
const OutputFilterOptions *opts);
void fg_free(FilterGraph **pfg);
@ -929,6 +965,15 @@ void opt_match_per_stream_int64(void *logctx, const SpecifierOptList *sol,
void opt_match_per_stream_dbl(void *logctx, const SpecifierOptList *sol,
AVFormatContext *fc, AVStream *st, double *out);
void opt_match_per_stream_group_str(void *logctx, const SpecifierOptList *sol,
AVFormatContext *fc, AVStreamGroup *stg, const char **out);
void opt_match_per_stream_group_int(void *logctx, const SpecifierOptList *sol,
AVFormatContext *fc, AVStreamGroup *stg, int *out);
void opt_match_per_stream_group_int64(void *logctx, const SpecifierOptList *sol,
AVFormatContext *fc, AVStreamGroup *stg, int64_t *out);
void opt_match_per_stream_group_dbl(void *logctx, const SpecifierOptList *sol,
AVFormatContext *fc, AVStreamGroup *stg, double *out);
int view_specifier_parse(const char **pspec, ViewSpecifier *vs);
int muxer_thread(void *arg);

View File

@ -104,6 +104,13 @@ typedef struct DemuxStream {
int64_t lag;
} DemuxStream;
typedef struct DemuxStreamGroup {
InputStreamGroup istg;
// name used for logging
char log_name[32];
} DemuxStreamGroup;
typedef struct Demuxer {
InputFile f;
@ -886,6 +893,16 @@ static void ist_free(InputStream **pist)
av_freep(pist);
}
static void istg_free(InputStreamGroup **pistg)
{
InputStreamGroup *istg = *pistg;
if (!istg)
return;
av_freep(pistg);
}
void ifile_close(InputFile **pf)
{
InputFile *f = *pf;
@ -901,6 +918,10 @@ void ifile_close(InputFile **pf)
ist_free(&f->streams[i]);
av_freep(&f->streams);
for (int i = 0; i < f->nb_stream_groups; i++)
istg_free(&f->stream_groups[i]);
av_freep(&f->stream_groups);
avformat_close_input(&f->ctx);
av_packet_free(&d->pkt_heartbeat);
@ -1586,6 +1607,139 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona
return 0;
}
static const char *input_stream_group_item_name(void *obj)
{
const DemuxStreamGroup *dsg = obj;
return dsg->log_name;
}
static const AVClass input_stream_group_class = {
.class_name = "InputStreamGroup",
.version = LIBAVUTIL_VERSION_INT,
.item_name = input_stream_group_item_name,
.category = AV_CLASS_CATEGORY_DEMUXER,
};
static DemuxStreamGroup *demux_stream_group_alloc(Demuxer *d, AVStreamGroup *stg)
{
InputFile *f = &d->f;
DemuxStreamGroup *dsg;
dsg = allocate_array_elem(&f->stream_groups, sizeof(*dsg), &f->nb_stream_groups);
if (!dsg)
return NULL;
dsg->istg.stg = stg;
dsg->istg.file = f;
dsg->istg.index = stg->index;
dsg->istg.class = &input_stream_group_class;
snprintf(dsg->log_name, sizeof(dsg->log_name), "istg#%d:%d/%s",
d->f.index, stg->index, avformat_stream_group_name(stg->type));
return dsg;
}
static int istg_parse_tile_grid(const OptionsContext *o, Demuxer *d, InputStreamGroup *istg)
{
InputFile *f = &d->f;
AVFormatContext *ic = d->f.ctx;
AVStreamGroup *stg = istg->stg;
const AVStreamGroupTileGrid *tg = stg->params.tile_grid;
OutputFilterOptions opts;
AVBPrint bp;
char *graph_str;
int autorotate = 1;
const char *apply_cropping = NULL;
int ret;
if (tg->nb_tiles == 1)
return 0;
memset(&opts, 0, sizeof(opts));
opt_match_per_stream_group_int(istg, &o->autorotate, ic, stg, &autorotate);
if (autorotate)
opts.flags |= OFILTER_FLAG_AUTOROTATE;
opts.flags |= OFILTER_FLAG_CROP;
opt_match_per_stream_group_str(istg, &o->apply_cropping, ic, stg, &apply_cropping);
if (apply_cropping) {
char *p;
int crop = strtol(apply_cropping, &p, 0);
if (*p)
return AVERROR(EINVAL);
if (!crop)
opts.flags &= ~OFILTER_FLAG_CROP;
}
av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
for (int i = 0; i < tg->nb_tiles; i++)
av_bprintf(&bp, "[%d:g:%d:%d]", f->index, stg->index, tg->offsets[i].idx);
av_bprintf(&bp, "xstack=inputs=%d:layout=", tg->nb_tiles);
for (int i = 0; i < tg->nb_tiles - 1; i++)
av_bprintf(&bp, "%d_%d|", tg->offsets[i].horizontal,
tg->offsets[i].vertical);
av_bprintf(&bp, "%d_%d:fill=0x%02X%02X%02X@0x%02X", tg->offsets[tg->nb_tiles - 1].horizontal,
tg->offsets[tg->nb_tiles - 1].vertical,
tg->background[0], tg->background[1],
tg->background[2], tg->background[3]);
av_bprintf(&bp, "[%d:g:%d]", f->index, stg->index);
ret = av_bprint_finalize(&bp, &graph_str);
if (ret < 0)
return ret;
if (tg->coded_width != tg->width || tg->coded_height != tg->height) {
opts.crop_top = tg->vertical_offset;
opts.crop_bottom = tg->coded_height - tg->height - tg->vertical_offset;
opts.crop_left = tg->horizontal_offset;
opts.crop_right = tg->coded_width - tg->width - tg->horizontal_offset;
}
for (int i = 0; i < tg->nb_coded_side_data; i++) {
const AVPacketSideData *sd = &tg->coded_side_data[i];
ret = av_packet_side_data_to_frame(&opts.side_data, &opts.nb_side_data, sd, 0);
if (ret < 0 && ret != AVERROR(EINVAL))
return ret;
}
ret = fg_create(NULL, graph_str, d->sch, &opts);
if (ret < 0)
return ret;
istg->fg = filtergraphs[nb_filtergraphs-1];
istg->fg->is_internal = 1;
return 0;
}
static int istg_add(const OptionsContext *o, Demuxer *d, AVStreamGroup *stg)
{
DemuxStreamGroup *dsg;
InputStreamGroup *istg;
int ret;
dsg = demux_stream_group_alloc(d, stg);
if (!dsg)
return AVERROR(ENOMEM);
istg = &dsg->istg;
switch (stg->type) {
case AV_STREAM_GROUP_PARAMS_TILE_GRID:
ret = istg_parse_tile_grid(o, d, istg);
if (ret < 0)
return ret;
break;
default:
break;
}
return 0;
}
static int dump_attachment(InputStream *ist, const char *filename)
{
AVStream *st = ist->st;
@ -1954,6 +2108,13 @@ int ifile_open(const OptionsContext *o, const char *filename, Scheduler *sch)
}
}
/* Add all the stream groups from the given input file to the demuxer */
for (int i = 0; i < ic->nb_stream_groups; i++) {
ret = istg_add(o, d, ic->stream_groups[i]);
if (ret < 0)
return ret;
}
/* dump the file content */
av_dump_format(ic, f->index, filename, 0);

View File

@ -205,19 +205,10 @@ int enc_open(void *opaque, const AVFrame *frame)
av_assert0(frame->opaque_ref);
fd = (FrameData*)frame->opaque_ref->data;
for (int i = 0; i < frame->nb_side_data; i++) {
const AVSideDataDescriptor *desc = av_frame_side_data_desc(frame->side_data[i]->type);
if (!(desc->props & AV_SIDE_DATA_PROP_GLOBAL))
continue;
ret = av_frame_side_data_clone(&enc_ctx->decoded_side_data,
&enc_ctx->nb_decoded_side_data,
frame->side_data[i],
AV_FRAME_SIDE_DATA_FLAG_UNIQUE);
if (ret < 0)
return ret;
}
ret = clone_side_data(&enc_ctx->decoded_side_data, &enc_ctx->nb_decoded_side_data,
fd->side_data, fd->nb_side_data, AV_FRAME_SIDE_DATA_FLAG_UNIQUE);
if (ret < 0)
return ret;
}
if (ist)

View File

@ -109,6 +109,9 @@ typedef struct InputFilterPriv {
// used to hold submitted input
AVFrame *frame;
// For inputs bound to a filtergraph output
OutputFilter *ofilter_src;
// source data type: AVMEDIA_TYPE_SUBTITLE for sub2video,
// same as type otherwise
enum AVMediaType type_src;
@ -190,6 +193,8 @@ typedef struct OutputFilterPriv {
void *log_parent;
char log_name[32];
int needed;
/* desired output stream properties */
int format;
int width, height;
@ -199,6 +204,11 @@ typedef struct OutputFilterPriv {
enum AVColorRange color_range;
enum AVAlphaMode alpha_mode;
unsigned crop_top;
unsigned crop_bottom;
unsigned crop_left;
unsigned crop_right;
AVFrameSideData **side_data;
int nb_side_data;
@ -223,6 +233,8 @@ typedef struct OutputFilterPriv {
const enum AVColorRange *color_ranges;
const enum AVAlphaMode *alpha_modes;
int32_t displaymatrix[9];
AVRational enc_timebase;
int64_t trim_start_us;
int64_t trim_duration_us;
@ -810,10 +822,10 @@ int ofilter_bind_enc(OutputFilter *ofilter, unsigned sched_idx_enc,
av_assert0(!opts->enc ||
ofilter->type == opts->enc->type);
ofilter->bound = 1;
ofp->needed = ofilter->bound = 1;
av_freep(&ofilter->linklabel);
ofp->flags = opts->flags;
ofp->flags |= opts->flags;
ofp->ts_offset = opts->ts_offset;
ofp->enc_timebase = opts->output_tb;
@ -921,13 +933,15 @@ static int ofilter_bind_ifilter(OutputFilter *ofilter, InputFilterPriv *ifp,
av_assert0(!ofilter->bound);
av_assert0(ofilter->type == ifp->ifilter.type);
ofilter->bound = 1;
ofp->needed = ofilter->bound = 1;
av_freep(&ofilter->linklabel);
ofilter->output_name = av_strdup(opts->name);
if (!ofilter->output_name)
return AVERROR(EINVAL);
ifp->ofilter_src = ofilter;
av_strlcatf(ofp->log_name, sizeof(ofp->log_name), "->%s", ofilter->output_name);
return 0;
@ -1071,7 +1085,8 @@ static const AVClass fg_class = {
.category = AV_CLASS_CATEGORY_FILTER,
};
int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch)
int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch,
const OutputFilterOptions *opts)
{
FilterGraphPriv *fgp;
FilterGraph *fg;
@ -1168,11 +1183,13 @@ int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch)
const enum AVMediaType type = avfilter_pad_get_type(cur->filter_ctx->output_pads,
cur->pad_idx);
OutputFilter *const ofilter = ofilter_alloc(fg, type);
OutputFilterPriv *ofp;
if (!ofilter) {
ret = AVERROR(ENOMEM);
goto fail;
}
ofp = ofp_from_ofilter(ofilter);
ofilter->linklabel = cur->name;
cur->name = NULL;
@ -1182,6 +1199,25 @@ int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch)
ret = AVERROR(ENOMEM);
goto fail;
}
// opts should only be needed in this function to fill fields from filtergraphs
// whose output is meant to be treated as if it was stream, e.g. merged HEIF
// tile groups.
if (opts) {
ofp->flags = opts->flags;
ofp->side_data = opts->side_data;
ofp->nb_side_data = opts->nb_side_data;
ofp->crop_top = opts->crop_top;
ofp->crop_bottom = opts->crop_bottom;
ofp->crop_left = opts->crop_left;
ofp->crop_right = opts->crop_right;
const AVFrameSideData *sd = av_frame_side_data_get(ofp->side_data, ofp->nb_side_data,
AV_FRAME_DATA_DISPLAYMATRIX);
if (sd)
memcpy(ofp->displaymatrix, sd->data, sizeof(ofp->displaymatrix));
}
}
if (!fg->nb_outputs) {
@ -1218,7 +1254,7 @@ int fg_create_simple(FilterGraph **pfg,
FilterGraphPriv *fgp;
int ret;
ret = fg_create(pfg, graph_desc, sch);
ret = fg_create(pfg, graph_desc, sch, NULL);
if (ret < 0)
return ret;
fg = *pfg;
@ -1259,7 +1295,7 @@ int fg_create_simple(FilterGraph **pfg,
return 0;
}
static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter)
static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter, int commit)
{
InputFilterPriv *ifp = ifp_from_ifilter(ifilter);
InputStream *ist = NULL;
@ -1310,15 +1346,20 @@ static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter)
if (!ofilter->bound && ofilter->linklabel &&
!strcmp(ofilter->linklabel, ifilter->linklabel)) {
av_log(fg, AV_LOG_VERBOSE,
"Binding input with label '%s' to filtergraph output %d:%d\n",
ifilter->linklabel, i, j);
if (commit) {
av_log(fg, AV_LOG_VERBOSE,
"Binding input with label '%s' to filtergraph output %d:%d\n",
ifilter->linklabel, i, j);
ret = ifilter_bind_fg(ifp, fg_src, j);
if (ret < 0)
av_log(fg, AV_LOG_ERROR, "Error binding filtergraph input %s\n",
ifilter->linklabel);
return ret;
ret = ifilter_bind_fg(ifp, fg_src, j);
if (ret < 0) {
av_log(fg, AV_LOG_ERROR, "Error binding filtergraph input %s\n",
ifilter->linklabel);
return ret;
}
} else
ofp_from_ofilter(ofilter)->needed = 1;
return 0;
}
}
}
@ -1366,10 +1407,38 @@ static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter)
}
ist = input_files[file_idx]->streams[st->index];
av_log(fg, AV_LOG_VERBOSE,
"Binding input with label '%s' to input stream %d:%d\n",
ifilter->linklabel, ist->file->index, ist->index);
if (commit)
av_log(fg, AV_LOG_VERBOSE,
"Binding input with label '%s' to input stream %d:%d\n",
ifilter->linklabel, ist->file->index, ist->index);
} else {
// try finding an unbound filtergraph output
for (int i = 0; i < nb_filtergraphs; i++) {
FilterGraph *fg_src = filtergraphs[i];
if (fg == fg_src)
continue;
for (int j = 0; j < fg_src->nb_outputs; j++) {
OutputFilter *ofilter = fg_src->outputs[j];
if (!ofilter->bound) {
if (commit) {
av_log(fg, AV_LOG_VERBOSE,
"Binding unlabeled filtergraph input to filtergraph output %d:%d\n", i, j);
ret = ifilter_bind_fg(ifp, fg_src, j);
if (ret < 0) {
av_log(fg, AV_LOG_ERROR, "Error binding filtergraph input %d:%d\n", i, j);
return ret;
}
} else
ofp_from_ofilter(ofilter)->needed = 1;
return 0;
}
}
}
ist = ist_find_unused(type);
if (!ist) {
av_log(fg, AV_LOG_FATAL,
@ -1379,24 +1448,27 @@ static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter)
return AVERROR(EINVAL);
}
av_log(fg, AV_LOG_VERBOSE,
"Binding unlabeled input %d to input stream %d:%d\n",
ifilter->index, ist->file->index, ist->index);
if (commit)
av_log(fg, AV_LOG_VERBOSE,
"Binding unlabeled input %d to input stream %d:%d\n",
ifilter->index, ist->file->index, ist->index);
}
av_assert0(ist);
ret = ifilter_bind_ist(ifilter, ist, &vs);
if (ret < 0) {
av_log(fg, AV_LOG_ERROR,
"Error binding an input stream to complex filtergraph input %s.\n",
ifilter->name);
return ret;
if (commit) {
ret = ifilter_bind_ist(ifilter, ist, &vs);
if (ret < 0) {
av_log(fg, AV_LOG_ERROR,
"Error binding an input stream to complex filtergraph input %s.\n",
ifilter->name);
return ret;
}
}
return 0;
}
static int bind_inputs(FilterGraph *fg)
static int bind_inputs(FilterGraph *fg, int commit)
{
// bind filtergraph inputs to input streams or other filtergraphs
for (int i = 0; i < fg->nb_inputs; i++) {
@ -1406,7 +1478,7 @@ static int bind_inputs(FilterGraph *fg)
if (ifp->bound)
continue;
ret = fg_complex_bind_input(fg, &ifp->ifilter);
ret = fg_complex_bind_input(fg, &ifp->ifilter, commit);
if (ret < 0)
return ret;
}
@ -1419,27 +1491,49 @@ int fg_finalise_bindings(void)
int ret;
for (int i = 0; i < nb_filtergraphs; i++) {
ret = bind_inputs(filtergraphs[i]);
ret = bind_inputs(filtergraphs[i], 0);
if (ret < 0)
return ret;
}
// check that all outputs were bound
for (int i = 0; i < nb_filtergraphs; i++) {
for (int i = nb_filtergraphs - 1; i >= 0; i--) {
FilterGraph *fg = filtergraphs[i];
FilterGraphPriv *fgp = fgp_from_fg(filtergraphs[i]);
for (int j = 0; j < fg->nb_outputs; j++) {
OutputFilter *output = fg->outputs[j];
if (!output->bound) {
av_log(fg, AV_LOG_FATAL,
"Filter '%s' has output %d (%s) unconnected\n",
if (!ofp_from_ofilter(output)->needed) {
if (!fg->is_internal) {
av_log(fg, AV_LOG_FATAL,
"Filter '%s' has output %d (%s) unconnected\n",
output->name, j,
output->linklabel ? (const char *)output->linklabel : "unlabeled");
return AVERROR(EINVAL);
}
av_log(fg, AV_LOG_DEBUG,
"Internal filter '%s' has output %d (%s) unconnected. Removing graph\n",
output->name, j,
output->linklabel ? (const char *)output->linklabel : "unlabeled");
return AVERROR(EINVAL);
sch_remove_filtergraph(fgp->sch, fgp->sch_idx);
fg_free(&filtergraphs[i]);
nb_filtergraphs--;
if (nb_filtergraphs > 0)
memmove(&filtergraphs[i],
&filtergraphs[i + 1],
(nb_filtergraphs - i) * sizeof(*filtergraphs));
break;
}
}
}
for (int i = 0; i < nb_filtergraphs; i++) {
ret = bind_inputs(filtergraphs[i], 1);
if (ret < 0)
return ret;
}
return 0;
}
@ -1538,6 +1632,53 @@ static int configure_output_video_filter(FilterGraphPriv *fgp, AVFilterGraph *gr
if (ret < 0)
return ret;
if (ofp->flags & OFILTER_FLAG_CROP) {
char crop_buf[64];
snprintf(crop_buf, sizeof(crop_buf), "w=iw-%u-%u:h=ih-%u-%u:x=%u:y=%u",
ofp->crop_left, ofp->crop_right,
ofp->crop_top, ofp->crop_bottom,
ofp->crop_left, ofp->crop_top);
ret = insert_filter(&last_filter, &pad_idx, "crop", crop_buf);
if (ret < 0)
return ret;
}
if (ofp->flags & OFILTER_FLAG_AUTOROTATE) {
int32_t *displaymatrix = ofp->displaymatrix;
double theta;
theta = get_rotation(displaymatrix);
if (fabs(theta - 90) < 1.0) {
ret = insert_filter(&last_filter, &pad_idx, "transpose",
displaymatrix[3] > 0 ? "cclock_flip" : "clock");
} else if (fabs(theta - 180) < 1.0) {
if (displaymatrix[0] < 0) {
ret = insert_filter(&last_filter, &pad_idx, "hflip", NULL);
if (ret < 0)
return ret;
}
if (displaymatrix[4] < 0) {
ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL);
}
} else if (fabs(theta - 270) < 1.0) {
ret = insert_filter(&last_filter, &pad_idx, "transpose",
displaymatrix[3] < 0 ? "clock_flip" : "cclock");
} else if (fabs(theta) > 1.0) {
char rotate_buf[64];
snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta);
ret = insert_filter(&last_filter, &pad_idx, "rotate", rotate_buf);
} else if (fabs(theta) < 1.0) {
if (displaymatrix && displaymatrix[4] < 0) {
ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL);
}
}
if (ret < 0)
return ret;
av_frame_side_data_remove(&ofp->side_data, &ofp->nb_side_data, AV_FRAME_DATA_DISPLAYMATRIX);
}
if ((ofp->width || ofp->height) && (ofp->flags & OFILTER_FLAG_AUTOSCALE)) {
char args[255];
AVFilterContext *filter;
@ -2035,12 +2176,11 @@ static int configure_filtergraph(FilterGraph *fg, FilterGraphThread *fgt)
ret = av_buffersink_get_ch_layout(sink, &ofp->ch_layout);
if (ret < 0)
goto fail;
av_frame_side_data_free(&ofp->side_data, &ofp->nb_side_data);
sd = av_buffersink_get_side_data(sink, &nb_sd);
if (nb_sd)
for (int j = 0; j < nb_sd; j++) {
ret = av_frame_side_data_clone(&ofp->side_data, &ofp->nb_side_data,
sd[j], 0);
sd[j], AV_FRAME_SIDE_DATA_FLAG_REPLACE);
if (ret < 0) {
av_frame_side_data_free(&ofp->side_data, &ofp->nb_side_data);
goto fail;
@ -2124,7 +2264,8 @@ static int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *fr
for (int i = 0; i < frame->nb_side_data; i++) {
const AVSideDataDescriptor *desc = av_frame_side_data_desc(frame->side_data[i]->type);
if (!(desc->props & AV_SIDE_DATA_PROP_GLOBAL))
if (!(desc->props & AV_SIDE_DATA_PROP_GLOBAL) ||
frame->side_data[i]->type == AV_FRAME_DATA_DISPLAYMATRIX)
continue;
ret = av_frame_side_data_clone(&ifp->side_data,
@ -2155,6 +2296,27 @@ static int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *fr
return 0;
}
static int ifilter_parameters_from_ofilter(InputFilter *ifilter, OutputFilter *ofilter)
{
const OutputFilterPriv *ofp = ofp_from_ofilter(ofilter);
InputFilterPriv *ifp = ifp_from_ifilter(ifilter);
if (!ifp->opts.framerate.num) {
ifp->opts.framerate = ofp->fps.framerate;
if (ifp->opts.framerate.num > 0 && ifp->opts.framerate.den > 0)
ifp->opts.flags |= IFILTER_FLAG_CFR;
}
for (int i = 0; i < ofp->nb_side_data; i++) {
int ret = av_frame_side_data_clone(&ifp->side_data, &ifp->nb_side_data,
ofp->side_data[i], AV_FRAME_SIDE_DATA_FLAG_REPLACE);
if (ret < 0)
return ret;
}
return 0;
}
int filtergraph_is_simple(const FilterGraph *fg)
{
const FilterGraphPriv *fgp = cfgp_from_cfg(fg);
@ -2476,16 +2638,17 @@ static int close_output(OutputFilterPriv *ofp, FilterGraphThread *fgt)
if (ret < 0)
return ret;
}
av_frame_side_data_free(&frame->side_data, &frame->nb_side_data);
ret = clone_side_data(&frame->side_data, &frame->nb_side_data,
ofp->side_data, ofp->nb_side_data, 0);
if (ret < 0)
return ret;
fd = frame_data(frame);
if (!fd)
return AVERROR(ENOMEM);
av_frame_side_data_free(&fd->side_data, &fd->nb_side_data);
ret = clone_side_data(&fd->side_data, &fd->nb_side_data,
ofp->side_data, ofp->nb_side_data, 0);
if (ret < 0)
return ret;
fd->frame_rate_filter = ofp->fps.framerate;
av_assert0(!frame->buf[0]);
@ -2640,6 +2803,14 @@ static int fg_output_step(OutputFilterPriv *ofp, FilterGraphThread *fgt,
return AVERROR(ENOMEM);
}
av_frame_side_data_free(&fd->side_data, &fd->nb_side_data);
if (!fgt->got_frame) {
ret = clone_side_data(&fd->side_data, &fd->nb_side_data,
ofp->side_data, ofp->nb_side_data, 0);
if (ret < 0)
return ret;
}
fd->wallclock[LATENCY_PROBE_FILTER_POST] = av_gettime_relative();
// only use bits_per_raw_sample passed through from the decoder
@ -2935,6 +3106,14 @@ static int send_frame(FilterGraph *fg, FilterGraphThread *fgt,
ret = ifilter_parameters_from_frame(ifilter, frame);
if (ret < 0)
return ret;
/* Inputs bound to a filtergraph output will have some fields unset.
* Handle them here */
if (ifp->ofilter_src) {
ret = ifilter_parameters_from_ofilter(ifilter, ifp->ofilter_src);
if (ret < 0)
return ret;
}
}
/* (re)init the graph if possible, otherwise buffer the frame and return */

View File

@ -1598,6 +1598,7 @@ fail:
static int map_auto_video(Muxer *mux, const OptionsContext *o)
{
AVFormatContext *oc = mux->fc;
InputStreamGroup *best_istg = NULL;
InputStream *best_ist = NULL;
int64_t best_score = 0;
int qcr;
@ -1609,8 +1610,41 @@ static int map_auto_video(Muxer *mux, const OptionsContext *o)
qcr = avformat_query_codec(oc->oformat, oc->oformat->video_codec, 0);
for (int j = 0; j < nb_input_files; j++) {
InputFile *ifile = input_files[j];
InputStreamGroup *file_best_istg = NULL;
InputStream *file_best_ist = NULL;
int64_t file_best_score = 0;
for (int i = 0; i < ifile->nb_stream_groups; i++) {
InputStreamGroup *istg = ifile->stream_groups[i];
int64_t score = 0;
if (!istg->fg)
continue;
for (int j = 0; j < istg->stg->nb_streams; j++) {
AVStream *st = istg->stg->streams[j];
if (st->event_flags & AVSTREAM_EVENT_FLAG_NEW_PACKETS) {
score = 100000000;
break;
}
}
switch (istg->stg->type) {
case AV_STREAM_GROUP_PARAMS_TILE_GRID: {
const AVStreamGroupTileGrid *tg = istg->stg->params.tile_grid;
score += tg->width * (int64_t)tg->height
+ 5000000*!!(istg->stg->disposition & AV_DISPOSITION_DEFAULT);
break;
}
default:
continue;
}
if (score > file_best_score) {
file_best_score = score;
file_best_istg = istg;
}
}
for (int i = 0; i < ifile->nb_streams; i++) {
InputStream *ist = ifile->streams[i];
int64_t score;
@ -1630,6 +1664,15 @@ static int map_auto_video(Muxer *mux, const OptionsContext *o)
continue;
file_best_score = score;
file_best_ist = ist;
file_best_istg = NULL;
}
}
if (file_best_istg) {
file_best_score -= 5000000*!!(file_best_istg->stg->disposition & AV_DISPOSITION_DEFAULT);
if (file_best_score > best_score) {
best_score = file_best_score;
best_istg = file_best_istg;
best_ist = NULL;
}
}
if (file_best_ist) {
@ -1639,9 +1682,19 @@ static int map_auto_video(Muxer *mux, const OptionsContext *o)
if (file_best_score > best_score) {
best_score = file_best_score;
best_ist = file_best_ist;
best_istg = NULL;
}
}
}
if (best_istg) {
FilterGraph *fg = best_istg->fg;
OutputFilter *ofilter = fg->outputs[0];
av_assert0(fg->nb_outputs == 1);
av_log(mux, AV_LOG_VERBOSE, "Creating output stream from stream group derived complex filtergraph %d.\n", fg->index);
return ost_add(mux, o, AVMEDIA_TYPE_VIDEO, NULL, ofilter, NULL, NULL);
}
if (best_ist)
return ost_add(mux, o, AVMEDIA_TYPE_VIDEO, best_ist, NULL, NULL, NULL);

View File

@ -242,6 +242,70 @@ OPT_MATCH_PER_STREAM(int, int, OPT_TYPE_INT, i);
OPT_MATCH_PER_STREAM(int64, int64_t, OPT_TYPE_INT64, i64);
OPT_MATCH_PER_STREAM(dbl, double, OPT_TYPE_DOUBLE, dbl);
static unsigned opt_match_per_stream_group(void *logctx, enum OptionType type,
const SpecifierOptList *sol,
AVFormatContext *fc, AVStreamGroup *stg)
{
int matches = 0, match_idx = -1;
av_assert0((type == sol->type) || !sol->nb_opt);
for (int i = 0; i < sol->nb_opt; i++) {
const StreamSpecifier *ss = &sol->opt[i].stream_spec;
if (stream_group_specifier_match(ss, fc, stg, logctx)) {
match_idx = i;
matches++;
}
}
if (matches > 1 && sol->opt_canon) {
const SpecifierOpt *so = &sol->opt[match_idx];
const char *spec = so->specifier && so->specifier[0] ? so->specifier : "";
char namestr[128] = "";
char optval_buf[32];
const char *optval = optval_buf;
snprintf(namestr, sizeof(namestr), "-%s", sol->opt_canon->name);
if (sol->opt_canon->flags & OPT_HAS_ALT) {
const char * const *names_alt = sol->opt_canon->u1.names_alt;
for (int i = 0; names_alt[i]; i++)
av_strlcatf(namestr, sizeof(namestr), "/-%s", names_alt[i]);
}
switch (sol->type) {
case OPT_TYPE_STRING: optval = so->u.str; break;
case OPT_TYPE_INT: snprintf(optval_buf, sizeof(optval_buf), "%d", so->u.i); break;
case OPT_TYPE_INT64: snprintf(optval_buf, sizeof(optval_buf), "%"PRId64, so->u.i64); break;
case OPT_TYPE_FLOAT: snprintf(optval_buf, sizeof(optval_buf), "%f", so->u.f); break;
case OPT_TYPE_DOUBLE: snprintf(optval_buf, sizeof(optval_buf), "%f", so->u.dbl); break;
default: av_assert0(0);
}
av_log(logctx, AV_LOG_WARNING, "Multiple %s options specified for "
"stream group %d, only the last option '-%s%s%s %s' will be used.\n",
namestr, stg->index, sol->opt_canon->name, spec[0] ? ":" : "",
spec, optval);
}
return match_idx + 1;
}
#define OPT_MATCH_PER_STREAM_GROUP(name, type, opt_type, m) \
void opt_match_per_stream_group_ ## name(void *logctx, const SpecifierOptList *sol, \
AVFormatContext *fc, AVStreamGroup *stg, type *out) \
{ \
unsigned ret = opt_match_per_stream_group(logctx, opt_type, sol, fc, stg); \
if (ret > 0) \
*out = sol->opt[ret - 1].u.m; \
}
OPT_MATCH_PER_STREAM_GROUP(str, const char *, OPT_TYPE_STRING, str);
OPT_MATCH_PER_STREAM_GROUP(int, int, OPT_TYPE_INT, i);
OPT_MATCH_PER_STREAM_GROUP(int64, int64_t, OPT_TYPE_INT64, i64);
OPT_MATCH_PER_STREAM_GROUP(dbl, double, OPT_TYPE_DOUBLE, dbl);
int view_specifier_parse(const char **pspec, ViewSpecifier *vs)
{
const char *spec = *pspec;
@ -504,8 +568,10 @@ static int opt_map(void *optctx, const char *opt, const char *arg)
}
if (arg[0] == '[') {
ViewSpecifier vs;
/* this mapping refers to lavfi output */
const char *c = arg + 1;
char *endptr;
ret = GROW_ARRAY(o->stream_maps, o->nb_stream_maps);
if (ret < 0)
@ -518,6 +584,27 @@ static int opt_map(void *optctx, const char *opt, const char *arg)
ret = AVERROR(EINVAL);
goto fail;
}
arg++;
m->group_index = -1;
file_idx = strtol(arg, &endptr, 0);
if (file_idx >= nb_input_files || file_idx < 0)
goto end;
arg = endptr;
ret = stream_specifier_parse(&ss, *arg == ':' ? arg + 1 : arg, 1, NULL);
if (ret < 0)
goto end;
arg = ss.remainder ? ss.remainder : "";
ret = view_specifier_parse(&arg, &vs);
if (ret < 0 || (*arg && strcmp(arg, "]")))
goto end;
m->file_index = file_idx;
m->stream_index = ss.idx;
m->group_index = ss.stream_list == STREAM_LIST_GROUP_IDX ? ss.list_id : -1;
} else {
ViewSpecifier vs;
char *endptr;
@ -583,6 +670,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg)
m->file_index = file_idx;
m->stream_index = i;
m->group_index = ss.stream_list == STREAM_LIST_GROUP_IDX ? ss.list_id : -1;
m->vs = vs;
}
}
@ -602,6 +690,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg)
goto fail;
}
}
end:
ret = 0;
fail:
stream_specifier_uninit(&ss);
@ -1407,7 +1496,7 @@ int ffmpeg_parse_options(int argc, char **argv, Scheduler *sch)
/* create complex filtergraphs */
for (int i = 0; i < go.nb_filtergraphs; i++) {
ret = fg_create(NULL, go.filtergraphs[i], sch);
ret = fg_create(NULL, go.filtergraphs[i], sch, NULL);
go.filtergraphs[i] = NULL;
if (ret < 0)
goto fail;

View File

@ -405,6 +405,9 @@ static int task_start(SchTask *task)
{
int ret;
if (!task->parent)
return 0;
av_log(task->func_arg, AV_LOG_VERBOSE, "Starting thread...\n");
av_assert0(!task->thread_running);
@ -454,6 +457,23 @@ static int64_t trailing_dts(const Scheduler *sch, int count_finished)
return min_dts == INT64_MAX ? AV_NOPTS_VALUE : min_dts;
}
void sch_remove_filtergraph(Scheduler *sch, int idx)
{
SchFilterGraph *fg = &sch->filters[idx];
av_assert0(!fg->task.thread_running);
memset(&fg->task, 0, sizeof(fg->task));
tq_free(&fg->queue);
av_freep(&fg->inputs);
fg->nb_inputs = 0;
av_freep(&fg->outputs);
fg->nb_outputs = 0;
fg->task_exited = 1;
}
void sch_free(Scheduler **psch)
{
Scheduler *sch = *psch;
@ -2630,6 +2650,9 @@ static int task_stop(Scheduler *sch, SchTask *task)
int ret;
void *thread_ret;
if (!task->parent)
return 0;
if (!task->thread_running)
return task_cleanup(sch, task->node);

View File

@ -205,6 +205,8 @@ int sch_add_dec_output(Scheduler *sch, unsigned dec_idx);
int sch_add_filtergraph(Scheduler *sch, unsigned nb_inputs, unsigned nb_outputs,
SchThreadFunc func, void *ctx);
void sch_remove_filtergraph(Scheduler *sch, int idx);
/**
* Add a muxer to the scheduler.
*

View File

@ -815,3 +815,53 @@ int avcodec_get_supported_config(const AVCodecContext *avctx, const AVCodec *cod
return ff_default_get_supported_config(avctx, codec, config, flags, out, out_num);
}
}
int av_packet_side_data_from_frame(AVPacketSideData **psd, int *pnb_sd,
const AVFrameSideData *src, unsigned int flags)
{
AVPacketSideData *sd = NULL;
for (unsigned j = 0; ff_sd_global_map[j].packet < AV_PKT_DATA_NB; j++) {
if (ff_sd_global_map[j].frame != src->type)
continue;
sd = av_packet_side_data_new(psd, pnb_sd, ff_sd_global_map[j].packet,
src->size, 0);
if (!sd)
return AVERROR(ENOMEM);
memcpy(sd->data, src->data, src->size);
break;
}
if (!sd)
return AVERROR(EINVAL);
return 0;
}
int av_packet_side_data_to_frame(AVFrameSideData ***psd, int *pnb_sd,
const AVPacketSideData *src, unsigned int flags)
{
AVFrameSideData *sd = NULL;
for (unsigned j = 0; ff_sd_global_map[j].packet < AV_PKT_DATA_NB; j++) {
if (ff_sd_global_map[j].packet != src->type)
continue;
sd = av_frame_side_data_new(psd, pnb_sd, ff_sd_global_map[j].frame,
src->size, flags);
if (!sd)
return AVERROR(ENOMEM);
memcpy(sd->data, src->data, src->size);
break;
}
if (!sd)
return AVERROR(EINVAL);
return 0;
}

View File

@ -108,7 +108,7 @@ typedef struct HFParam {
* Optimized fixed codebook excitation parameters
*/
typedef struct FCBParam {
int min_err;
int64_t min_err;
int amp_index;
int grid_index;
int dirac_train;

View File

@ -1013,7 +1013,7 @@ static void fcb_search(G723_1_ChannelContext *p, int16_t *impulse_resp,
int pulse_cnt = pulses[index];
int i;
optim.min_err = 1 << 30;
optim.min_err = 1LL << 31;
get_fcb_param(&optim, impulse_resp, buf, pulse_cnt, SUBFRAME_LEN);
if (p->pitch_lag[index >> 1] < SUBFRAME_LEN - 2) {

View File

@ -238,7 +238,6 @@ int ff_hevc_decode_short_term_rps(GetBitContext *gb, AVCodecContext *avctx,
static int decode_profile_tier_level(GetBitContext *gb, AVCodecContext *avctx,
PTLCommon *ptl)
{
const char *profile_name = NULL;
int i;
if (get_bits_left(gb) < 2+1+5 + 32 + 4 + 43 + 1)
@ -249,14 +248,15 @@ static int decode_profile_tier_level(GetBitContext *gb, AVCodecContext *avctx,
ptl->profile_idc = get_bits(gb, 5);
#if !CONFIG_SMALL
const char *profile_name = NULL;
for (int i = 0; ff_hevc_profiles[i].profile != AV_PROFILE_UNKNOWN; i++)
if (ff_hevc_profiles[i].profile == ptl->profile_idc) {
profile_name = ff_hevc_profiles[i].name;
break;
}
#endif
av_log(avctx, profile_name ? AV_LOG_DEBUG : AV_LOG_WARNING,
"%s profile bitstream\n", profile_name ? profile_name : "Unknown");
#endif
for (i = 0; i < 32; i++) {
ptl->profile_compatibility_flag[i] = get_bits1(gb);

View File

@ -489,6 +489,36 @@ void av_packet_side_data_remove(AVPacketSideData *sd, int *nb_sd,
*/
void av_packet_side_data_free(AVPacketSideData **sd, int *nb_sd);
struct AVFrameSideData;
/**
* Add a new packet side data entry to an array based on existing frame
* side data, if a matching type exists for packet side data.
*
* @param flags Currently unused. Must be 0.
* @retval >= 0 Success
* @retval AVERROR(EINVAL) The frame side data type does not have a matching
* packet side data type.
* @retval AVERROR(ENOMEM) Failed to add a side data entry to the array, or
* similar.
*/
int av_packet_side_data_from_frame(AVPacketSideData **sd, int *nb_sd,
const struct AVFrameSideData *src, unsigned int flags);
/**
* Add a new frame side data entry to an array based on existing packet
* side data, if a matching type exists for frame side data.
*
* @param flags Some combination of AV_FRAME_SIDE_DATA_FLAG_* flags,
* or 0.
* @retval >= 0 Success
* @retval AVERROR(EINVAL) The packet side data type does not have a matching
* frame side data type.
* @retval AVERROR(ENOMEM) Failed to add a side data entry to the array, or
* similar.
*/
int av_packet_side_data_to_frame(struct AVFrameSideData ***sd, int *nb_sd,
const AVPacketSideData *src, unsigned int flags);
const char *av_packet_side_data_name(enum AVPacketSideDataType type);
/**

View File

@ -334,6 +334,9 @@ static int decode_frame(AVCodecContext *avctx,
DECLARE_ALIGNED(32, uint8_t, qmat)[64];
memset(qmat, 1, 64);
if (avctx->skip_frame >= AVDISCARD_ALL)
return avpkt->size;
switch (avctx->codec_tag) {
case 0:
break;

View File

@ -55,8 +55,8 @@ typedef int (*rv40_loop_filter_strength_func)(uint8_t *src, ptrdiff_t stride,
int *p1, int *q1);
typedef struct RV34DSPContext {
qpel_mc_func put_pixels_tab[4][16];
qpel_mc_func avg_pixels_tab[4][16];
qpel_mc_func put_pixels_tab[2][16];
qpel_mc_func avg_pixels_tab[2][16];
h264_chroma_mc_func put_chroma_pixels_tab[3];
h264_chroma_mc_func avg_chroma_pixels_tab[3];
/**

View File

@ -29,7 +29,7 @@
#include "version_major.h"
#define LIBAVCODEC_VERSION_MINOR 16
#define LIBAVCODEC_VERSION_MINOR 17
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \

View File

@ -527,7 +527,7 @@ int ff_vlc_init_multi_from_lengths(VLC *vlc, VLC_MULTI *multi, int nb_bits, int
if (ret < 0)
return ret;
multi->table = av_malloc(sizeof(*multi->table) << nb_bits);
multi->table = av_mallocz(sizeof(*multi->table) << nb_bits);
if (!multi->table)
goto fail;

View File

@ -65,7 +65,7 @@ void ff_hevc_idct_ ## W ## _dc_8_ ## opt(int16_t *coeffs); \
void ff_hevc_idct_ ## W ## _dc_10_ ## opt(int16_t *coeffs); \
void ff_hevc_idct_ ## W ## _dc_12_ ## opt(int16_t *coeffs)
IDCT_DC_FUNCS(4x4, mmxext);
IDCT_DC_FUNCS(4x4, sse2);
IDCT_DC_FUNCS(8x8, sse2);
IDCT_DC_FUNCS(16x16, sse2);
IDCT_DC_FUNCS(32x32, sse2);
@ -816,8 +816,6 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
if (bit_depth == 8) {
if (EXTERNAL_MMXEXT(cpu_flags)) {
c->idct_dc[0] = ff_hevc_idct_4x4_dc_8_mmxext;
c->add_residual[0] = ff_hevc_add_residual_4_8_mmxext;
}
if (EXTERNAL_SSE2(cpu_flags)) {
@ -832,6 +830,7 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
}
SAO_BAND_INIT(8, sse2);
c->idct_dc[0] = ff_hevc_idct_4x4_dc_8_sse2;
c->idct_dc[1] = ff_hevc_idct_8x8_dc_8_sse2;
c->idct_dc[2] = ff_hevc_idct_16x16_dc_8_sse2;
c->idct_dc[3] = ff_hevc_idct_32x32_dc_8_sse2;
@ -998,7 +997,6 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
} else if (bit_depth == 10) {
if (EXTERNAL_MMXEXT(cpu_flags)) {
c->add_residual[0] = ff_hevc_add_residual_4_10_mmxext;
c->idct_dc[0] = ff_hevc_idct_4x4_dc_10_mmxext;
}
if (EXTERNAL_SSE2(cpu_flags)) {
c->hevc_v_loop_filter_chroma = ff_hevc_v_loop_filter_chroma_10_sse2;
@ -1013,6 +1011,7 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
SAO_BAND_INIT(10, sse2);
SAO_EDGE_INIT(10, sse2);
c->idct_dc[0] = ff_hevc_idct_4x4_dc_10_sse2;
c->idct_dc[1] = ff_hevc_idct_8x8_dc_10_sse2;
c->idct_dc[2] = ff_hevc_idct_16x16_dc_10_sse2;
c->idct_dc[3] = ff_hevc_idct_32x32_dc_10_sse2;
@ -1218,9 +1217,6 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
}
#endif /* HAVE_AVX2_EXTERNAL */
} else if (bit_depth == 12) {
if (EXTERNAL_MMXEXT(cpu_flags)) {
c->idct_dc[0] = ff_hevc_idct_4x4_dc_12_mmxext;
}
if (EXTERNAL_SSE2(cpu_flags)) {
c->hevc_v_loop_filter_chroma = ff_hevc_v_loop_filter_chroma_12_sse2;
c->hevc_h_loop_filter_chroma = ff_hevc_h_loop_filter_chroma_12_sse2;
@ -1231,6 +1227,7 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
SAO_BAND_INIT(12, sse2);
SAO_EDGE_INIT(12, sse2);
c->idct_dc[0] = ff_hevc_idct_4x4_dc_12_sse2;
c->idct_dc[1] = ff_hevc_idct_8x8_dc_12_sse2;
c->idct_dc[2] = ff_hevc_idct_16x16_dc_12_sse2;
c->idct_dc[3] = ff_hevc_idct_32x32_dc_12_sse2;

View File

@ -273,16 +273,11 @@ cglobal hevc_idct_%1x%1_dc_%2, 1, 2, 1, coeff, tmp
sar tmpd, (15 - %2)
movd m0, tmpd
SPLATW m0, xm0
mova [coeffq+mmsize*0], m0
mova [coeffq+mmsize*1], m0
mova [coeffq+mmsize*2], m0
mova [coeffq+mmsize*3], m0
%if mmsize == 16
mova [coeffq+mmsize*4], m0
mova [coeffq+mmsize*5], m0
mova [coeffq+mmsize*6], m0
mova [coeffq+mmsize*7], m0
%endif
%assign %%offset 0
%rep 2*%1*%1/mmsize
mova [coeffq+%%offset], m0
%assign %%offset %%offset+mmsize
%endrep
RET
%endmacro
@ -809,10 +804,8 @@ cglobal hevc_idct_32x32_%1, 1, 6, 16, 256, coeffs
%endmacro
%macro INIT_IDCT_DC 1
INIT_MMX mmxext
IDCT_DC_NL 4, %1
INIT_XMM sse2
IDCT_DC_NL 4, %1
IDCT_DC_NL 8, %1
IDCT_DC 16, 4, %1
IDCT_DC 32, 16, %1

View File

@ -1650,7 +1650,7 @@ static const AVOption libplacebo_options[] = {
{"pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, .unit = "range"},
{"jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, .unit = "range"},
{"color_primaries", "select color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_PRI_NB-1, DYNAMIC, .unit = "color_primaries"},
{"color_primaries", "select color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_PRI_EXT_NB-1, DYNAMIC, .unit = "color_primaries"},
{"auto", "keep the same color primaries", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"},
{"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"},
{"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"},
@ -1667,7 +1667,7 @@ static const AVOption libplacebo_options[] = {
{"ebu3213", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_EBU3213}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"},
{"vgamut", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_V_GAMUT}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"},
{"color_trc", "select color transfer", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_TRC_NB-1, DYNAMIC, .unit = "color_trc"},
{"color_trc", "select color transfer", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_TRC_EXT_NB-1, DYNAMIC, .unit = "color_trc"},
{"auto", "keep the same color transfer", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"},
{"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"},
{"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"},

View File

@ -196,6 +196,9 @@ void ff_rtp_send_h264_hevc(AVFormatContext *s1, const uint8_t *buf1, int size)
r1 = ff_nal_mp4_find_startcode(r, end, s->nal_length_size);
if (!r1)
r1 = end;
// Check that the last is not truncated
if (r1 - r < s->nal_length_size)
break;
r += s->nal_length_size;
} else {
while (!*(r++));

View File

@ -69,7 +69,7 @@ static void check_idct_dc(HEVCDSPContext *h, int bit_depth)
for (i = 2; i <= 5; i++) {
int block_size = 1 << i;
int size = block_size * block_size;
declare_func_emms(AV_CPU_FLAG_MMXEXT, void, int16_t *coeffs);
declare_func(void, int16_t *coeffs);
randomize_buffers(coeffs0, size);
memcpy(coeffs1, coeffs0, sizeof(*coeffs0) * size);

View File

@ -267,3 +267,16 @@ FATE_FFMPEG-$(call ENCDEC2, MPEG2VIDEO, FFV1, NUT, HSTACK_FILTER PIPE_PROTOCOL F
# test matching by stream disposition
fate-ffmpeg-spec-disposition: CMD = framecrc -i $(TARGET_SAMPLES)/mpegts/pmtchange.ts -map '0:disp:visual_impaired+descriptions:1' -c copy
FATE_SAMPLES_FFMPEG-$(call FRAMECRC, MPEGTS,,) += fate-ffmpeg-spec-disposition
# test heif image merging using internally defined filtegraphs
# picking the stream group if not mapping any specific stream
fate-ffmpeg-heif-merge: CMD = framecrc -i $(TARGET_SAMPLES)/heif-conformance/C007.heic
FATE_SAMPLES_FFMPEG-$(call FRAMECRC, MOV, HEVC, HEVC_PARSER) += fate-ffmpeg-heif-merge
# mapping the stream group
fate-ffmpeg-heif-merge-mapped: CMD = framecrc -i $(TARGET_SAMPLES)/heif-conformance/C007.heic -map '[0:g:0]'
FATE_SAMPLES_FFMPEG-$(call FRAMECRC, MOV, HEVC, HEVC_PARSER) += fate-ffmpeg-heif-merge-mapped
# binding the internal filtegraph with a caller defined filtergraph
fate-ffmpeg-heif-merge-filtergraph: CMD = framecrc -i $(TARGET_SAMPLES)/heif-conformance/C007.heic -filter_complex "sws_flags=+accurate_rnd+bitexact\;[0:g:0]scale=w=1280:h=720[out]" -map "[out]"
FATE_SAMPLES_FFMPEG-$(call FRAMECRC, MOV, HEVC, HEVC_PARSER SCALE_FILTER) += fate-ffmpeg-heif-merge-filtergraph

View File

@ -0,0 +1,6 @@
#tb 0: 1/1
#media_type 0: video
#codec_id 0: rawvideo
#dimensions 0: 2560x1440
#sar 0: 0/1
0, 0, 0, 1, 5529600, 0x3bf5d001

View File

@ -0,0 +1,6 @@
#tb 0: 1/1
#media_type 0: video
#codec_id 0: rawvideo
#dimensions 0: 1280x720
#sar 0: 0/1
0, 0, 0, 1, 1382400, 0x0f97ebd5

View File

@ -0,0 +1,6 @@
#tb 0: 1/1
#media_type 0: video
#codec_id 0: rawvideo
#dimensions 0: 2560x1440
#sar 0: 0/1
0, 0, 0, 1, 5529600, 0x3bf5d001