diff mbox series

[FFmpeg-devel,v4,2/4] avformat/matroska{dec, enc} Parse BlockAdditionMapping elements

Message ID 20210927025728.285093-2-tcChlisop0@gmail.com
State New
Headers show
Series [FFmpeg-devel,v4,1/4] avformat/dovi_isom Implement Dolby Vision configuration parsing/writing | expand

Checks

Context Check Description
andriy/commit_msg_x86 warning The first line of the commit message must start with a context terminated by a colon and a space, for example "lavu/opt: " or "doc: ".
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/commit_msg_ppc warning The first line of the commit message must start with a context terminated by a colon and a space, for example "lavu/opt: " or "doc: ".
andriy/make_ppc success Make finished
andriy/make_fate_ppc success Make fate finished
andriy/makeppc warning New warnings during build

Commit Message

quietvoid Sept. 27, 2021, 2:57 a.m. UTC
Adds handling of dvcC/dvvC block addition mappings.
The parsing creates AVDOVIDecoderConfigurationRecord side data for the video track.
The configuration block is written when muxing into MKV if DOVI side data is present for the track.

In version 2.2 of the Dolby ISOM specification, there is also dvwC but it is not in the Matroska spec.

Most of the Matroska element parsing was implemented by Plex developers.

Signed-off-by: quietvoid <tcChlisop0@gmail.com>
---
 libavformat/Makefile      |  4 +--
 libavformat/matroska.h    |  7 +++++
 libavformat/matroskadec.c | 59 +++++++++++++++++++++++++++++++++++++--
 libavformat/matroskaenc.c | 45 +++++++++++++++++++++++++++++
 4 files changed, 111 insertions(+), 4 deletions(-)

Comments

Andreas Rheinhardt Sept. 27, 2021, 4:49 a.m. UTC | #1
quietvoid:
> Adds handling of dvcC/dvvC block addition mappings.
> The parsing creates AVDOVIDecoderConfigurationRecord side data for the video track.
> The configuration block is written when muxing into MKV if DOVI side data is present for the track.
> 
> In version 2.2 of the Dolby ISOM specification, there is also dvwC but it is not in the Matroska spec.
> 
> Most of the Matroska element parsing was implemented by Plex developers.
> 
> Signed-off-by: quietvoid <tcChlisop0@gmail.com>
> ---
>  libavformat/Makefile      |  4 +--
>  libavformat/matroska.h    |  7 +++++
>  libavformat/matroskadec.c | 59 +++++++++++++++++++++++++++++++++++++--
>  libavformat/matroskaenc.c | 45 +++++++++++++++++++++++++++++
>  4 files changed, 111 insertions(+), 4 deletions(-)
> 
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index c45caa3eed..680030014d 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -313,11 +313,11 @@ OBJS-$(CONFIG_M4V_MUXER)                 += rawenc.o
>  OBJS-$(CONFIG_MATROSKA_DEMUXER)          += matroskadec.o matroska.o  \
>                                              flac_picture.o isom_tags.o rmsipr.o \
>                                              oggparsevorbis.o vorbiscomment.o \
> -                                            qtpalette.o replaygain.o
> +                                            qtpalette.o replaygain.o dovi_isom.o
>  OBJS-$(CONFIG_MATROSKA_MUXER)            += matroskaenc.o matroska.o \
>                                              av1.o avc.o hevc.o isom_tags.o \
>                                              flacenc_header.o avlanguage.o \
> -                                            vorbiscomment.o wv.o
> +                                            vorbiscomment.o wv.o dovi_isom.o
>  OBJS-$(CONFIG_MCA_DEMUXER)               += mca.o
>  OBJS-$(CONFIG_MCC_DEMUXER)               += mccdec.o subtitles.o
>  OBJS-$(CONFIG_MD5_MUXER)                 += hashenc.o
> diff --git a/libavformat/matroska.h b/libavformat/matroska.h
> index 2d04a6838b..4b2a3310a4 100644
> --- a/libavformat/matroska.h
> +++ b/libavformat/matroska.h
> @@ -111,6 +111,7 @@
>  #define MATROSKA_ID_TRACKCONTENTENCODING 0x6240
>  #define MATROSKA_ID_TRACKTIMECODESCALE 0x23314F
>  #define MATROSKA_ID_TRACKMAXBLKADDID 0x55EE
> +#define MATROSKA_ID_TRACKBLKADDMAPPING 0x41E4
>  
>  /* IDs in the trackvideo master */
>  #define MATROSKA_ID_VIDEOFRAMERATE 0x2383E3
> @@ -189,6 +190,12 @@
>  #define MATROSKA_ID_ENCODINGSIGKEYID 0x47E4
>  #define MATROSKA_ID_ENCODINGSIGNATURE 0x47E3
>  
> +/* IDs in the block addition mapping master */
> +#define MATROSKA_ID_BLKADDIDVALUE 0x41F0
> +#define MATROSKA_ID_BLKADDIDNAME 0x41A4
> +#define MATROSKA_ID_BLKADDIDTYPE 0x41E7
> +#define MATROSKA_ID_BLKADDIDEXTRADATA 0x41ED
> +
>  /* ID in the cues master */
>  #define MATROSKA_ID_POINTENTRY 0xBB
>  
> diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
> index 500c83ac3a..a50851b8d3 100644
> --- a/libavformat/matroskadec.c
> +++ b/libavformat/matroskadec.c
> @@ -51,6 +51,8 @@
>  #include "libavcodec/mpeg4audio.h"
>  #include "libavcodec/packet_internal.h"
>  
> +#include "libavformat/dovi_isom.h"

We use relative paths for headers in the same directory; and this
inclusion should be added to the headers below, keeping its alphabetical
ordering. This goes for lots of headers added by you.

> +
>  #include "avformat.h"
>  #include "avio_internal.h"
>  #include "internal.h"
> @@ -239,6 +241,13 @@ typedef struct MatroskaTrackOperation {
>      EbmlList combine_planes;
>  } MatroskaTrackOperation;
>  
> +typedef struct MatroskaBlockAdditionMapping {
> +    uint64_t value;
> +    char *name;
> +    uint64_t type;
> +    EbmlBin extradata;
> +} MatroskaBlockAdditionMapping;
> +
>  typedef struct MatroskaTrack {
>      uint64_t num;
>      uint64_t uid;
> @@ -269,6 +278,7 @@ typedef struct MatroskaTrack {
>      int ms_compat;
>      int needs_decoding;
>      uint64_t max_block_additional_id;
> +    EbmlList block_addition_mappings;
>  
>      uint32_t palette[AVPALETTE_COUNT];
>      int has_palette;
> @@ -419,8 +429,8 @@ typedef struct MatroskaDemuxContext {
>  // incomplete type (6.7.2 in C90, 6.9.2 in C99).
>  // Removing the sizes breaks MSVC.
>  static EbmlSyntax ebml_syntax[3], matroska_segment[9], matroska_track_video_color[15], matroska_track_video[19],
> -                  matroska_track[32], matroska_track_encoding[6], matroska_track_encodings[2],
> -                  matroska_track_combine_planes[2], matroska_track_operation[2], matroska_tracks[2],
> +                  matroska_track[33], matroska_track_encoding[6], matroska_track_encodings[2],
> +                  matroska_track_combine_planes[2], matroska_track_operation[2], matroska_block_addition_mapping[5], matroska_tracks[2],
>                    matroska_attachments[2], matroska_chapter_entry[9], matroska_chapter[6], matroska_chapters[2],
>                    matroska_index_entry[3], matroska_index[2], matroska_tag[3], matroska_tags[2], matroska_seekhead[2],
>                    matroska_blockadditions[2], matroska_blockgroup[8], matroska_cluster_parsing[8];
> @@ -570,6 +580,14 @@ static EbmlSyntax matroska_track_operation[] = {
>      CHILD_OF(matroska_track)
>  };
>  
> +static EbmlSyntax matroska_block_addition_mapping[] = {
> +    { MATROSKA_ID_BLKADDIDVALUE,      EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, value) },
> +    { MATROSKA_ID_BLKADDIDNAME,       EBML_STR,  0, 0, offsetof(MatroskaBlockAdditionMapping, name) },
> +    { MATROSKA_ID_BLKADDIDTYPE,       EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, type) },
> +    { MATROSKA_ID_BLKADDIDEXTRADATA,  EBML_BIN,  0, 0, offsetof(MatroskaBlockAdditionMapping, extradata) },
> +    CHILD_OF(matroska_track)
> +};
> +
>  static EbmlSyntax matroska_track[] = {
>      { MATROSKA_ID_TRACKNUMBER,           EBML_UINT,  0, 0, offsetof(MatroskaTrack, num) },
>      { MATROSKA_ID_TRACKNAME,             EBML_UTF8,  0, 0, offsetof(MatroskaTrack, name) },
> @@ -593,6 +611,7 @@ static EbmlSyntax matroska_track[] = {
>      { MATROSKA_ID_TRACKOPERATION,        EBML_NEST,  0, 0, offsetof(MatroskaTrack, operation),    { .n = matroska_track_operation } },
>      { MATROSKA_ID_TRACKCONTENTENCODINGS, EBML_NEST,  0, 0, 0,                                     { .n = matroska_track_encodings } },
>      { MATROSKA_ID_TRACKMAXBLKADDID,      EBML_UINT,  0, 0, offsetof(MatroskaTrack, max_block_additional_id), { .u = 0 } },
> +    { MATROSKA_ID_TRACKBLKADDMAPPING,    EBML_NEST,  0, sizeof(MatroskaBlockAdditionMapping), offsetof(MatroskaTrack, block_addition_mappings), { .n = matroska_block_addition_mapping } },
>      { MATROSKA_ID_SEEKPREROLL,           EBML_UINT,  0, 0, offsetof(MatroskaTrack, seek_preroll), { .u = 0 } },
>      { MATROSKA_ID_TRACKFLAGENABLED,      EBML_NONE },
>      { MATROSKA_ID_TRACKFLAGLACING,       EBML_NONE },
> @@ -2306,6 +2325,38 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track,
>      return 0;
>  }
>  
> +static int mkv_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const MatroskaTrack *track,
> +                               EbmlBin *bin)
> +{
> +    return ff_isom_parse_dvcc_dvvc(s, st, bin->data, bin->size);
> +}
> +
> +static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, const MatroskaTrack *track)
> +{
> +    const EbmlList *mappings_list = &track->block_addition_mappings;
> +    MatroskaBlockAdditionMapping *mappings = mappings_list->elem;
> +    int ret;
> +
> +    for (int i = 0; i < mappings_list->nb_elem; i++) {
> +        MatroskaBlockAdditionMapping *mapping = &mappings[i];
> +
> +        switch (mapping->type) {
> +        case MKBETAG('d','v','c','C'):
> +        case MKBETAG('d','v','v','C'):
> +            if ((ret = mkv_parse_dvcc_dvvc(s, st, track, &mapping->extradata)) < 0)
> +                return ret;
> +
> +            break;
> +        default:
> +            av_log(s, AV_LOG_DEBUG,
> +                   "Unknown block additional mapping type %ld, value %ld, name \"%s\"\n",

This is wrong and won't work on Windows, where long is always 32bit.
Furthermore, printing signed values here is absolutely not what we want.
It is probably best to use hexadecimal values for the type, but decimal
for the value.

> +                   mapping->type, mapping->value, mapping->name ? mapping->name : "");
> +        }
> +    }
> +
> +    return 0;
> +}
> +
>  static int get_qt_codec(MatroskaTrack *track, uint32_t *fourcc, enum AVCodecID *codec_id)
>  {
>      const AVCodecTag *codec_tags;
> @@ -2893,6 +2944,10 @@ static int matroska_parse_tracks(AVFormatContext *s)
>              if (track->flag_textdescriptions)
>                  st->disposition |= AV_DISPOSITION_DESCRIPTIONS;
>          }
> +
> +        ret = mkv_parse_block_addition_mappings(s, st, track);
> +        if (ret < 0)
> +            return ret;
>      }
>  
>      return 0;
> diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
> index 039f20988a..720ee63b9e 100644
> --- a/libavformat/matroskaenc.c
> +++ b/libavformat/matroskaenc.c
> @@ -54,6 +54,8 @@
>  #include "libavcodec/xiph.h"
>  #include "libavcodec/mpeg4audio.h"
>  
> +#include "libavformat/dovi_isom.h"
> +
>  /* Level 1 elements we create a SeekHead entry for:
>   * Info, Tracks, Chapters, Attachments, Tags (potentially twice) and Cues */
>  #define MAX_SEEKHEAD_ENTRIES 7
> @@ -1115,6 +1117,43 @@ static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb,
>      return 0;
>  }
>  
> +static int mkv_write_dovi(AVFormatContext *s, AVIOContext *pb, AVStream *st)
> +{
> +    int ret;
> +    AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *)
> +                                             av_stream_get_side_data(st, AV_PKT_DATA_DOVI_CONF, NULL);
> +
> +    if (dovi) {
> +        ebml_master mapping;
> +        uint8_t buf[ISOM_DVCC_DVVC_SIZE];
> +        uint32_t type;
> +
> +        uint64_t size;
> +        uint64_t expected_size = (2 + 1 + (sizeof("Dolby Vision configuration") - 1))

Please use a #define for this string in order not to repeat yourself.

> +                                + (2 + 1 + 4) + (2 + 1 + ISOM_DVCC_DVVC_SIZE);
> +
> +        if (dovi->dv_profile > 7)
> +            type = MKBETAG('d', 'v', 'v', 'C');
> +        else
> +            type = MKBETAG('d', 'v', 'c', 'C');
> +
> +        if ((ret = ff_isom_put_dvcc_dvvc(s, buf, sizeof(buf), dovi)) < 0)
> +            return ret;
> +
> +        size = ret;
> +
> +        mapping = start_ebml_master(pb, MATROSKA_ID_TRACKBLKADDMAPPING, expected_size);
> +
> +        put_ebml_string(pb, MATROSKA_ID_BLKADDIDNAME, "Dolby Vision configuration");
> +        put_ebml_uint(pb, MATROSKA_ID_BLKADDIDTYPE, type);
> +        put_ebml_binary(pb, MATROSKA_ID_BLKADDIDEXTRADATA, buf, size);
> +
> +        end_ebml_master(pb, mapping);
> +    }
> +
> +    return 0;
> +}
> +
>  static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
>                             AVStream *st, mkv_track *track, AVIOContext *pb,
>                             int is_default)
> @@ -1314,6 +1353,12 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
>          mkv_write_video_projection(s, pb, st);
>  
>          end_ebml_master(pb, subinfo);
> +
> +        if (mkv->mode != MODE_WEBM) {
> +            if ((ret = mkv_write_dovi(s, pb, st)) < 0)
> +                return ret;
> +        }
> +
>          break;
>  
>      case AVMEDIA_TYPE_AUDIO:
>
quietvoid Sept. 27, 2021, 11:58 a.m. UTC | #2
> quietvoid:
>> Adds handling of dvcC/dvvC block addition mappings.
>> The parsing creates AVDOVIDecoderConfigurationRecord side data for the video track.
>> The configuration block is written when muxing into MKV if DOVI side data is present for the track.
>>
>> In version 2.2 of the Dolby ISOM specification, there is also dvwC but it is not in the Matroska spec.
>>
>> Most of the Matroska element parsing was implemented by Plex developers.
>>
>> Signed-off-by: quietvoid<tcChlisop0@gmail.com>
>> ---
>>   libavformat/Makefile      |  4 +--
>>   libavformat/matroska.h    |  7 +++++
>>   libavformat/matroskadec.c | 59 +++++++++++++++++++++++++++++++++++++--
>>   libavformat/matroskaenc.c | 45 +++++++++++++++++++++++++++++
>>   4 files changed, 111 insertions(+), 4 deletions(-)
>>
>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>> index c45caa3eed..680030014d 100644
>> --- a/libavformat/Makefile
>> +++ b/libavformat/Makefile
>> @@ -313,11 +313,11 @@ OBJS-$(CONFIG_M4V_MUXER)                 += rawenc.o
>>   OBJS-$(CONFIG_MATROSKA_DEMUXER)          += matroskadec.o matroska.o  \
>>                                               flac_picture.o isom_tags.o rmsipr.o \
>>                                               oggparsevorbis.o vorbiscomment.o \
>> -                                            qtpalette.o replaygain.o
>> +                                            qtpalette.o replaygain.o dovi_isom.o
>>   OBJS-$(CONFIG_MATROSKA_MUXER)            += matroskaenc.o matroska.o \
>>                                               av1.o avc.o hevc.o isom_tags.o \
>>                                               flacenc_header.o avlanguage.o \
>> -                                            vorbiscomment.o wv.o
>> +                                            vorbiscomment.o wv.o dovi_isom.o
>>   OBJS-$(CONFIG_MCA_DEMUXER)               += mca.o
>>   OBJS-$(CONFIG_MCC_DEMUXER)               += mccdec.o subtitles.o
>>   OBJS-$(CONFIG_MD5_MUXER)                 += hashenc.o
>> diff --git a/libavformat/matroska.h b/libavformat/matroska.h
>> index 2d04a6838b..4b2a3310a4 100644
>> --- a/libavformat/matroska.h
>> +++ b/libavformat/matroska.h
>> @@ -111,6 +111,7 @@
>>   #define MATROSKA_ID_TRACKCONTENTENCODING 0x6240
>>   #define MATROSKA_ID_TRACKTIMECODESCALE 0x23314F
>>   #define MATROSKA_ID_TRACKMAXBLKADDID 0x55EE
>> +#define MATROSKA_ID_TRACKBLKADDMAPPING 0x41E4
>>   
>>   /* IDs in the trackvideo master */
>>   #define MATROSKA_ID_VIDEOFRAMERATE 0x2383E3
>> @@ -189,6 +190,12 @@
>>   #define MATROSKA_ID_ENCODINGSIGKEYID 0x47E4
>>   #define MATROSKA_ID_ENCODINGSIGNATURE 0x47E3
>>   
>> +/* IDs in the block addition mapping master */
>> +#define MATROSKA_ID_BLKADDIDVALUE 0x41F0
>> +#define MATROSKA_ID_BLKADDIDNAME 0x41A4
>> +#define MATROSKA_ID_BLKADDIDTYPE 0x41E7
>> +#define MATROSKA_ID_BLKADDIDEXTRADATA 0x41ED
>> +
>>   /* ID in the cues master */
>>   #define MATROSKA_ID_POINTENTRY 0xBB
>>   
>> diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
>> index 500c83ac3a..a50851b8d3 100644
>> --- a/libavformat/matroskadec.c
>> +++ b/libavformat/matroskadec.c
>> @@ -51,6 +51,8 @@
>>   #include "libavcodec/mpeg4audio.h"
>>   #include "libavcodec/packet_internal.h"
>>   
>> +#include "libavformat/dovi_isom.h"
> We use relative paths for headers in the same directory; and this
> inclusion should be added to the headers below, keeping its alphabetical
> ordering. This goes for lots of headers added by you.
>
Had not realized the headers below were relative. Thanks.


>> +
>>   #include "avformat.h"
>>   #include "avio_internal.h"
>>   #include "internal.h"
>> @@ -239,6 +241,13 @@ typedef struct MatroskaTrackOperation {
>>       EbmlList combine_planes;
>>   } MatroskaTrackOperation;
>>   
>> +typedef struct MatroskaBlockAdditionMapping {
>> +    uint64_t value;
>> +    char *name;
>> +    uint64_t type;
>> +    EbmlBin extradata;
>> +} MatroskaBlockAdditionMapping;
>> +
>>   typedef struct MatroskaTrack {
>>       uint64_t num;
>>       uint64_t uid;
>> @@ -269,6 +278,7 @@ typedef struct MatroskaTrack {
>>       int ms_compat;
>>       int needs_decoding;
>>       uint64_t max_block_additional_id;
>> +    EbmlList block_addition_mappings;
>>   
>>       uint32_t palette[AVPALETTE_COUNT];
>>       int has_palette;
>> @@ -419,8 +429,8 @@ typedef struct MatroskaDemuxContext {
>>   // incomplete type (6.7.2 in C90, 6.9.2 in C99).
>>   // Removing the sizes breaks MSVC.
>>   static EbmlSyntax ebml_syntax[3], matroska_segment[9], matroska_track_video_color[15], matroska_track_video[19],
>> -                  matroska_track[32], matroska_track_encoding[6], matroska_track_encodings[2],
>> -                  matroska_track_combine_planes[2], matroska_track_operation[2], matroska_tracks[2],
>> +                  matroska_track[33], matroska_track_encoding[6], matroska_track_encodings[2],
>> +                  matroska_track_combine_planes[2], matroska_track_operation[2], matroska_block_addition_mapping[5], matroska_tracks[2],
>>                     matroska_attachments[2], matroska_chapter_entry[9], matroska_chapter[6], matroska_chapters[2],
>>                     matroska_index_entry[3], matroska_index[2], matroska_tag[3], matroska_tags[2], matroska_seekhead[2],
>>                     matroska_blockadditions[2], matroska_blockgroup[8], matroska_cluster_parsing[8];
>> @@ -570,6 +580,14 @@ static EbmlSyntax matroska_track_operation[] = {
>>       CHILD_OF(matroska_track)
>>   };
>>   
>> +static EbmlSyntax matroska_block_addition_mapping[] = {
>> +    { MATROSKA_ID_BLKADDIDVALUE,      EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, value) },
>> +    { MATROSKA_ID_BLKADDIDNAME,       EBML_STR,  0, 0, offsetof(MatroskaBlockAdditionMapping, name) },
>> +    { MATROSKA_ID_BLKADDIDTYPE,       EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, type) },
>> +    { MATROSKA_ID_BLKADDIDEXTRADATA,  EBML_BIN,  0, 0, offsetof(MatroskaBlockAdditionMapping, extradata) },
>> +    CHILD_OF(matroska_track)
>> +};
>> +
>>   static EbmlSyntax matroska_track[] = {
>>       { MATROSKA_ID_TRACKNUMBER,           EBML_UINT,  0, 0, offsetof(MatroskaTrack, num) },
>>       { MATROSKA_ID_TRACKNAME,             EBML_UTF8,  0, 0, offsetof(MatroskaTrack, name) },
>> @@ -593,6 +611,7 @@ static EbmlSyntax matroska_track[] = {
>>       { MATROSKA_ID_TRACKOPERATION,        EBML_NEST,  0, 0, offsetof(MatroskaTrack, operation),    { .n = matroska_track_operation } },
>>       { MATROSKA_ID_TRACKCONTENTENCODINGS, EBML_NEST,  0, 0, 0,                                     { .n = matroska_track_encodings } },
>>       { MATROSKA_ID_TRACKMAXBLKADDID,      EBML_UINT,  0, 0, offsetof(MatroskaTrack, max_block_additional_id), { .u = 0 } },
>> +    { MATROSKA_ID_TRACKBLKADDMAPPING,    EBML_NEST,  0, sizeof(MatroskaBlockAdditionMapping), offsetof(MatroskaTrack, block_addition_mappings), { .n = matroska_block_addition_mapping } },
>>       { MATROSKA_ID_SEEKPREROLL,           EBML_UINT,  0, 0, offsetof(MatroskaTrack, seek_preroll), { .u = 0 } },
>>       { MATROSKA_ID_TRACKFLAGENABLED,      EBML_NONE },
>>       { MATROSKA_ID_TRACKFLAGLACING,       EBML_NONE },
>> @@ -2306,6 +2325,38 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track,
>>       return 0;
>>   }
>>   
>> +static int mkv_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const MatroskaTrack *track,
>> +                               EbmlBin *bin)
>> +{
>> +    return ff_isom_parse_dvcc_dvvc(s, st, bin->data, bin->size);
>> +}
>> +
>> +static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, const MatroskaTrack *track)
>> +{
>> +    const EbmlList *mappings_list = &track->block_addition_mappings;
>> +    MatroskaBlockAdditionMapping *mappings = mappings_list->elem;
>> +    int ret;
>> +
>> +    for (int i = 0; i < mappings_list->nb_elem; i++) {
>> +        MatroskaBlockAdditionMapping *mapping = &mappings[i];
>> +
>> +        switch (mapping->type) {
>> +        case MKBETAG('d','v','c','C'):
>> +        case MKBETAG('d','v','v','C'):
>> +            if ((ret = mkv_parse_dvcc_dvvc(s, st, track, &mapping->extradata)) < 0)
>> +                return ret;
>> +
>> +            break;
>> +        default:
>> +            av_log(s, AV_LOG_DEBUG,
>> +                   "Unknown block additional mapping type %ld, value %ld, name \"%s\"\n",
> This is wrong and won't work on Windows, where long is always 32bit.
> Furthermore, printing signed values here is absolutely not what we want.
> It is probably best to use hexadecimal values for the type, but decimal
> for the value.

Ok, I had only changed it to long because of warnings.
Andreas Rheinhardt Sept. 27, 2021, 12:06 p.m. UTC | #3
quietvoid:
>> quietvoid:
>>> Adds handling of dvcC/dvvC block addition mappings.
>>> The parsing creates AVDOVIDecoderConfigurationRecord side data for
>>> the video track.
>>> The configuration block is written when muxing into MKV if DOVI side
>>> data is present for the track.
>>>
>>> In version 2.2 of the Dolby ISOM specification, there is also dvwC
>>> but it is not in the Matroska spec.
>>>
>>> Most of the Matroska element parsing was implemented by Plex developers.
>>>
>>> Signed-off-by: quietvoid<tcChlisop0@gmail.com>
>>> ---
>>>   libavformat/Makefile      |  4 +--
>>>   libavformat/matroska.h    |  7 +++++
>>>   libavformat/matroskadec.c | 59 +++++++++++++++++++++++++++++++++++++--
>>>   libavformat/matroskaenc.c | 45 +++++++++++++++++++++++++++++
>>>   4 files changed, 111 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>>> index c45caa3eed..680030014d 100644
>>> --- a/libavformat/Makefile
>>> +++ b/libavformat/Makefile
>>> @@ -313,11 +313,11 @@ OBJS-$(CONFIG_M4V_MUXER)                 +=
>>> rawenc.o
>>>   OBJS-$(CONFIG_MATROSKA_DEMUXER)          += matroskadec.o
>>> matroska.o  \
>>>                                               flac_picture.o
>>> isom_tags.o rmsipr.o \
>>>                                               oggparsevorbis.o
>>> vorbiscomment.o \
>>> -                                            qtpalette.o replaygain.o
>>> +                                            qtpalette.o replaygain.o
>>> dovi_isom.o
>>>   OBJS-$(CONFIG_MATROSKA_MUXER)            += matroskaenc.o matroska.o \
>>>                                               av1.o avc.o hevc.o
>>> isom_tags.o \
>>>                                               flacenc_header.o
>>> avlanguage.o \
>>> -                                            vorbiscomment.o wv.o
>>> +                                            vorbiscomment.o wv.o
>>> dovi_isom.o
>>>   OBJS-$(CONFIG_MCA_DEMUXER)               += mca.o
>>>   OBJS-$(CONFIG_MCC_DEMUXER)               += mccdec.o subtitles.o
>>>   OBJS-$(CONFIG_MD5_MUXER)                 += hashenc.o
>>> diff --git a/libavformat/matroska.h b/libavformat/matroska.h
>>> index 2d04a6838b..4b2a3310a4 100644
>>> --- a/libavformat/matroska.h
>>> +++ b/libavformat/matroska.h
>>> @@ -111,6 +111,7 @@
>>>   #define MATROSKA_ID_TRACKCONTENTENCODING 0x6240
>>>   #define MATROSKA_ID_TRACKTIMECODESCALE 0x23314F
>>>   #define MATROSKA_ID_TRACKMAXBLKADDID 0x55EE
>>> +#define MATROSKA_ID_TRACKBLKADDMAPPING 0x41E4
>>>     /* IDs in the trackvideo master */
>>>   #define MATROSKA_ID_VIDEOFRAMERATE 0x2383E3
>>> @@ -189,6 +190,12 @@
>>>   #define MATROSKA_ID_ENCODINGSIGKEYID 0x47E4
>>>   #define MATROSKA_ID_ENCODINGSIGNATURE 0x47E3
>>>   +/* IDs in the block addition mapping master */
>>> +#define MATROSKA_ID_BLKADDIDVALUE 0x41F0
>>> +#define MATROSKA_ID_BLKADDIDNAME 0x41A4
>>> +#define MATROSKA_ID_BLKADDIDTYPE 0x41E7
>>> +#define MATROSKA_ID_BLKADDIDEXTRADATA 0x41ED
>>> +
>>>   /* ID in the cues master */
>>>   #define MATROSKA_ID_POINTENTRY 0xBB
>>>   diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
>>> index 500c83ac3a..a50851b8d3 100644
>>> --- a/libavformat/matroskadec.c
>>> +++ b/libavformat/matroskadec.c
>>> @@ -51,6 +51,8 @@
>>>   #include "libavcodec/mpeg4audio.h"
>>>   #include "libavcodec/packet_internal.h"
>>>   +#include "libavformat/dovi_isom.h"
>> We use relative paths for headers in the same directory; and this
>> inclusion should be added to the headers below, keeping its alphabetical
>> ordering. This goes for lots of headers added by you.
>>
> Had not realized the headers below were relative. Thanks.
> 
> 
>>> +
>>>   #include "avformat.h"
>>>   #include "avio_internal.h"
>>>   #include "internal.h"
>>> @@ -239,6 +241,13 @@ typedef struct MatroskaTrackOperation {
>>>       EbmlList combine_planes;
>>>   } MatroskaTrackOperation;
>>>   +typedef struct MatroskaBlockAdditionMapping {
>>> +    uint64_t value;
>>> +    char *name;
>>> +    uint64_t type;
>>> +    EbmlBin extradata;
>>> +} MatroskaBlockAdditionMapping;
>>> +
>>>   typedef struct MatroskaTrack {
>>>       uint64_t num;
>>>       uint64_t uid;
>>> @@ -269,6 +278,7 @@ typedef struct MatroskaTrack {
>>>       int ms_compat;
>>>       int needs_decoding;
>>>       uint64_t max_block_additional_id;
>>> +    EbmlList block_addition_mappings;
>>>         uint32_t palette[AVPALETTE_COUNT];
>>>       int has_palette;
>>> @@ -419,8 +429,8 @@ typedef struct MatroskaDemuxContext {
>>>   // incomplete type (6.7.2 in C90, 6.9.2 in C99).
>>>   // Removing the sizes breaks MSVC.
>>>   static EbmlSyntax ebml_syntax[3], matroska_segment[9],
>>> matroska_track_video_color[15], matroska_track_video[19],
>>> -                  matroska_track[32], matroska_track_encoding[6],
>>> matroska_track_encodings[2],
>>> -                  matroska_track_combine_planes[2],
>>> matroska_track_operation[2], matroska_tracks[2],
>>> +                  matroska_track[33], matroska_track_encoding[6],
>>> matroska_track_encodings[2],
>>> +                  matroska_track_combine_planes[2],
>>> matroska_track_operation[2], matroska_block_addition_mapping[5],
>>> matroska_tracks[2],
>>>                     matroska_attachments[2],
>>> matroska_chapter_entry[9], matroska_chapter[6], matroska_chapters[2],
>>>                     matroska_index_entry[3], matroska_index[2],
>>> matroska_tag[3], matroska_tags[2], matroska_seekhead[2],
>>>                     matroska_blockadditions[2],
>>> matroska_blockgroup[8], matroska_cluster_parsing[8];
>>> @@ -570,6 +580,14 @@ static EbmlSyntax matroska_track_operation[] = {
>>>       CHILD_OF(matroska_track)
>>>   };
>>>   +static EbmlSyntax matroska_block_addition_mapping[] = {
>>> +    { MATROSKA_ID_BLKADDIDVALUE,      EBML_UINT, 0, 0,
>>> offsetof(MatroskaBlockAdditionMapping, value) },
>>> +    { MATROSKA_ID_BLKADDIDNAME,       EBML_STR,  0, 0,
>>> offsetof(MatroskaBlockAdditionMapping, name) },
>>> +    { MATROSKA_ID_BLKADDIDTYPE,       EBML_UINT, 0, 0,
>>> offsetof(MatroskaBlockAdditionMapping, type) },
>>> +    { MATROSKA_ID_BLKADDIDEXTRADATA,  EBML_BIN,  0, 0,
>>> offsetof(MatroskaBlockAdditionMapping, extradata) },
>>> +    CHILD_OF(matroska_track)
>>> +};
>>> +
>>>   static EbmlSyntax matroska_track[] = {
>>>       { MATROSKA_ID_TRACKNUMBER,           EBML_UINT,  0, 0,
>>> offsetof(MatroskaTrack, num) },
>>>       { MATROSKA_ID_TRACKNAME,             EBML_UTF8,  0, 0,
>>> offsetof(MatroskaTrack, name) },
>>> @@ -593,6 +611,7 @@ static EbmlSyntax matroska_track[] = {
>>>       { MATROSKA_ID_TRACKOPERATION,        EBML_NEST,  0, 0,
>>> offsetof(MatroskaTrack, operation),    { .n =
>>> matroska_track_operation } },
>>>       { MATROSKA_ID_TRACKCONTENTENCODINGS, EBML_NEST,  0, 0,
>>> 0,                                     { .n =
>>> matroska_track_encodings } },
>>>       { MATROSKA_ID_TRACKMAXBLKADDID,      EBML_UINT,  0, 0,
>>> offsetof(MatroskaTrack, max_block_additional_id), { .u = 0 } },
>>> +    { MATROSKA_ID_TRACKBLKADDMAPPING,    EBML_NEST,  0,
>>> sizeof(MatroskaBlockAdditionMapping), offsetof(MatroskaTrack,
>>> block_addition_mappings), { .n = matroska_block_addition_mapping } },
>>>       { MATROSKA_ID_SEEKPREROLL,           EBML_UINT,  0, 0,
>>> offsetof(MatroskaTrack, seek_preroll), { .u = 0 } },
>>>       { MATROSKA_ID_TRACKFLAGENABLED,      EBML_NONE },
>>>       { MATROSKA_ID_TRACKFLAGLACING,       EBML_NONE },
>>> @@ -2306,6 +2325,38 @@ static int mkv_parse_video_projection(AVStream
>>> *st, const MatroskaTrack *track,
>>>       return 0;
>>>   }
>>>   +static int mkv_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st,
>>> const MatroskaTrack *track,
>>> +                               EbmlBin *bin)
>>> +{
>>> +    return ff_isom_parse_dvcc_dvvc(s, st, bin->data, bin->size);
>>> +}
>>> +
>>> +static int mkv_parse_block_addition_mappings(AVFormatContext *s,
>>> AVStream *st, const MatroskaTrack *track)
>>> +{
>>> +    const EbmlList *mappings_list = &track->block_addition_mappings;
>>> +    MatroskaBlockAdditionMapping *mappings = mappings_list->elem;
>>> +    int ret;
>>> +
>>> +    for (int i = 0; i < mappings_list->nb_elem; i++) {
>>> +        MatroskaBlockAdditionMapping *mapping = &mappings[i];
>>> +
>>> +        switch (mapping->type) {
>>> +        case MKBETAG('d','v','c','C'):
>>> +        case MKBETAG('d','v','v','C'):
>>> +            if ((ret = mkv_parse_dvcc_dvvc(s, st, track,
>>> &mapping->extradata)) < 0)
>>> +                return ret;
>>> +
>>> +            break;
>>> +        default:
>>> +            av_log(s, AV_LOG_DEBUG,
>>> +                   "Unknown block additional mapping type %ld, value
>>> %ld, name \"%s\"\n",
>> This is wrong and won't work on Windows, where long is always 32bit.
>> Furthermore, printing signed values here is absolutely not what we want.
>> It is probably best to use hexadecimal values for the type, but decimal
>> for the value.
> 
> Ok, I had only changed it to long because of warnings.
> 

The variables here are of type uint64_t. uint64_t is guaranteed to be a
64bit unsigned integer type, but it is not guaranteed whether it is
compatible with long int or long long int (or even just int). On 64bit
Linux systems, long is typically 64bit and uint64_t is just a typedef
for long int (which is why using %ld fixed your warning). But there are
other systems: On Windows long is 32bit. Therefore there are macros that
expand to the right printf specifier, namely PRIx64 (hex
representation), PRId64 (signed representation) and PRIu64 (unsigned
representation). Use them.

- Andreas
diff mbox series

Patch

diff --git a/libavformat/Makefile b/libavformat/Makefile
index c45caa3eed..680030014d 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -313,11 +313,11 @@  OBJS-$(CONFIG_M4V_MUXER)                 += rawenc.o
 OBJS-$(CONFIG_MATROSKA_DEMUXER)          += matroskadec.o matroska.o  \
                                             flac_picture.o isom_tags.o rmsipr.o \
                                             oggparsevorbis.o vorbiscomment.o \
-                                            qtpalette.o replaygain.o
+                                            qtpalette.o replaygain.o dovi_isom.o
 OBJS-$(CONFIG_MATROSKA_MUXER)            += matroskaenc.o matroska.o \
                                             av1.o avc.o hevc.o isom_tags.o \
                                             flacenc_header.o avlanguage.o \
-                                            vorbiscomment.o wv.o
+                                            vorbiscomment.o wv.o dovi_isom.o
 OBJS-$(CONFIG_MCA_DEMUXER)               += mca.o
 OBJS-$(CONFIG_MCC_DEMUXER)               += mccdec.o subtitles.o
 OBJS-$(CONFIG_MD5_MUXER)                 += hashenc.o
diff --git a/libavformat/matroska.h b/libavformat/matroska.h
index 2d04a6838b..4b2a3310a4 100644
--- a/libavformat/matroska.h
+++ b/libavformat/matroska.h
@@ -111,6 +111,7 @@ 
 #define MATROSKA_ID_TRACKCONTENTENCODING 0x6240
 #define MATROSKA_ID_TRACKTIMECODESCALE 0x23314F
 #define MATROSKA_ID_TRACKMAXBLKADDID 0x55EE
+#define MATROSKA_ID_TRACKBLKADDMAPPING 0x41E4
 
 /* IDs in the trackvideo master */
 #define MATROSKA_ID_VIDEOFRAMERATE 0x2383E3
@@ -189,6 +190,12 @@ 
 #define MATROSKA_ID_ENCODINGSIGKEYID 0x47E4
 #define MATROSKA_ID_ENCODINGSIGNATURE 0x47E3
 
+/* IDs in the block addition mapping master */
+#define MATROSKA_ID_BLKADDIDVALUE 0x41F0
+#define MATROSKA_ID_BLKADDIDNAME 0x41A4
+#define MATROSKA_ID_BLKADDIDTYPE 0x41E7
+#define MATROSKA_ID_BLKADDIDEXTRADATA 0x41ED
+
 /* ID in the cues master */
 #define MATROSKA_ID_POINTENTRY 0xBB
 
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
index 500c83ac3a..a50851b8d3 100644
--- a/libavformat/matroskadec.c
+++ b/libavformat/matroskadec.c
@@ -51,6 +51,8 @@ 
 #include "libavcodec/mpeg4audio.h"
 #include "libavcodec/packet_internal.h"
 
+#include "libavformat/dovi_isom.h"
+
 #include "avformat.h"
 #include "avio_internal.h"
 #include "internal.h"
@@ -239,6 +241,13 @@  typedef struct MatroskaTrackOperation {
     EbmlList combine_planes;
 } MatroskaTrackOperation;
 
+typedef struct MatroskaBlockAdditionMapping {
+    uint64_t value;
+    char *name;
+    uint64_t type;
+    EbmlBin extradata;
+} MatroskaBlockAdditionMapping;
+
 typedef struct MatroskaTrack {
     uint64_t num;
     uint64_t uid;
@@ -269,6 +278,7 @@  typedef struct MatroskaTrack {
     int ms_compat;
     int needs_decoding;
     uint64_t max_block_additional_id;
+    EbmlList block_addition_mappings;
 
     uint32_t palette[AVPALETTE_COUNT];
     int has_palette;
@@ -419,8 +429,8 @@  typedef struct MatroskaDemuxContext {
 // incomplete type (6.7.2 in C90, 6.9.2 in C99).
 // Removing the sizes breaks MSVC.
 static EbmlSyntax ebml_syntax[3], matroska_segment[9], matroska_track_video_color[15], matroska_track_video[19],
-                  matroska_track[32], matroska_track_encoding[6], matroska_track_encodings[2],
-                  matroska_track_combine_planes[2], matroska_track_operation[2], matroska_tracks[2],
+                  matroska_track[33], matroska_track_encoding[6], matroska_track_encodings[2],
+                  matroska_track_combine_planes[2], matroska_track_operation[2], matroska_block_addition_mapping[5], matroska_tracks[2],
                   matroska_attachments[2], matroska_chapter_entry[9], matroska_chapter[6], matroska_chapters[2],
                   matroska_index_entry[3], matroska_index[2], matroska_tag[3], matroska_tags[2], matroska_seekhead[2],
                   matroska_blockadditions[2], matroska_blockgroup[8], matroska_cluster_parsing[8];
@@ -570,6 +580,14 @@  static EbmlSyntax matroska_track_operation[] = {
     CHILD_OF(matroska_track)
 };
 
+static EbmlSyntax matroska_block_addition_mapping[] = {
+    { MATROSKA_ID_BLKADDIDVALUE,      EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, value) },
+    { MATROSKA_ID_BLKADDIDNAME,       EBML_STR,  0, 0, offsetof(MatroskaBlockAdditionMapping, name) },
+    { MATROSKA_ID_BLKADDIDTYPE,       EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, type) },
+    { MATROSKA_ID_BLKADDIDEXTRADATA,  EBML_BIN,  0, 0, offsetof(MatroskaBlockAdditionMapping, extradata) },
+    CHILD_OF(matroska_track)
+};
+
 static EbmlSyntax matroska_track[] = {
     { MATROSKA_ID_TRACKNUMBER,           EBML_UINT,  0, 0, offsetof(MatroskaTrack, num) },
     { MATROSKA_ID_TRACKNAME,             EBML_UTF8,  0, 0, offsetof(MatroskaTrack, name) },
@@ -593,6 +611,7 @@  static EbmlSyntax matroska_track[] = {
     { MATROSKA_ID_TRACKOPERATION,        EBML_NEST,  0, 0, offsetof(MatroskaTrack, operation),    { .n = matroska_track_operation } },
     { MATROSKA_ID_TRACKCONTENTENCODINGS, EBML_NEST,  0, 0, 0,                                     { .n = matroska_track_encodings } },
     { MATROSKA_ID_TRACKMAXBLKADDID,      EBML_UINT,  0, 0, offsetof(MatroskaTrack, max_block_additional_id), { .u = 0 } },
+    { MATROSKA_ID_TRACKBLKADDMAPPING,    EBML_NEST,  0, sizeof(MatroskaBlockAdditionMapping), offsetof(MatroskaTrack, block_addition_mappings), { .n = matroska_block_addition_mapping } },
     { MATROSKA_ID_SEEKPREROLL,           EBML_UINT,  0, 0, offsetof(MatroskaTrack, seek_preroll), { .u = 0 } },
     { MATROSKA_ID_TRACKFLAGENABLED,      EBML_NONE },
     { MATROSKA_ID_TRACKFLAGLACING,       EBML_NONE },
@@ -2306,6 +2325,38 @@  static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track,
     return 0;
 }
 
+static int mkv_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const MatroskaTrack *track,
+                               EbmlBin *bin)
+{
+    return ff_isom_parse_dvcc_dvvc(s, st, bin->data, bin->size);
+}
+
+static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, const MatroskaTrack *track)
+{
+    const EbmlList *mappings_list = &track->block_addition_mappings;
+    MatroskaBlockAdditionMapping *mappings = mappings_list->elem;
+    int ret;
+
+    for (int i = 0; i < mappings_list->nb_elem; i++) {
+        MatroskaBlockAdditionMapping *mapping = &mappings[i];
+
+        switch (mapping->type) {
+        case MKBETAG('d','v','c','C'):
+        case MKBETAG('d','v','v','C'):
+            if ((ret = mkv_parse_dvcc_dvvc(s, st, track, &mapping->extradata)) < 0)
+                return ret;
+
+            break;
+        default:
+            av_log(s, AV_LOG_DEBUG,
+                   "Unknown block additional mapping type %ld, value %ld, name \"%s\"\n",
+                   mapping->type, mapping->value, mapping->name ? mapping->name : "");
+        }
+    }
+
+    return 0;
+}
+
 static int get_qt_codec(MatroskaTrack *track, uint32_t *fourcc, enum AVCodecID *codec_id)
 {
     const AVCodecTag *codec_tags;
@@ -2893,6 +2944,10 @@  static int matroska_parse_tracks(AVFormatContext *s)
             if (track->flag_textdescriptions)
                 st->disposition |= AV_DISPOSITION_DESCRIPTIONS;
         }
+
+        ret = mkv_parse_block_addition_mappings(s, st, track);
+        if (ret < 0)
+            return ret;
     }
 
     return 0;
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 039f20988a..720ee63b9e 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -54,6 +54,8 @@ 
 #include "libavcodec/xiph.h"
 #include "libavcodec/mpeg4audio.h"
 
+#include "libavformat/dovi_isom.h"
+
 /* Level 1 elements we create a SeekHead entry for:
  * Info, Tracks, Chapters, Attachments, Tags (potentially twice) and Cues */
 #define MAX_SEEKHEAD_ENTRIES 7
@@ -1115,6 +1117,43 @@  static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb,
     return 0;
 }
 
+static int mkv_write_dovi(AVFormatContext *s, AVIOContext *pb, AVStream *st)
+{
+    int ret;
+    AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *)
+                                             av_stream_get_side_data(st, AV_PKT_DATA_DOVI_CONF, NULL);
+
+    if (dovi) {
+        ebml_master mapping;
+        uint8_t buf[ISOM_DVCC_DVVC_SIZE];
+        uint32_t type;
+
+        uint64_t size;
+        uint64_t expected_size = (2 + 1 + (sizeof("Dolby Vision configuration") - 1))
+                                + (2 + 1 + 4) + (2 + 1 + ISOM_DVCC_DVVC_SIZE);
+
+        if (dovi->dv_profile > 7)
+            type = MKBETAG('d', 'v', 'v', 'C');
+        else
+            type = MKBETAG('d', 'v', 'c', 'C');
+
+        if ((ret = ff_isom_put_dvcc_dvvc(s, buf, sizeof(buf), dovi)) < 0)
+            return ret;
+
+        size = ret;
+
+        mapping = start_ebml_master(pb, MATROSKA_ID_TRACKBLKADDMAPPING, expected_size);
+
+        put_ebml_string(pb, MATROSKA_ID_BLKADDIDNAME, "Dolby Vision configuration");
+        put_ebml_uint(pb, MATROSKA_ID_BLKADDIDTYPE, type);
+        put_ebml_binary(pb, MATROSKA_ID_BLKADDIDEXTRADATA, buf, size);
+
+        end_ebml_master(pb, mapping);
+    }
+
+    return 0;
+}
+
 static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
                            AVStream *st, mkv_track *track, AVIOContext *pb,
                            int is_default)
@@ -1314,6 +1353,12 @@  static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv,
         mkv_write_video_projection(s, pb, st);
 
         end_ebml_master(pb, subinfo);
+
+        if (mkv->mode != MODE_WEBM) {
+            if ((ret = mkv_write_dovi(s, pb, st)) < 0)
+                return ret;
+        }
+
         break;
 
     case AVMEDIA_TYPE_AUDIO: