demux_lavf: add support for splitting EL track from in-band

This commit is contained in:
Kacper Michajłow
2026-07-01 04:46:42 +02:00
parent 998c20af67
commit 116c8ecf3c
+64 -1
View File
@@ -52,6 +52,7 @@
#include "stream/stream_curl.h"
#include "demux.h"
#include "dovi_split.h"
#include "stheader.h"
#include "options/m_config.h"
#include "options/m_option.h"
@@ -224,6 +225,7 @@ struct stream_info {
double last_key_pts;
double highest_pts;
double ts_offset;
struct mp_dovi_split *dovi_split;
};
typedef struct lavf_priv {
@@ -252,6 +254,8 @@ typedef struct lavf_priv {
int retry_counter;
struct demux_packet *pending_pkt;
AVDictionary *av_opts;
// Proxying nested streams.
@@ -600,6 +604,12 @@ static void select_tracks(struct demuxer *demuxer, int start)
AVStream *st = priv->avfc->streams[n];
bool selected = stream && demux_stream_is_selected(stream) &&
!stream->attached_picture;
if (!selected && priv->streams[n]->dovi_split) {
struct sh_stream *el =
mp_dovi_split_el_stream(priv->streams[n]->dovi_split);
if (el && demux_stream_is_selected(el))
selected = true;
}
st->discard = selected ? AVDISCARD_DEFAULT : AVDISCARD_ALL;
}
}
@@ -773,6 +783,7 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
sh->codec->dovi = true;
sh->codec->dv_profile = cfg->dv_profile;
sh->codec->dv_level = cfg->dv_level;
sh->codec->dv_el_present = cfg->bl_present_flag && cfg->el_present_flag;
}
// AVI uses decode-order indices as DTS and needs the compensation.
@@ -1307,6 +1318,22 @@ static void handle_stream_groups(demuxer_t *demuxer)
}
#endif
static void detect_dovi_split_streams(demuxer_t *demuxer)
{
lavf_priv_t *priv = demuxer->priv;
int snapshot_count = priv->num_streams;
for (int n = 0; n < snapshot_count; n++) {
struct stream_info *info = priv->streams[n];
struct sh_stream *sh = info ? info->sh : NULL;
if (!sh || sh->type != STREAM_VIDEO || !sh->codec ||
!sh->codec->dv_el_present || sh->group)
{
continue;
}
info->dovi_split = mp_dovi_split_create(demuxer, sh);
}
}
static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
{
AVFormatContext *avfc = NULL;
@@ -1489,6 +1516,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 19, 100)
handle_stream_groups(demuxer);
#endif
detect_dovi_split_streams(demuxer);
mp_tags_move_from_av_dictionary(demuxer->metadata, &avfc->metadata);
@@ -1576,6 +1604,13 @@ static bool demux_lavf_read_packet(struct demuxer *demux,
{
lavf_priv_t *priv = demux->priv;
// Companion EL packet queued by the Dolby Vision splitter on a prior call.
if (priv->pending_pkt) {
*mp_pkt = priv->pending_pkt;
priv->pending_pkt = NULL;
return true;
}
AVPacket *pkt = av_packet_alloc();
MP_HANDLE_OOM(pkt);
int r = av_read_frame(priv->avfc, pkt);
@@ -1604,7 +1639,14 @@ static bool demux_lavf_read_packet(struct demuxer *demux,
struct sh_stream *stream = info->sh;
AVStream *st = priv->avfc->streams[pkt->stream_index];
if (!demux_stream_is_selected(stream)) {
// Keep BL packets flowing to feed the Dolby Vision splitter when its
// virtual EL is selected, even if the BL itself isn't selected. The
// unselected BL dp gets discarded by the demuxer queue downstream.
struct sh_stream *split_el = info->dovi_split
? mp_dovi_split_el_stream(info->dovi_split)
: NULL;
bool need_for_split = split_el && demux_stream_is_selected(split_el);
if (!demux_stream_is_selected(stream) && !need_for_split) {
av_packet_free(&pkt);
return true; // don't signal EOF if skipping a packet
}
@@ -1666,6 +1708,13 @@ static bool demux_lavf_read_packet(struct demuxer *demux,
}
}
// Dispatch the EL view of this packet via the splitter.
if (info->dovi_split) {
struct sh_stream *el = mp_dovi_split_el_stream(info->dovi_split);
if (el && demux_stream_is_selected(el))
priv->pending_pkt = mp_dovi_split_dispatch(info->dovi_split, dp);
}
if (st->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
st->event_flags = 0;
struct mp_tags *tags = talloc_zero(NULL, struct mp_tags);
@@ -1678,6 +1727,16 @@ static bool demux_lavf_read_packet(struct demuxer *demux,
return true;
}
static void reset_dovi_split_state(demuxer_t *demuxer)
{
lavf_priv_t *priv = demuxer->priv;
TA_FREEP(&priv->pending_pkt);
for (int n = 0; n < priv->num_streams; n++) {
if (priv->streams[n] && priv->streams[n]->dovi_split)
mp_dovi_split_reset(priv->streams[n]->dovi_split);
}
}
static void demux_drop_buffers_lavf(demuxer_t *demuxer)
{
lavf_priv_t *priv = demuxer->priv;
@@ -1686,6 +1745,7 @@ static void demux_drop_buffers_lavf(demuxer_t *demuxer)
stream_drop_buffers(priv->stream);
avio_flush(priv->avfc->pb);
avformat_flush(priv->avfc);
reset_dovi_split_state(demuxer);
}
static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags)
@@ -1769,6 +1829,7 @@ static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags)
av_strerror(r, buf, sizeof(buf));
MP_VERBOSE(demuxer, "Seek failed (%s)\n", buf);
}
reset_dovi_split_state(demuxer);
update_read_stats(demuxer);
}
@@ -1802,7 +1863,9 @@ static void demux_close_lavf(demuxer_t *demuxer)
struct stream_info *info = priv->streams[n];
if (info->sh)
avcodec_parameters_free(&info->sh->codec->lav_codecpar);
TA_FREEP(&info->dovi_split);
}
TA_FREEP(&priv->pending_pkt);
if (priv->own_stream)
free_stream(priv->stream);
if (priv->av_opts)