diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index 31e88bc15d19f..719d0653bb1c1 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -1096,65 +1096,92 @@ static void build_editions(demuxer_t *demuxer) #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 19, 100) #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 22, 100) -// Correct the display dimensions of the primary tile stream to the composed -// image size, and mark the remaining tile streams as dependent. -static void handle_tile_grid_group(demuxer_t *demuxer, AVStreamGroup *stg) +// Synthesize a virtual track that assembles an AVStreamGroupTileGrid. +static void handle_tile_grid_group(demuxer_t *demuxer, AVStreamGroup *stream_group) { lavf_priv_t *priv = demuxer->priv; - AVStreamGroupTileGrid *tile_grid = stg->params.tile_grid; - - // AV_DISPOSITION_DEFAULT identifies the intended presentation stream. - AVStream *primary_st = NULL; - struct sh_stream *primary_sh = NULL; - for (unsigned i = 0; i < stg->nb_streams; i++) { - AVStream *st = stg->streams[i]; - if (st->index < 0 || (unsigned)st->index >= (unsigned)priv->num_streams) + AVStreamGroupTileGrid *grid = stream_group->params.tile_grid; + + MP_VERBOSE(demuxer, "Tile grid group: %u tiles -> %dx%d", + grid->nb_tiles, grid->width, grid->height); + + struct sh_stream *vsh = demux_alloc_sh_stream(STREAM_VIDEO); + vsh->group = talloc_zero(vsh, struct sh_stream_group); + MP_TARRAY_GROW(vsh->group, vsh->group->members, grid->nb_tiles); + char *graph = talloc_strdup(vsh->group, ""); + + for (int i = 0; i < grid->nb_tiles; i++) { + unsigned int group_idx = grid->offsets[i].idx; + if (group_idx >= stream_group->nb_streams) { + MP_ERR(demuxer, "Tile %d references out-of-range group " + "stream index %u (group has %u streams) – skipping.\n", + i, group_idx, stream_group->nb_streams); continue; - struct sh_stream *sh = priv->streams[st->index]->sh; - if (!sh) - continue; - if (st->disposition & AV_DISPOSITION_DEFAULT) { - primary_st = st; - primary_sh = sh; - break; } - if (!primary_st) { - primary_st = st; - primary_sh = sh; + + if (grid->offsets[i].horizontal >= grid->coded_width || + grid->offsets[i].vertical >= grid->coded_height) { + MP_WARN(demuxer, "Tile grid offsets exceed coded canvas (%dx%d) -" + "ignoring tile grid.\n", + grid->coded_width, grid->coded_height); + goto error; + } + + int ff_idx = stream_group->streams[group_idx]->index; + if (ff_idx >= 0 && ff_idx < priv->num_streams && priv->streams[ff_idx] && + priv->streams[ff_idx]->sh) { + vsh->group->members[vsh->group->num_members++] = priv->streams[ff_idx]->sh; + } else { + MP_WARN(demuxer, "Tile grid offset %d is not associated to any stream.\n", i); + goto error; } + + graph = talloc_asprintf_append(graph, "[%d]", i); } - if (!primary_sh) { - MP_WARN(demuxer, "Tile grid stream group %u has no usable streams.\n", - stg->index); - return; + graph = talloc_asprintf_append(graph, "xstack=inputs=%d:layout=", grid->nb_tiles); + for (int i = 0; i < grid->nb_tiles; i++) { + if (i > 0) + graph = talloc_asprintf_append(graph, "|"); + graph = talloc_asprintf_append(graph, "%d_%d", + grid->offsets[i].horizontal, grid->offsets[i].vertical); } + graph = talloc_asprintf_append(graph, + ":fill=0x%02X%02X%02X@0x%02X", + grid->background[0], grid->background[1], + grid->background[2], grid->background[3]); - MP_VERBOSE(demuxer, "Tile grid group: %u tiles, canvas %dx%d, " - "visible %dx%d at offset (%d,%d)\n", - tile_grid->nb_tiles, - tile_grid->coded_width, tile_grid->coded_height, - tile_grid->width, tile_grid->height, - tile_grid->horizontal_offset, tile_grid->vertical_offset); + if (grid->coded_width != grid->width || grid->coded_height != grid->height) { + graph = talloc_asprintf_append(graph, ",crop=w=%d:h=%d:x=%d:y=%d", + grid->width, grid->height, + grid->horizontal_offset, grid->vertical_offset); + } - // width/height is the final visible region; coded_width/coded_height is - // the full canvas including padding — use the former for presentation. - primary_sh->codec->disp_w = tile_grid->width; - primary_sh->codec->disp_h = tile_grid->height; + graph = talloc_asprintf_append(graph, "[out]"); + vsh->group->lavfi_graph = graph; - if (stg->metadata) - mp_tags_copy_from_av_dictionary(primary_sh->tags, stg->metadata); + struct sh_stream *primary_sh = vsh->group->members[0]; + vsh->codec->fps = primary_sh->codec->fps; + vsh->image = primary_sh->image; + vsh->still_image = primary_sh->still_image; + vsh->default_track = true; + vsh->codec->codec = primary_sh->codec->codec; + vsh->codec->codec_desc = primary_sh->codec->codec_desc; + vsh->codec->disp_w = grid->width; + vsh->codec->disp_h = grid->height; + vsh->title = talloc_asprintf(vsh, "Tile grid (%dx%d, %d tiles)", + grid->width, grid->height, + grid->nb_tiles); - for (unsigned i = 0; i < stg->nb_streams; i++) { - AVStream *st = stg->streams[i]; - if (st == primary_st) - continue; - if (st->index < 0 || (unsigned)st->index >= (unsigned)priv->num_streams) - continue; - struct sh_stream *sh = priv->streams[st->index]->sh; - if (sh) - sh->dependent_track = true; - } + if (stream_group->metadata) + mp_tags_copy_from_av_dictionary(vsh->tags, stream_group->metadata); + + demux_add_sh_stream(demuxer, vsh); + + return; + +error: + talloc_free(vsh); } #endif diff --git a/filters/f_decoder_wrapper.c b/filters/f_decoder_wrapper.c index 16d5b30fe3caf..a29131c770850 100644 --- a/filters/f_decoder_wrapper.c +++ b/filters/f_decoder_wrapper.c @@ -1263,6 +1263,10 @@ static bool init_group_decoder(struct priv *p, struct mp_filter *public_f) return false; } + mp_require(!public_f->stream_info); + struct mp_stream_info *no_hwdec = talloc_zero(public_f, struct mp_stream_info); + public_f->stream_info = no_hwdec; + enum mp_frame_type ftype; switch (p->header->type) { case STREAM_VIDEO: ftype = MP_FRAME_VIDEO; break; diff --git a/player/video.c b/player/video.c index 68218f8cb5b82..820bbfbf6c961 100644 --- a/player/video.c +++ b/player/video.c @@ -144,8 +144,11 @@ static void vo_chain_uninit(struct vo_chain *vo_c) track->vo_c = NULL; if (vo_c->dec_src) mp_assert(track->dec->f->pins[0] == vo_c->dec_src); - talloc_free(track->dec->f); - track->dec = NULL; + // This is NULL if a sh_stream_group's filter fails. + if (track->dec) { + talloc_free(track->dec->f); + track->dec = NULL; + } } if (vo_c->filter_src)