Files
Velix-MPV-Core/demux/dovi_split.c
T
Kacper Michajłow f73dd13376 demux_lavf: parent mp_codec_params members to it
The members were parented to the demuxer or sh_stream, while they really
commute better with mp_codec_params.
2026-08-26 01:47:41 +02:00

204 lines
6.3 KiB
C

/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/bsf.h>
#include <libavutil/dovi_meta.h>
#include <libavutil/opt.h>
#include "common/av_common.h"
#include "common/common.h"
#include "common/msg.h"
#include "demux.h"
#include "demux/packet.h"
#include "demux/packet_pool.h"
#include "demux/stheader.h"
#include "dovi_split.h"
#include "mpv_talloc.h"
struct mp_dovi_split {
struct mp_log *log;
struct demuxer *demuxer;
struct sh_stream *bl;
struct sh_stream *el;
AVBSFContext *bsf;
AVPacket *staging;
};
static void mp_dovi_split_destructor(void *p)
{
struct mp_dovi_split *s = p;
av_packet_free(&s->staging);
av_bsf_free(&s->bsf);
}
struct mp_dovi_split *mp_dovi_split_create(struct demuxer *demuxer,
struct sh_stream *bl)
{
if (!bl || bl->type != STREAM_VIDEO || !bl->codec ||
!bl->codec->codec || strcmp(bl->codec->codec, "hevc") != 0)
return NULL;
const AVBitStreamFilter *def = av_bsf_get_by_name("dovi_split");
if (!def) {
MP_WARN(demuxer, "Dolby Vision EL: 'dovi_split' BSF not available in "
"libavcodec; rendering base layer only.\n");
return NULL;
}
struct mp_dovi_split *s = talloc_zero(demuxer, struct mp_dovi_split);
talloc_set_destructor(s, mp_dovi_split_destructor);
s->log = demuxer->log;
s->demuxer = demuxer;
s->bl = bl;
s->staging = av_packet_alloc();
if (!s->staging)
goto fail;
int ret = av_bsf_alloc(def, &s->bsf);
if (ret < 0)
goto fail;
AVCodecParameters *par = mp_codec_params_to_av(bl->codec);
if (par) {
avcodec_parameters_copy(s->bsf->par_in, par);
avcodec_parameters_free(&par);
}
s->bsf->time_base_in = mp_get_codec_timebase(bl->codec);
if (av_opt_set(s->bsf, "mode", "el", AV_OPT_SEARCH_CHILDREN) < 0)
goto fail;
if (av_bsf_init(s->bsf) < 0)
goto fail;
const AVCodecParameters *par_out = s->bsf->par_out;
// Allocate the virtual EL sh_stream.
struct sh_stream *el = demux_alloc_sh_stream(STREAM_VIDEO);
el->codec->codec = "hevc";
el->codec->native_tb_num = bl->codec->native_tb_num;
el->codec->native_tb_den = bl->codec->native_tb_den;
el->codec->fps = bl->codec->fps;
el->codec->disp_w = par_out->width;
el->codec->disp_h = par_out->height;
if (par_out->extradata_size > 0) {
el->codec->extradata = talloc_memdup(el->codec, par_out->extradata,
par_out->extradata_size);
el->codec->extradata_size = par_out->extradata_size;
}
for (int i = 0; i < par_out->nb_coded_side_data; i++) {
const AVPacketSideData *sd = &par_out->coded_side_data[i];
if (sd->type != AV_PKT_DATA_DOVI_CONF)
continue;
const AVDOVIDecoderConfigurationRecord *cfg = (const void *)sd->data;
el->codec->dovi = true;
el->codec->dv_profile = cfg->dv_profile;
el->codec->dv_level = cfg->dv_level;
el->codec->dv_el_present = cfg->el_present_flag;
break;
}
el->title = talloc_strdup(el, "Dolby Vision enhancement layer");
el->dependent_track = true;
demux_add_sh_stream(demuxer, el);
s->el = el;
// Bind BL and EL into a sh_stream_group.
struct sh_stream_group *group = talloc_zero(bl, struct sh_stream_group);
MP_TARRAY_APPEND(group, group->members, group->num_members, bl);
MP_TARRAY_APPEND(group, group->members, group->num_members, el);
bl->group = group;
el->group = group;
MP_VERBOSE(demuxer, "Dolby Vision Profile 7 splitter: BL stream %d, "
"virtual EL stream %d (dependent_track).\n",
bl->index, el->index);
return s;
fail:
talloc_free(s);
return NULL;
}
void mp_dovi_split_reset(struct mp_dovi_split *s)
{
if (!s || !s->bsf)
return;
av_bsf_flush(s->bsf);
}
struct sh_stream *mp_dovi_split_el_stream(struct mp_dovi_split *s)
{
return s ? s->el : NULL;
}
struct demux_packet *mp_dovi_split_dispatch(struct mp_dovi_split *s,
struct demux_packet *bl_dp)
{
if (!s || !s->bsf || !bl_dp || !bl_dp->buffer || bl_dp->len <= 0)
return NULL;
// av_bsf_send_packet takes ownership of the packet's buffer, so copy it,
// to not steal it from caller.
AVPacket *copy = av_packet_alloc();
if (!copy)
return NULL;
int ret = av_new_packet(copy, bl_dp->len);
if (ret < 0) {
av_packet_free(&copy);
return NULL;
}
memcpy(copy->data, bl_dp->buffer, bl_dp->len);
copy->flags = bl_dp->keyframe ? AV_PKT_FLAG_KEY : 0;
ret = av_bsf_send_packet(s->bsf, copy);
av_packet_free(&copy);
if (ret < 0) {
MP_VERBOSE(s->demuxer, "dovi_split: BSF send failed: %s\n",
mp_strerror(AVUNERROR(ret)));
return NULL;
}
ret = av_bsf_receive_packet(s->bsf, s->staging);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
// No EL NALs in this AU, nothing to emit.
return NULL;
}
if (ret < 0) {
MP_VERBOSE(s->demuxer, "dovi_split: BSF receive failed: %s; flushing.\n",
mp_strerror(AVUNERROR(ret)));
av_bsf_flush(s->bsf);
return NULL;
}
struct demux_packet *dp =
new_demux_packet_from_avpacket(s->demuxer->packet_pool, s->staging);
if (dp) {
// Mirror the BL packet's timing so the pairing filter can match by PTS.
dp->pts = bl_dp->pts;
dp->dts = bl_dp->dts;
dp->duration = bl_dp->duration;
dp->keyframe = bl_dp->keyframe;
dp->stream = s->el->index;
}
av_packet_unref(s->staging);
return dp;
}