mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2026-06-11 08:13:06 +00:00
avformat/mpegts: create Dolby Vision stream group
See: https://professionalsupport.dolby.com/s/article/How-to-signal-Dolby-Vision-in-MPEG-2-TS Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
This commit is contained in:
committed by
James Almer
co-authored by
James Almer
parent
8d89d7c25a
commit
29bc8ec8d1
+121
-6
@@ -120,6 +120,7 @@ struct Stream {
|
||||
struct StreamGroup {
|
||||
enum AVStreamGroupParamsType type;
|
||||
int id;
|
||||
int dep_pid; /* PID of the linked dependency stream */
|
||||
unsigned int nb_streams;
|
||||
AVStream *streams[MAX_STREAMS_PER_PROGRAM];
|
||||
};
|
||||
@@ -2446,6 +2447,36 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
|
||||
dependency_pid,
|
||||
dovi->dv_bl_signal_compatibility_id,
|
||||
dovi->dv_md_compression);
|
||||
|
||||
/* A Profile 7 dual-track EL stream points at its base layer via
|
||||
* the descriptor's dependency_pid. Record a pending group entry,
|
||||
* it will be resolved once all streams are available. */
|
||||
if (dovi->dv_profile == 7 && dovi->el_present_flag &&
|
||||
!dovi->bl_present_flag && dependency_pid >= 0) {
|
||||
struct Program *p = get_program(ts, prg_id);
|
||||
struct StreamGroup *stg;
|
||||
int gi;
|
||||
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
for (gi = 0; gi < p->nb_stream_groups; gi++) {
|
||||
stg = &p->stream_groups[gi];
|
||||
if (stg->type == AV_STREAM_GROUP_PARAMS_DOLBY_VISION &&
|
||||
stg->nb_streams && stg->streams[0] == st)
|
||||
break;
|
||||
}
|
||||
if (gi == p->nb_stream_groups) {
|
||||
if (p->nb_stream_groups == MAX_STREAMS_PER_PROGRAM)
|
||||
return AVERROR(EINVAL);
|
||||
p->nb_stream_groups++;
|
||||
stg = &p->stream_groups[gi];
|
||||
stg->type = AV_STREAM_GROUP_PARAMS_DOLBY_VISION;
|
||||
stg->id = st->id;
|
||||
stg->streams[stg->nb_streams++] = st;
|
||||
}
|
||||
stg->dep_pid = dependency_pid;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXTENSION_DESCRIPTOR: /* descriptor extension */
|
||||
@@ -2541,12 +2572,81 @@ static int is_pes_stream(int stream_type, uint32_t prog_reg_desc)
|
||||
}
|
||||
}
|
||||
|
||||
static void create_stream_groups(MpegTSContext *ts, const struct Program *prg)
|
||||
/* UHD Blu-ray titles don't follow the Dolby Vision MPEG-TS spec [1] when
|
||||
* signaling a dual-PID Profile 7 stream. The enhancement-layer PID lacks the
|
||||
* DOVI_video_stream_descriptor, is advertised with stream_type 0x24 (HEVC)
|
||||
* instead of the spec-mandated 0x06, and uses the HDMV registration descriptor
|
||||
* instead of "DOVI". EL always has M2TS_VIDEO_EL_PID (0x1015) PID.
|
||||
* [1] <https://professionalsupport.dolby.com/s/article/How-to-signal-Dolby-Vision-in-MPEG-2-TS>
|
||||
*/
|
||||
static void detect_bdmv_dovi_group(MpegTSContext *ts, struct Program *prg,
|
||||
uint32_t prog_reg_desc)
|
||||
{
|
||||
AVStream *bl_st = NULL, *el_st = NULL;
|
||||
struct StreamGroup *grp;
|
||||
|
||||
if (prog_reg_desc != AV_RL32("HDMV"))
|
||||
return;
|
||||
|
||||
if (prg->nb_stream_groups >= MAX_STREAMS_PER_PROGRAM)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < prg->nb_stream_groups; i++) {
|
||||
if (prg->stream_groups[i].type == AV_STREAM_GROUP_PARAMS_DOLBY_VISION)
|
||||
return;
|
||||
}
|
||||
|
||||
for (int j = 0; j < prg->nb_streams; j++) {
|
||||
int idx = prg->streams[j].idx;
|
||||
AVStream *st;
|
||||
if (idx < 0 || idx >= ts->stream->nb_streams)
|
||||
continue;
|
||||
st = ts->stream->streams[idx];
|
||||
if (st->codecpar->codec_id != AV_CODEC_ID_HEVC)
|
||||
continue;
|
||||
if (st->id == M2TS_VIDEO_PID)
|
||||
bl_st = st;
|
||||
else if (st->id == M2TS_VIDEO_EL_PID)
|
||||
el_st = st;
|
||||
}
|
||||
|
||||
if (!bl_st || !el_st)
|
||||
return;
|
||||
|
||||
grp = &prg->stream_groups[prg->nb_stream_groups++];
|
||||
grp->type = AV_STREAM_GROUP_PARAMS_DOLBY_VISION;
|
||||
grp->id = el_st->id;
|
||||
grp->streams[0] = bl_st;
|
||||
grp->streams[1] = el_st;
|
||||
grp->nb_streams = 2;
|
||||
}
|
||||
|
||||
static void create_stream_groups(MpegTSContext *ts, struct Program *prg)
|
||||
{
|
||||
for (int i = 0; i < prg->nb_stream_groups; i++) {
|
||||
const struct StreamGroup *grp = &prg->stream_groups[i];
|
||||
struct StreamGroup *grp = &prg->stream_groups[i];
|
||||
AVStreamGroup *stg;
|
||||
int j;
|
||||
|
||||
/* The DOVI descriptor on a Profile 7 EL points at its base layer
|
||||
* via dependency_pid. Resolve the BL now that all streams of the
|
||||
* program have been added. */
|
||||
if (grp->type == AV_STREAM_GROUP_PARAMS_DOLBY_VISION && grp->nb_streams == 1) {
|
||||
for (j = 0; j < prg->nb_streams; j++) {
|
||||
int idx = prg->streams[j].idx;
|
||||
AVStream *cand;
|
||||
if (idx < 0 || idx >= ts->stream->nb_streams)
|
||||
continue;
|
||||
cand = ts->stream->streams[idx];
|
||||
if (cand == grp->streams[0] || cand->id != grp->dep_pid)
|
||||
continue;
|
||||
grp->streams[1] = grp->streams[0];
|
||||
grp->streams[0] = cand;
|
||||
grp->nb_streams = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (grp->nb_streams < 2)
|
||||
continue;
|
||||
for (j = 0; j < ts->stream->nb_stream_groups; j++) {
|
||||
@@ -2560,7 +2660,6 @@ static void create_stream_groups(MpegTSContext *ts, const struct Program *prg)
|
||||
continue;
|
||||
if (!stg)
|
||||
continue;
|
||||
av_assert0(grp->type == AV_STREAM_GROUP_PARAMS_LCEVC);
|
||||
stg->id = grp->id;
|
||||
for (int j = 0; j < grp->nb_streams; j++) {
|
||||
int ret = avformat_stream_group_add_stream(stg, grp->streams[j]);
|
||||
@@ -2568,8 +2667,22 @@ static void create_stream_groups(MpegTSContext *ts, const struct Program *prg)
|
||||
ff_remove_stream_group(ts->stream, stg);
|
||||
continue;
|
||||
}
|
||||
if (grp->streams[j]->codecpar->codec_id == AV_CODEC_ID_LCEVC)
|
||||
stg->params.lcevc->lcevc_index = stg->nb_streams - 1;
|
||||
switch (grp->type) {
|
||||
case AV_STREAM_GROUP_PARAMS_LCEVC:
|
||||
if (grp->streams[j]->codecpar->codec_id == AV_CODEC_ID_LCEVC)
|
||||
stg->params.lcevc->lcevc_index = stg->nb_streams - 1;
|
||||
break;
|
||||
case AV_STREAM_GROUP_PARAMS_DOLBY_VISION:
|
||||
if (j == 0) {
|
||||
stg->params.layered_video->width = grp->streams[j]->codecpar->width;
|
||||
stg->params.layered_video->height = grp->streams[j]->codecpar->height;
|
||||
}
|
||||
if (j == grp->nb_streams - 1)
|
||||
stg->params.layered_video->el_index = stg->nb_streams - 1;
|
||||
break;
|
||||
default:
|
||||
av_unreachable("Invalid group type!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2791,8 +2904,10 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
|
||||
mpegts_open_pcr_filter(ts, pcr_pid);
|
||||
|
||||
out:
|
||||
if (prg)
|
||||
if (prg) {
|
||||
detect_bdmv_dovi_group(ts, prg, prog_reg_desc);
|
||||
create_stream_groups(ts, prg);
|
||||
}
|
||||
|
||||
for (i = 0; i < mp4_descr_count; i++)
|
||||
av_free(mp4_descr[i].dec_config_descr);
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
#define M2TS_PMT_PID 0x0100
|
||||
#define M2TS_PCR_PID 0x1001
|
||||
#define M2TS_VIDEO_PID 0x1011
|
||||
#define M2TS_VIDEO_EL_PID 0x1015
|
||||
#define M2TS_AUDIO_START_PID 0x1100
|
||||
#define M2TS_PGSSUB_START_PID 0x1200
|
||||
#define M2TS_TEXTSUB_PID 0x1800
|
||||
|
||||
Reference in New Issue
Block a user