Merge commit '55fe72a841ba306370e68e86c88f34b4456aa4dd'
authorJames Almer <jamrial@gmail.com>
Sun, 12 Nov 2017 03:46:52 +0000 (00:46 -0300)
committerJames Almer <jamrial@gmail.com>
Sun, 12 Nov 2017 03:46:52 +0000 (00:46 -0300)
* commit '55fe72a841ba306370e68e86c88f34b4456aa4dd':
  matroskadec: don't warn about unknown spherical medata when none is present

Merged-by: James Almer <jamrial@gmail.com>
1  2 
libavformat/matroskadec.c

index cdb2e20bb1b92aa1e9325ee0616f0b7fe3f56479,3953cd304e2a5693910a9f2f1bf3fc00f7c0934c..496499b5532733ca61d6da6029302a629be12f74
@@@ -1,21 -1,21 +1,21 @@@
  /*
   * Matroska file demuxer
 - * Copyright (c) 2003-2008 The Libav Project
 + * Copyright (c) 2003-2008 The FFmpeg Project
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg 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.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg 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 Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
  
  #include <inttypes.h>
  #include <stdio.h>
 -#if CONFIG_BZLIB
 -#include <bzlib.h>
 -#endif
 -#if CONFIG_ZLIB
 -#include <zlib.h>
 -#endif
  
  #include "libavutil/avstring.h"
 +#include "libavutil/base64.h"
  #include "libavutil/dict.h"
  #include "libavutil/intfloat.h"
  #include "libavutil/intreadwrite.h"
  #include "libavutil/lzo.h"
 +#include "libavutil/mastering_display_metadata.h"
  #include "libavutil/mathematics.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/time_internal.h"
  #include "libavutil/spherical.h"
  
  #include "libavcodec/bytestream.h"
  #include "riff.h"
  #include "rmsipr.h"
  
 +#if CONFIG_BZLIB
 +#include <bzlib.h>
 +#endif
 +#if CONFIG_ZLIB
 +#include <zlib.h>
 +#endif
 +
 +#include "qtpalette.h"
 +
  typedef enum {
      EBML_NONE,
      EBML_UINT,
      EBML_UTF8,
      EBML_BIN,
      EBML_NEST,
 +    EBML_LEVEL1,
      EBML_PASS,
      EBML_STOP,
 +    EBML_SINT,
      EBML_TYPE_COUNT
  } EbmlType;
  
@@@ -89,7 -80,6 +89,7 @@@ typedef const struct EbmlSyntax 
      int list_elem_size;
      int data_offset;
      union {
 +        int64_t     i;
          uint64_t    u;
          double      f;
          const char *s;
@@@ -121,48 -111,12 +121,48 @@@ typedef struct MatroskaTrackCompressio
      EbmlBin  settings;
  } MatroskaTrackCompression;
  
 +typedef struct MatroskaTrackEncryption {
 +    uint64_t algo;
 +    EbmlBin  key_id;
 +} MatroskaTrackEncryption;
 +
  typedef struct MatroskaTrackEncoding {
      uint64_t scope;
      uint64_t type;
      MatroskaTrackCompression compression;
 +    MatroskaTrackEncryption encryption;
  } MatroskaTrackEncoding;
  
 +typedef struct MatroskaMasteringMeta {
 +    double r_x;
 +    double r_y;
 +    double g_x;
 +    double g_y;
 +    double b_x;
 +    double b_y;
 +    double white_x;
 +    double white_y;
 +    double max_luminance;
 +    double min_luminance;
 +} MatroskaMasteringMeta;
 +
 +typedef struct MatroskaTrackVideoColor {
 +    uint64_t matrix_coefficients;
 +    uint64_t bits_per_channel;
 +    uint64_t chroma_sub_horz;
 +    uint64_t chroma_sub_vert;
 +    uint64_t cb_sub_horz;
 +    uint64_t cb_sub_vert;
 +    uint64_t chroma_siting_horz;
 +    uint64_t chroma_siting_vert;
 +    uint64_t range;
 +    uint64_t transfer_characteristics;
 +    uint64_t primaries;
 +    uint64_t max_cll;
 +    uint64_t max_fall;
 +    MatroskaMasteringMeta mastering_meta;
 +} MatroskaTrackVideoColor;
 +
  typedef struct MatroskaTrackVideoProjection {
      uint64_t type;
      EbmlBin private;
@@@ -177,13 -131,10 +177,13 @@@ typedef struct MatroskaTrackVideo 
      uint64_t display_height;
      uint64_t pixel_width;
      uint64_t pixel_height;
 -    uint64_t fourcc;
 +    EbmlBin color_space;
 +    uint64_t display_unit;
      uint64_t interlaced;
      uint64_t field_order;
      uint64_t stereo_mode;
 +    uint64_t alpha_mode;
 +    EbmlList color;
      MatroskaTrackVideoProjection projection;
  } MatroskaTrackVideo;
  
@@@ -204,15 -155,6 +204,15 @@@ typedef struct MatroskaTrackAudio 
      uint8_t *buf;
  } MatroskaTrackAudio;
  
 +typedef struct MatroskaTrackPlane {
 +    uint64_t uid;
 +    uint64_t type;
 +} MatroskaTrackPlane;
 +
 +typedef struct MatroskaTrackOperation {
 +    EbmlList combine_planes;
 +} MatroskaTrackOperation;
 +
  typedef struct MatroskaTrack {
      uint64_t num;
      uint64_t uid;
      uint64_t default_duration;
      uint64_t flag_default;
      uint64_t flag_forced;
 +    uint64_t seek_preroll;
      MatroskaTrackVideo video;
      MatroskaTrackAudio audio;
 +    MatroskaTrackOperation operation;
      EbmlList encodings;
      uint64_t codec_delay;
 +    uint64_t codec_delay_in_track_tb;
  
      AVStream *stream;
      int64_t end_timecode;
      int ms_compat;
 +    uint64_t max_block_additional_id;
 +
 +    uint32_t palette[AVPALETTE_COUNT];
 +    int has_palette;
  } MatroskaTrack;
  
  typedef struct MatroskaAttachment {
@@@ -306,14 -241,7 +306,14 @@@ typedef struct MatroskaCluster 
      EbmlList blocks;
  } MatroskaCluster;
  
 +typedef struct MatroskaLevel1Element {
 +    uint64_t id;
 +    uint64_t pos;
 +    int parsed;
 +} MatroskaLevel1Element;
 +
  typedef struct MatroskaDemuxContext {
 +    const AVClass *class;
      AVFormatContext *ctx;
  
      /* EBML stuff */
      uint64_t time_scale;
      double   duration;
      char    *title;
 +    char    *muxingapp;
 +    EbmlBin date_utc;
      EbmlList tracks;
      EbmlList attachments;
      EbmlList chapters;
      /* File has a CUES element, but we defer parsing until it is needed. */
      int cues_parsing_deferred;
  
 +    /* Level1 elements and whether they were read yet */
 +    MatroskaLevel1Element level1_elems[64];
 +    int num_level1_elems;
 +
      int current_cluster_num_blocks;
      int64_t current_cluster_pos;
      MatroskaCluster current_cluster;
  
      /* File has SSA subtitles which prevent incremental cluster parsing. */
      int contains_ssa;
 +
 +    /* WebM DASH Manifest live flag */
 +    int is_live;
 +
 +    /* Bandwidth value for WebM DASH Manifest */
 +    int bandwidth;
  } MatroskaDemuxContext;
  
  typedef struct MatroskaBlock {
      int64_t  reference;
      uint64_t non_simple;
      EbmlBin  bin;
 +    uint64_t additional_id;
 +    EbmlBin  additional;
 +    int64_t discard_padding;
  } MatroskaBlock;
  
 -static EbmlSyntax ebml_header[] = {
 +static const EbmlSyntax ebml_header[] = {
      { EBML_ID_EBMLREADVERSION,    EBML_UINT, 0, offsetof(Ebml, version),         { .u = EBML_VERSION } },
      { EBML_ID_EBMLMAXSIZELENGTH,  EBML_UINT, 0, offsetof(Ebml, max_size),        { .u = 8 } },
      { EBML_ID_EBMLMAXIDLENGTH,    EBML_UINT, 0, offsetof(Ebml, id_length),       { .u = 4 } },
      { 0 }
  };
  
 -static EbmlSyntax ebml_syntax[] = {
 +static const EbmlSyntax ebml_syntax[] = {
      { EBML_ID_HEADER, EBML_NEST, 0, 0, { .n = ebml_header } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_info[] = {
 +static const EbmlSyntax matroska_info[] = {
      { MATROSKA_ID_TIMECODESCALE, EBML_UINT,  0, offsetof(MatroskaDemuxContext, time_scale), { .u = 1000000 } },
      { MATROSKA_ID_DURATION,      EBML_FLOAT, 0, offsetof(MatroskaDemuxContext, duration) },
      { MATROSKA_ID_TITLE,         EBML_UTF8,  0, offsetof(MatroskaDemuxContext, title) },
      { MATROSKA_ID_WRITINGAPP,    EBML_NONE },
 -    { MATROSKA_ID_MUXINGAPP,     EBML_NONE },
 -    { MATROSKA_ID_DATEUTC,       EBML_NONE },
 +    { MATROSKA_ID_MUXINGAPP,     EBML_UTF8, 0, offsetof(MatroskaDemuxContext, muxingapp) },
 +    { MATROSKA_ID_DATEUTC,       EBML_BIN,  0, offsetof(MatroskaDemuxContext, date_utc) },
      { MATROSKA_ID_SEGMENTUID,    EBML_NONE },
      { 0 }
  };
  
 +static const EbmlSyntax matroska_mastering_meta[] = {
 +    { MATROSKA_ID_VIDEOCOLOR_RX, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, r_x), { .f=-1 } },
 +    { MATROSKA_ID_VIDEOCOLOR_RY, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, r_y), { .f=-1 } },
 +    { MATROSKA_ID_VIDEOCOLOR_GX, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, g_x), { .f=-1 } },
 +    { MATROSKA_ID_VIDEOCOLOR_GY, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, g_y), { .f=-1 } },
 +    { MATROSKA_ID_VIDEOCOLOR_BX, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, b_x), { .f=-1 } },
 +    { MATROSKA_ID_VIDEOCOLOR_BY, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, b_y), { .f=-1 } },
 +    { MATROSKA_ID_VIDEOCOLOR_WHITEX, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, white_x), { .f=-1 } },
 +    { MATROSKA_ID_VIDEOCOLOR_WHITEY, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, white_y), { .f=-1 } },
 +    { MATROSKA_ID_VIDEOCOLOR_LUMINANCEMIN, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, min_luminance), { .f=-1 } },
 +    { MATROSKA_ID_VIDEOCOLOR_LUMINANCEMAX, EBML_FLOAT, 0, offsetof(MatroskaMasteringMeta, max_luminance), { .f=-1 } },
 +    { 0 }
 +};
 +
 +static const EbmlSyntax matroska_track_video_color[] = {
 +    { MATROSKA_ID_VIDEOCOLORMATRIXCOEFF,      EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, matrix_coefficients), { .u = AVCOL_SPC_UNSPECIFIED } },
 +    { MATROSKA_ID_VIDEOCOLORBITSPERCHANNEL,   EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, bits_per_channel), { .u=0 } },
 +    { MATROSKA_ID_VIDEOCOLORCHROMASUBHORZ,    EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, chroma_sub_horz), { .u=0 } },
 +    { MATROSKA_ID_VIDEOCOLORCHROMASUBVERT,    EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, chroma_sub_vert), { .u=0 } },
 +    { MATROSKA_ID_VIDEOCOLORCBSUBHORZ,        EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, cb_sub_horz), { .u=0 } },
 +    { MATROSKA_ID_VIDEOCOLORCBSUBVERT,        EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, cb_sub_vert), { .u=0 } },
 +    { MATROSKA_ID_VIDEOCOLORCHROMASITINGHORZ, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, chroma_siting_horz), { .u = MATROSKA_COLOUR_CHROMASITINGHORZ_UNDETERMINED } },
 +    { MATROSKA_ID_VIDEOCOLORCHROMASITINGVERT, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, chroma_siting_vert), { .u = MATROSKA_COLOUR_CHROMASITINGVERT_UNDETERMINED } },
 +    { MATROSKA_ID_VIDEOCOLORRANGE,            EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, range), { .u = AVCOL_RANGE_UNSPECIFIED } },
 +    { MATROSKA_ID_VIDEOCOLORTRANSFERCHARACTERISTICS, EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, transfer_characteristics), { .u = AVCOL_TRC_UNSPECIFIED } },
 +    { MATROSKA_ID_VIDEOCOLORPRIMARIES,        EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, primaries), { .u = AVCOL_PRI_UNSPECIFIED } },
 +    { MATROSKA_ID_VIDEOCOLORMAXCLL,           EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, max_cll), { .u=0 } },
 +    { MATROSKA_ID_VIDEOCOLORMAXFALL,          EBML_UINT, 0, offsetof(MatroskaTrackVideoColor, max_fall), { .u=0 } },
 +    { MATROSKA_ID_VIDEOCOLORMASTERINGMETA,    EBML_NEST, 0, offsetof(MatroskaTrackVideoColor, mastering_meta), { .n = matroska_mastering_meta } },
 +    { 0 }
 +};
 +
  static const EbmlSyntax matroska_track_video_projection[] = {
 -    { MATROSKA_ID_VIDEOPROJECTIONTYPE,      EBML_UINT,  0, offsetof(MatroskaTrackVideoProjection, type), { .u = MATROSKA_VIDEO_PROJECTION_TYPE_RECTANGULAR } },
 -    { MATROSKA_ID_VIDEOPROJECTIONPRIVATE,   EBML_BIN,   0, offsetof(MatroskaTrackVideoProjection, private) },
 -    { MATROSKA_ID_VIDEOPROJECTIONPOSEYAW,   EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, yaw), { .f=0.0 } },
 -    { MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, pitch), { .f=0.0 } },
 -    { MATROSKA_ID_VIDEOPROJECTIONPOSEROLL,  EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, roll), { .f=0.0 } },
 +    { MATROSKA_ID_VIDEOPROJECTIONTYPE,        EBML_UINT,  0, offsetof(MatroskaTrackVideoProjection, type), { .u = MATROSKA_VIDEO_PROJECTION_TYPE_RECTANGULAR } },
 +    { MATROSKA_ID_VIDEOPROJECTIONPRIVATE,     EBML_BIN,   0, offsetof(MatroskaTrackVideoProjection, private) },
 +    { MATROSKA_ID_VIDEOPROJECTIONPOSEYAW,     EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, yaw), { .f=0.0 } },
 +    { MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH,   EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, pitch), { .f=0.0 } },
 +    { MATROSKA_ID_VIDEOPROJECTIONPOSEROLL,    EBML_FLOAT, 0, offsetof(MatroskaTrackVideoProjection, roll), { .f=0.0 } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_track_video[] = {
 +static const EbmlSyntax matroska_track_video[] = {
      { MATROSKA_ID_VIDEOFRAMERATE,      EBML_FLOAT, 0, offsetof(MatroskaTrackVideo, frame_rate) },
 -    { MATROSKA_ID_VIDEODISPLAYWIDTH,   EBML_UINT,  0, offsetof(MatroskaTrackVideo, display_width) },
 -    { MATROSKA_ID_VIDEODISPLAYHEIGHT,  EBML_UINT,  0, offsetof(MatroskaTrackVideo, display_height) },
 +    { MATROSKA_ID_VIDEODISPLAYWIDTH,   EBML_UINT,  0, offsetof(MatroskaTrackVideo, display_width), { .u=-1 } },
 +    { MATROSKA_ID_VIDEODISPLAYHEIGHT,  EBML_UINT,  0, offsetof(MatroskaTrackVideo, display_height), { .u=-1 } },
      { MATROSKA_ID_VIDEOPIXELWIDTH,     EBML_UINT,  0, offsetof(MatroskaTrackVideo, pixel_width) },
      { MATROSKA_ID_VIDEOPIXELHEIGHT,    EBML_UINT,  0, offsetof(MatroskaTrackVideo, pixel_height) },
 -    { MATROSKA_ID_VIDEOCOLORSPACE,     EBML_UINT,  0, offsetof(MatroskaTrackVideo, fourcc) },
 +    { MATROSKA_ID_VIDEOCOLORSPACE,     EBML_BIN,   0, offsetof(MatroskaTrackVideo, color_space) },
 +    { MATROSKA_ID_VIDEOALPHAMODE,      EBML_UINT,  0, offsetof(MatroskaTrackVideo, alpha_mode) },
 +    { MATROSKA_ID_VIDEOCOLOR,          EBML_NEST,  sizeof(MatroskaTrackVideoColor), offsetof(MatroskaTrackVideo, color), { .n = matroska_track_video_color } },
      { MATROSKA_ID_VIDEOPROJECTION,     EBML_NEST,  0, offsetof(MatroskaTrackVideo, projection), { .n = matroska_track_video_projection } },
      { MATROSKA_ID_VIDEOPIXELCROPB,     EBML_NONE },
      { MATROSKA_ID_VIDEOPIXELCROPT,     EBML_NONE },
      { MATROSKA_ID_VIDEOPIXELCROPL,     EBML_NONE },
      { MATROSKA_ID_VIDEOPIXELCROPR,     EBML_NONE },
 -    { MATROSKA_ID_VIDEODISPLAYUNIT,    EBML_NONE },
 +    { MATROSKA_ID_VIDEODISPLAYUNIT,    EBML_UINT,  0, offsetof(MatroskaTrackVideo, display_unit), { .u= MATROSKA_VIDEO_DISPLAYUNIT_PIXELS } },
      { MATROSKA_ID_VIDEOFLAGINTERLACED, EBML_UINT,  0, offsetof(MatroskaTrackVideo, interlaced),  { .u = MATROSKA_VIDEO_INTERLACE_FLAG_UNDETERMINED } },
      { MATROSKA_ID_VIDEOFIELDORDER,     EBML_UINT,  0, offsetof(MatroskaTrackVideo, field_order), { .u = MATROSKA_VIDEO_FIELDORDER_UNDETERMINED } },
      { MATROSKA_ID_VIDEOSTEREOMODE,     EBML_UINT,  0, offsetof(MatroskaTrackVideo, stereo_mode), { .u = MATROSKA_VIDEO_STEREOMODE_TYPE_NB } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_track_audio[] = {
 +static const EbmlSyntax matroska_track_audio[] = {
      { MATROSKA_ID_AUDIOSAMPLINGFREQ,    EBML_FLOAT, 0, offsetof(MatroskaTrackAudio, samplerate), { .f = 8000.0 } },
      { MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, EBML_FLOAT, 0, offsetof(MatroskaTrackAudio, out_samplerate) },
      { MATROSKA_ID_AUDIOBITDEPTH,        EBML_UINT,  0, offsetof(MatroskaTrackAudio, bitdepth) },
      { 0 }
  };
  
 -static EbmlSyntax matroska_track_encoding_compression[] = {
 +static const EbmlSyntax matroska_track_encoding_compression[] = {
      { MATROSKA_ID_ENCODINGCOMPALGO,     EBML_UINT, 0, offsetof(MatroskaTrackCompression, algo), { .u = 0 } },
      { MATROSKA_ID_ENCODINGCOMPSETTINGS, EBML_BIN,  0, offsetof(MatroskaTrackCompression, settings) },
      { 0 }
  };
  
 -static EbmlSyntax matroska_track_encoding[] = {
 +static const EbmlSyntax matroska_track_encoding_encryption[] = {
 +    { MATROSKA_ID_ENCODINGENCALGO,        EBML_UINT, 0, offsetof(MatroskaTrackEncryption,algo), {.u = 0} },
 +    { MATROSKA_ID_ENCODINGENCKEYID,       EBML_BIN, 0, offsetof(MatroskaTrackEncryption,key_id) },
 +    { MATROSKA_ID_ENCODINGENCAESSETTINGS, EBML_NONE },
 +    { MATROSKA_ID_ENCODINGSIGALGO,        EBML_NONE },
 +    { MATROSKA_ID_ENCODINGSIGHASHALGO,    EBML_NONE },
 +    { MATROSKA_ID_ENCODINGSIGKEYID,       EBML_NONE },
 +    { MATROSKA_ID_ENCODINGSIGNATURE,      EBML_NONE },
 +    { 0 }
 +};
 +static const EbmlSyntax matroska_track_encoding[] = {
      { MATROSKA_ID_ENCODINGSCOPE,       EBML_UINT, 0, offsetof(MatroskaTrackEncoding, scope),       { .u = 1 } },
      { MATROSKA_ID_ENCODINGTYPE,        EBML_UINT, 0, offsetof(MatroskaTrackEncoding, type),        { .u = 0 } },
      { MATROSKA_ID_ENCODINGCOMPRESSION, EBML_NEST, 0, offsetof(MatroskaTrackEncoding, compression), { .n = matroska_track_encoding_compression } },
 +    { MATROSKA_ID_ENCODINGENCRYPTION,  EBML_NEST, 0, offsetof(MatroskaTrackEncoding, encryption),  { .n = matroska_track_encoding_encryption } },
      { MATROSKA_ID_ENCODINGORDER,       EBML_NONE },
      { 0 }
  };
  
 -static EbmlSyntax matroska_track_encodings[] = {
 +static const EbmlSyntax matroska_track_encodings[] = {
      { MATROSKA_ID_TRACKCONTENTENCODING, EBML_NEST, sizeof(MatroskaTrackEncoding), offsetof(MatroskaTrack, encodings), { .n = matroska_track_encoding } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_track[] = {
 +static const EbmlSyntax matroska_track_plane[] = {
 +    { MATROSKA_ID_TRACKPLANEUID,  EBML_UINT, 0, offsetof(MatroskaTrackPlane,uid) },
 +    { MATROSKA_ID_TRACKPLANETYPE, EBML_UINT, 0, offsetof(MatroskaTrackPlane,type) },
 +    { 0 }
 +};
 +
 +static const EbmlSyntax matroska_track_combine_planes[] = {
 +    { MATROSKA_ID_TRACKPLANE, EBML_NEST, sizeof(MatroskaTrackPlane), offsetof(MatroskaTrackOperation,combine_planes), {.n = matroska_track_plane} },
 +    { 0 }
 +};
 +
 +static const EbmlSyntax matroska_track_operation[] = {
 +    { MATROSKA_ID_TRACKCOMBINEPLANES, EBML_NEST, 0, 0, {.n = matroska_track_combine_planes} },
 +    { 0 }
 +};
 +
 +static const EbmlSyntax matroska_track[] = {
      { MATROSKA_ID_TRACKNUMBER,           EBML_UINT,  0, offsetof(MatroskaTrack, num) },
      { MATROSKA_ID_TRACKNAME,             EBML_UTF8,  0, offsetof(MatroskaTrack, name) },
      { MATROSKA_ID_TRACKUID,              EBML_UINT,  0, offsetof(MatroskaTrack, uid) },
      { MATROSKA_ID_CODECID,               EBML_STR,   0, offsetof(MatroskaTrack, codec_id) },
      { MATROSKA_ID_CODECPRIVATE,          EBML_BIN,   0, offsetof(MatroskaTrack, codec_priv) },
      { MATROSKA_ID_CODECDELAY,            EBML_UINT,  0, offsetof(MatroskaTrack, codec_delay) },
 -    { MATROSKA_ID_TRACKLANGUAGE,         EBML_UTF8,  0, offsetof(MatroskaTrack, language),     { .s = "eng"  } },
 +    { MATROSKA_ID_TRACKLANGUAGE,         EBML_UTF8,  0, offsetof(MatroskaTrack, language),     { .s = "eng" } },
      { MATROSKA_ID_TRACKDEFAULTDURATION,  EBML_UINT,  0, offsetof(MatroskaTrack, default_duration) },
 -    { MATROSKA_ID_TRACKTIMECODESCALE,    EBML_FLOAT, 0, offsetof(MatroskaTrack, time_scale),   { .f = 1.0  } },
 -    { MATROSKA_ID_TRACKFLAGDEFAULT,      EBML_UINT,  0, offsetof(MatroskaTrack, flag_default), { .u = 1  } },
 -    { MATROSKA_ID_TRACKFLAGFORCED,       EBML_UINT,  0, offsetof(MatroskaTrack, flag_forced),  { .u = 0   } },
 +    { MATROSKA_ID_TRACKTIMECODESCALE,    EBML_FLOAT, 0, offsetof(MatroskaTrack, time_scale),   { .f = 1.0 } },
 +    { MATROSKA_ID_TRACKFLAGDEFAULT,      EBML_UINT,  0, offsetof(MatroskaTrack, flag_default), { .u = 1 } },
 +    { MATROSKA_ID_TRACKFLAGFORCED,       EBML_UINT,  0, offsetof(MatroskaTrack, flag_forced),  { .u = 0 } },
      { MATROSKA_ID_TRACKVIDEO,            EBML_NEST,  0, offsetof(MatroskaTrack, video),        { .n = matroska_track_video } },
      { MATROSKA_ID_TRACKAUDIO,            EBML_NEST,  0, offsetof(MatroskaTrack, audio),        { .n = matroska_track_audio } },
 +    { MATROSKA_ID_TRACKOPERATION,        EBML_NEST,  0, offsetof(MatroskaTrack, operation),    { .n = matroska_track_operation } },
      { MATROSKA_ID_TRACKCONTENTENCODINGS, EBML_NEST,  0, 0,                                     { .n = matroska_track_encodings } },
 +    { MATROSKA_ID_TRACKMAXBLKADDID,      EBML_UINT,  0, offsetof(MatroskaTrack, max_block_additional_id) },
 +    { MATROSKA_ID_SEEKPREROLL,           EBML_UINT,  0, offsetof(MatroskaTrack, seek_preroll) },
      { MATROSKA_ID_TRACKFLAGENABLED,      EBML_NONE },
      { MATROSKA_ID_TRACKFLAGLACING,       EBML_NONE },
      { MATROSKA_ID_CODECNAME,             EBML_NONE },
      { MATROSKA_ID_CODECDOWNLOADURL,      EBML_NONE },
      { MATROSKA_ID_TRACKMINCACHE,         EBML_NONE },
      { MATROSKA_ID_TRACKMAXCACHE,         EBML_NONE },
 -    { MATROSKA_ID_TRACKMAXBLKADDID,      EBML_NONE },
      { 0 }
  };
  
 -static EbmlSyntax matroska_tracks[] = {
 +static const EbmlSyntax matroska_tracks[] = {
      { MATROSKA_ID_TRACKENTRY, EBML_NEST, sizeof(MatroskaTrack), offsetof(MatroskaDemuxContext, tracks), { .n = matroska_track } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_attachment[] = {
 +static const EbmlSyntax matroska_attachment[] = {
      { MATROSKA_ID_FILEUID,      EBML_UINT, 0, offsetof(MatroskaAttachment, uid) },
      { MATROSKA_ID_FILENAME,     EBML_UTF8, 0, offsetof(MatroskaAttachment, filename) },
      { MATROSKA_ID_FILEMIMETYPE, EBML_STR,  0, offsetof(MatroskaAttachment, mime) },
      { 0 }
  };
  
 -static EbmlSyntax matroska_attachments[] = {
 +static const EbmlSyntax matroska_attachments[] = {
      { MATROSKA_ID_ATTACHEDFILE, EBML_NEST, sizeof(MatroskaAttachment), offsetof(MatroskaDemuxContext, attachments), { .n = matroska_attachment } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_chapter_display[] = {
 -    { MATROSKA_ID_CHAPSTRING, EBML_UTF8, 0, offsetof(MatroskaChapter, title) },
 -    { MATROSKA_ID_CHAPLANG,   EBML_NONE },
 +static const EbmlSyntax matroska_chapter_display[] = {
 +    { MATROSKA_ID_CHAPSTRING,  EBML_UTF8, 0, offsetof(MatroskaChapter, title) },
 +    { MATROSKA_ID_CHAPLANG,    EBML_NONE },
 +    { MATROSKA_ID_CHAPCOUNTRY, EBML_NONE },
      { 0 }
  };
  
 -static EbmlSyntax matroska_chapter_entry[] = {
 +static const EbmlSyntax matroska_chapter_entry[] = {
      { MATROSKA_ID_CHAPTERTIMESTART,   EBML_UINT, 0, offsetof(MatroskaChapter, start), { .u = AV_NOPTS_VALUE } },
      { MATROSKA_ID_CHAPTERTIMEEND,     EBML_UINT, 0, offsetof(MatroskaChapter, end),   { .u = AV_NOPTS_VALUE } },
      { MATROSKA_ID_CHAPTERUID,         EBML_UINT, 0, offsetof(MatroskaChapter, uid) },
      { 0 }
  };
  
 -static EbmlSyntax matroska_chapter[] = {
 +static const EbmlSyntax matroska_chapter[] = {
      { MATROSKA_ID_CHAPTERATOM,        EBML_NEST, sizeof(MatroskaChapter), offsetof(MatroskaDemuxContext, chapters), { .n = matroska_chapter_entry } },
      { MATROSKA_ID_EDITIONUID,         EBML_NONE },
      { MATROSKA_ID_EDITIONFLAGHIDDEN,  EBML_NONE },
      { 0 }
  };
  
 -static EbmlSyntax matroska_chapters[] = {
 +static const EbmlSyntax matroska_chapters[] = {
      { MATROSKA_ID_EDITIONENTRY, EBML_NEST, 0, 0, { .n = matroska_chapter } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_index_pos[] = {
 +static const EbmlSyntax matroska_index_pos[] = {
      { MATROSKA_ID_CUETRACK,           EBML_UINT, 0, offsetof(MatroskaIndexPos, track) },
      { MATROSKA_ID_CUECLUSTERPOSITION, EBML_UINT, 0, offsetof(MatroskaIndexPos, pos) },
 +    { MATROSKA_ID_CUERELATIVEPOSITION,EBML_NONE },
 +    { MATROSKA_ID_CUEDURATION,        EBML_NONE },
      { MATROSKA_ID_CUEBLOCKNUMBER,     EBML_NONE },
      { 0 }
  };
  
 -static EbmlSyntax matroska_index_entry[] = {
 +static const EbmlSyntax matroska_index_entry[] = {
      { MATROSKA_ID_CUETIME,          EBML_UINT, 0,                        offsetof(MatroskaIndex, time) },
      { MATROSKA_ID_CUETRACKPOSITION, EBML_NEST, sizeof(MatroskaIndexPos), offsetof(MatroskaIndex, pos), { .n = matroska_index_pos } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_index[] = {
 +static const EbmlSyntax matroska_index[] = {
      { MATROSKA_ID_POINTENTRY, EBML_NEST, sizeof(MatroskaIndex), offsetof(MatroskaDemuxContext, index), { .n = matroska_index_entry } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_simpletag[] = {
 +static const EbmlSyntax matroska_simpletag[] = {
      { MATROSKA_ID_TAGNAME,        EBML_UTF8, 0,                   offsetof(MatroskaTag, name) },
      { MATROSKA_ID_TAGSTRING,      EBML_UTF8, 0,                   offsetof(MatroskaTag, string) },
      { MATROSKA_ID_TAGLANG,        EBML_STR,  0,                   offsetof(MatroskaTag, lang), { .s = "und" } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_tagtargets[] = {
 +static const EbmlSyntax matroska_tagtargets[] = {
      { MATROSKA_ID_TAGTARGETS_TYPE,       EBML_STR,  0, offsetof(MatroskaTagTarget, type) },
      { MATROSKA_ID_TAGTARGETS_TYPEVALUE,  EBML_UINT, 0, offsetof(MatroskaTagTarget, typevalue), { .u = 50 } },
      { MATROSKA_ID_TAGTARGETS_TRACKUID,   EBML_UINT, 0, offsetof(MatroskaTagTarget, trackuid) },
      { 0 }
  };
  
 -static EbmlSyntax matroska_tag[] = {
 +static const EbmlSyntax matroska_tag[] = {
      { MATROSKA_ID_SIMPLETAG,  EBML_NEST, sizeof(MatroskaTag), offsetof(MatroskaTags, tag),    { .n = matroska_simpletag } },
      { MATROSKA_ID_TAGTARGETS, EBML_NEST, 0,                   offsetof(MatroskaTags, target), { .n = matroska_tagtargets } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_tags[] = {
 +static const EbmlSyntax matroska_tags[] = {
      { MATROSKA_ID_TAG, EBML_NEST, sizeof(MatroskaTags), offsetof(MatroskaDemuxContext, tags), { .n = matroska_tag } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_seekhead_entry[] = {
 +static const EbmlSyntax matroska_seekhead_entry[] = {
      { MATROSKA_ID_SEEKID,       EBML_UINT, 0, offsetof(MatroskaSeekhead, id) },
      { MATROSKA_ID_SEEKPOSITION, EBML_UINT, 0, offsetof(MatroskaSeekhead, pos), { .u = -1 } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_seekhead[] = {
 +static const EbmlSyntax matroska_seekhead[] = {
      { MATROSKA_ID_SEEKENTRY, EBML_NEST, sizeof(MatroskaSeekhead), offsetof(MatroskaDemuxContext, seekhead), { .n = matroska_seekhead_entry } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_segment[] = {
 -    { MATROSKA_ID_INFO,        EBML_NEST, 0, 0, { .n = matroska_info } },
 -    { MATROSKA_ID_TRACKS,      EBML_NEST, 0, 0, { .n = matroska_tracks } },
 -    { MATROSKA_ID_ATTACHMENTS, EBML_NEST, 0, 0, { .n = matroska_attachments } },
 -    { MATROSKA_ID_CHAPTERS,    EBML_NEST, 0, 0, { .n = matroska_chapters } },
 -    { MATROSKA_ID_CUES,        EBML_NEST, 0, 0, { .n = matroska_index } },
 -    { MATROSKA_ID_TAGS,        EBML_NEST, 0, 0, { .n = matroska_tags } },
 -    { MATROSKA_ID_SEEKHEAD,    EBML_NEST, 0, 0, { .n = matroska_seekhead } },
 +static const EbmlSyntax matroska_segment[] = {
 +    { MATROSKA_ID_INFO,        EBML_LEVEL1, 0, 0, { .n = matroska_info } },
 +    { MATROSKA_ID_TRACKS,      EBML_LEVEL1, 0, 0, { .n = matroska_tracks } },
 +    { MATROSKA_ID_ATTACHMENTS, EBML_LEVEL1, 0, 0, { .n = matroska_attachments } },
 +    { MATROSKA_ID_CHAPTERS,    EBML_LEVEL1, 0, 0, { .n = matroska_chapters } },
 +    { MATROSKA_ID_CUES,        EBML_LEVEL1, 0, 0, { .n = matroska_index } },
 +    { MATROSKA_ID_TAGS,        EBML_LEVEL1, 0, 0, { .n = matroska_tags } },
 +    { MATROSKA_ID_SEEKHEAD,    EBML_LEVEL1, 0, 0, { .n = matroska_seekhead } },
      { MATROSKA_ID_CLUSTER,     EBML_STOP },
      { 0 }
  };
  
 -static EbmlSyntax matroska_segments[] = {
 +static const EbmlSyntax matroska_segments[] = {
      { MATROSKA_ID_SEGMENT, EBML_NEST, 0, 0, { .n = matroska_segment } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_blockgroup[] = {
 +static const EbmlSyntax matroska_blockmore[] = {
 +    { MATROSKA_ID_BLOCKADDID,      EBML_UINT, 0, offsetof(MatroskaBlock,additional_id) },
 +    { MATROSKA_ID_BLOCKADDITIONAL, EBML_BIN,  0, offsetof(MatroskaBlock,additional) },
 +    { 0 }
 +};
 +
 +static const EbmlSyntax matroska_blockadditions[] = {
 +    { MATROSKA_ID_BLOCKMORE, EBML_NEST, 0, 0, {.n = matroska_blockmore} },
 +    { 0 }
 +};
 +
 +static const EbmlSyntax matroska_blockgroup[] = {
      { MATROSKA_ID_BLOCK,          EBML_BIN,  0, offsetof(MatroskaBlock, bin) },
 +    { MATROSKA_ID_BLOCKADDITIONS, EBML_NEST, 0, 0, { .n = matroska_blockadditions} },
      { MATROSKA_ID_SIMPLEBLOCK,    EBML_BIN,  0, offsetof(MatroskaBlock, bin) },
 -    { MATROSKA_ID_BLOCKDURATION,  EBML_UINT, 0, offsetof(MatroskaBlock, duration), { .u = AV_NOPTS_VALUE } },
 -    { MATROSKA_ID_BLOCKREFERENCE, EBML_UINT, 0, offsetof(MatroskaBlock, reference) },
 +    { MATROSKA_ID_BLOCKDURATION,  EBML_UINT, 0, offsetof(MatroskaBlock, duration) },
 +    { MATROSKA_ID_DISCARDPADDING, EBML_SINT, 0, offsetof(MatroskaBlock, discard_padding) },
 +    { MATROSKA_ID_BLOCKREFERENCE, EBML_SINT, 0, offsetof(MatroskaBlock, reference), { .i = INT64_MIN } },
      { MATROSKA_ID_CODECSTATE,     EBML_NONE },
      {                          1, EBML_UINT, 0, offsetof(MatroskaBlock, non_simple), { .u = 1 } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_cluster[] = {
 +static const EbmlSyntax matroska_cluster[] = {
      { MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0,                     offsetof(MatroskaCluster, timecode) },
      { MATROSKA_ID_BLOCKGROUP,      EBML_NEST, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
      { MATROSKA_ID_SIMPLEBLOCK,     EBML_PASS, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_clusters[] = {
 +static const EbmlSyntax matroska_clusters[] = {
      { MATROSKA_ID_CLUSTER,  EBML_NEST, 0, 0, { .n = matroska_cluster } },
      { MATROSKA_ID_INFO,     EBML_NONE },
      { MATROSKA_ID_CUES,     EBML_NONE },
      { 0 }
  };
  
 -static EbmlSyntax matroska_cluster_incremental_parsing[] = {
 +static const EbmlSyntax matroska_cluster_incremental_parsing[] = {
      { MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0,                     offsetof(MatroskaCluster, timecode) },
      { MATROSKA_ID_BLOCKGROUP,      EBML_NEST, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
      { MATROSKA_ID_SIMPLEBLOCK,     EBML_PASS, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },
      { 0 }
  };
  
 -static EbmlSyntax matroska_cluster_incremental[] = {
 +static const EbmlSyntax matroska_cluster_incremental[] = {
      { MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0, offsetof(MatroskaCluster, timecode) },
      { MATROSKA_ID_BLOCKGROUP,      EBML_STOP },
      { MATROSKA_ID_SIMPLEBLOCK,     EBML_STOP },
      { 0 }
  };
  
 -static EbmlSyntax matroska_clusters_incremental[] = {
 +static const EbmlSyntax matroska_clusters_incremental[] = {
      { MATROSKA_ID_CLUSTER,  EBML_NEST, 0, 0, { .n = matroska_cluster_incremental } },
      { MATROSKA_ID_INFO,     EBML_NONE },
      { MATROSKA_ID_CUES,     EBML_NONE },
  
  static const char *const matroska_doctypes[] = { "matroska", "webm" };
  
 +static int matroska_read_close(AVFormatContext *s);
 +
  static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos)
  {
      AVIOContext *pb = matroska->ctx->pb;
 +    int64_t ret;
      uint32_t id;
      matroska->current_id = 0;
      matroska->num_levels = 0;
  
      /* seek to next position to resync from */
 -    if (avio_seek(pb, last_pos + 1, SEEK_SET) < 0)
 -        goto eof;
 +    if ((ret = avio_seek(pb, last_pos + 1, SEEK_SET)) < 0) {
 +        matroska->done = 1;
 +        return ret;
 +    }
  
      id = avio_rb32(pb);
  
      // try to find a toplevel element
 -    while (!pb->eof_reached) {
 +    while (!avio_feof(pb)) {
          if (id == MATROSKA_ID_INFO     || id == MATROSKA_ID_TRACKS      ||
              id == MATROSKA_ID_CUES     || id == MATROSKA_ID_TAGS        ||
              id == MATROSKA_ID_SEEKHEAD || id == MATROSKA_ID_ATTACHMENTS ||
          id = (id << 8) | avio_r8(pb);
      }
  
 -eof:
      matroska->done = 1;
      return AVERROR_EOF;
  }
@@@ -807,7 -637,7 +807,7 @@@ static int ebml_level_end(MatroskaDemux
              return 1;
          }
      }
 -    return 0;
 +    return (matroska->is_live && matroska->ctx->pb->eof_reached) ? 1 : 0;
  }
  
  /*
@@@ -829,7 -659,7 +829,7 @@@ static int ebml_read_num(MatroskaDemuxC
       * use it safely here to catch EOS. */
      if (!(total = avio_r8(pb))) {
          /* we might encounter EOS here */
 -        if (!pb->eof_reached) {
 +        if (!avio_feof(pb)) {
              int64_t pos = avio_tell(pb);
              av_log(matroska->ctx, AV_LOG_ERROR,
                     "Read error at pos. %"PRIu64" (0x%"PRIx64")\n",
@@@ -892,30 -722,6 +892,30 @@@ static int ebml_read_uint(AVIOContext *
      return 0;
  }
  
 +/*
 + * Read the next element as a signed int.
 + * 0 is success, < 0 is failure.
 + */
 +static int ebml_read_sint(AVIOContext *pb, int size, int64_t *num)
 +{
 +    int n = 1;
 +
 +    if (size > 8)
 +        return AVERROR_INVALIDDATA;
 +
 +    if (size == 0) {
 +        *num = 0;
 +    } else {
 +        *num = sign_extend(avio_r8(pb), 8);
 +
 +        /* big-endian ordering; build up number */
 +        while (n++ < size)
 +            *num = ((uint64_t)*num << 8) | avio_r8(pb);
 +    }
 +
 +    return 0;
 +}
 +
  /*
   * Read the next element as a float.
   * 0 is success, < 0 is failure.
@@@ -963,18 -769,20 +963,18 @@@ static int ebml_read_ascii(AVIOContext 
   */
  static int ebml_read_binary(AVIOContext *pb, int length, EbmlBin *bin)
  {
 -    av_free(bin->data);
 -    bin->size = 0;
 -
 -    if (!(bin->data = av_mallocz(length + AV_INPUT_BUFFER_PADDING_SIZE)))
 +    av_fast_padded_malloc(&bin->data, &bin->size, length);
 +    if (!bin->data)
          return AVERROR(ENOMEM);
  
 +    bin->size = length;
      bin->pos  = avio_tell(pb);
      if (avio_read(pb, bin->data, length) != length) {
          av_freep(&bin->data);
 +        bin->size = 0;
          return AVERROR(EIO);
      }
  
 -    bin->size = length;
 -
      return 0;
  }
  
@@@ -1047,7 -855,9 +1047,7 @@@ static int ebml_parse_id(MatroskaDemuxC
          matroska->levels[matroska->num_levels - 1].length == 0xffffffffffffff)
          return 0;  // we reached the end of an unknown size cluster
      if (!syntax[i].id && id != EBML_ID_VOID && id != EBML_ID_CRC32) {
 -        av_log(matroska->ctx, AV_LOG_INFO, "Unknown entry 0x%"PRIX32"\n", id);
 -        if (matroska->ctx->error_recognition & AV_EF_EXPLODE)
 -            return AVERROR_INVALIDDATA;
 +        av_log(matroska->ctx, AV_LOG_DEBUG, "Unknown entry 0x%"PRIX32"\n", id);
      }
      return ebml_parse_elem(matroska, &syntax[i], data);
  }
@@@ -1058,11 -868,8 +1058,11 @@@ static int ebml_parse(MatroskaDemuxCont
      if (!matroska->current_id) {
          uint64_t id;
          int res = ebml_read_num(matroska, matroska->ctx->pb, 4, &id);
 -        if (res < 0)
 -            return res;
 +        if (res < 0) {
 +            // in live mode, finish parsing if EOF is reached.
 +            return (matroska->is_live && matroska->ctx->pb->eof_reached &&
 +                    res == AVERROR_EOF) ? 1 : res;
 +        }
          matroska->current_id = id | 1 << 7 * res;
      }
      return ebml_parse_id(matroska, syntax, matroska->current_id, data);
@@@ -1075,9 -882,6 +1075,9 @@@ static int ebml_parse_nest(MatroskaDemu
  
      for (i = 0; syntax[i].id; i++)
          switch (syntax[i].type) {
 +        case EBML_SINT:
 +            *(int64_t *) ((char *) data + syntax[i].data_offset) = syntax[i].def.i;
 +            break;
          case EBML_UINT:
              *(uint64_t *) ((char *) data + syntax[i].data_offset) = syntax[i].def.u;
              break;
      return res;
  }
  
 +static int is_ebml_id_valid(uint32_t id)
 +{
 +    // Due to endian nonsense in Matroska, the highest byte with any bits set
 +    // will contain the leading length bit. This bit in turn identifies the
 +    // total byte length of the element by its position within the byte.
 +    unsigned int bits = av_log2(id);
 +    return id && (bits + 7) / 8 ==  (8 - bits % 8);
 +}
 +
 +/*
 + * Allocate and return the entry for the level1 element with the given ID. If
 + * an entry already exists, return the existing entry.
 + */
 +static MatroskaLevel1Element *matroska_find_level1_elem(MatroskaDemuxContext *matroska,
 +                                                        uint32_t id)
 +{
 +    int i;
 +    MatroskaLevel1Element *elem;
 +
 +    if (!is_ebml_id_valid(id))
 +        return NULL;
 +
 +    // Some files link to all clusters; useless.
 +    if (id == MATROSKA_ID_CLUSTER)
 +        return NULL;
 +
 +    // There can be multiple seekheads.
 +    if (id != MATROSKA_ID_SEEKHEAD) {
 +        for (i = 0; i < matroska->num_level1_elems; i++) {
 +            if (matroska->level1_elems[i].id == id)
 +                return &matroska->level1_elems[i];
 +        }
 +    }
 +
 +    // Only a completely broken file would have more elements.
 +    // It also provides a low-effort way to escape from circular seekheads
 +    // (every iteration will add a level1 entry).
 +    if (matroska->num_level1_elems >= FF_ARRAY_ELEMS(matroska->level1_elems)) {
 +        av_log(matroska->ctx, AV_LOG_ERROR, "Too many level1 elements or circular seekheads.\n");
 +        return NULL;
 +    }
 +
 +    elem = &matroska->level1_elems[matroska->num_level1_elems++];
 +    *elem = (MatroskaLevel1Element){.id = id};
 +
 +    return elem;
 +}
 +
  static int ebml_parse_elem(MatroskaDemuxContext *matroska,
                             EbmlSyntax *syntax, void *data)
  {
      uint32_t id = syntax->id;
      uint64_t length;
      int res;
 +    void *newelem;
 +    MatroskaLevel1Element *level1_elem;
  
      data = (char *) data + syntax->data_offset;
      if (syntax->list_elem_size) {
          EbmlList *list = data;
 -        if ((res = av_reallocp_array(&list->elem,
 -                                     list->nb_elem + 1,
 -                                     syntax->list_elem_size)) < 0) {
 -            list->nb_elem = 0;
 -            return res;
 -        }
 +        newelem = av_realloc_array(list->elem, list->nb_elem + 1, syntax->list_elem_size);
 +        if (!newelem)
 +            return AVERROR(ENOMEM);
 +        list->elem = newelem;
          data = (char *) list->elem + list->nb_elem * syntax->list_elem_size;
          memset(data, 0, syntax->list_elem_size);
          list->nb_elem++;
      case EBML_UINT:
          res = ebml_read_uint(pb, length, data);
          break;
 +    case EBML_SINT:
 +        res = ebml_read_sint(pb, length, data);
 +        break;
      case EBML_FLOAT:
          res = ebml_read_float(pb, length, data);
          break;
      case EBML_BIN:
          res = ebml_read_binary(pb, length, data);
          break;
 +    case EBML_LEVEL1:
      case EBML_NEST:
          if ((res = ebml_read_master(matroska, length)) < 0)
              return res;
          if (id == MATROSKA_ID_SEGMENT)
              matroska->segment_start = avio_tell(matroska->ctx->pb);
 +        if (id == MATROSKA_ID_CUES)
 +            matroska->cues_parsing_deferred = 0;
 +        if (syntax->type == EBML_LEVEL1 &&
 +            (level1_elem = matroska_find_level1_elem(matroska, syntax->id))) {
 +            if (level1_elem->parsed)
 +                av_log(matroska->ctx, AV_LOG_ERROR, "Duplicate element\n");
 +            level1_elem->parsed = 1;
 +        }
          return ebml_parse_nest(matroska, syntax->def.n, data);
      case EBML_PASS:
          return ebml_parse_id(matroska, syntax->def.n, id, data);
      case EBML_STOP:
          return 1;
      default:
 +        if (ffio_limit(pb, length) != length)
 +            return AVERROR(EIO);
          return avio_skip(pb, length) < 0 ? AVERROR(EIO) : 0;
      }
      if (res == AVERROR_INVALIDDATA)
@@@ -1255,7 -997,6 +1255,7 @@@ static void ebml_free(EbmlSyntax *synta
          case EBML_BIN:
              av_freep(&((EbmlBin *) data_off)->data);
              break;
 +        case EBML_LEVEL1:
          case EBML_NEST:
              if (syntax[i].list_elem_size) {
                  EbmlList *list = data_off;
                  for (j = 0; j < list->nb_elem;
                       j++, ptr += syntax[i].list_elem_size)
                      ebml_free(syntax[i].def.n, ptr);
 -                av_free(list->elem);
 +                av_freep(&list->elem);
 +                list->nb_elem = 0;
              } else
                  ebml_free(syntax[i].def.n, data_off);
          default:
@@@ -1306,7 -1046,7 +1306,7 @@@ static int matroska_probe(AVProbeData *
       * availability of that array of characters inside the header.
       * Not fully fool-proof, but good enough. */
      for (i = 0; i < FF_ARRAY_ELEMS(matroska_doctypes); i++) {
 -        int probelen = strlen(matroska_doctypes[i]);
 +        size_t probelen = strlen(matroska_doctypes[i]);
          if (total < probelen)
              continue;
          for (n = 4 + size; n <= 4 + size + total - probelen; n++)
@@@ -1344,7 -1084,7 +1344,7 @@@ static int matroska_decode_buffer(uint8
      int result = 0;
      int olen;
  
 -    if (pkt_size >= 10000000)
 +    if (pkt_size >= 10000000U)
          return AVERROR_INVALIDDATA;
  
      switch (encodings[0].compression.algo) {
          int header_size = encodings[0].compression.settings.size;
          uint8_t *header = encodings[0].compression.settings.data;
  
 +        if (header_size && !header) {
 +            av_log(NULL, AV_LOG_ERROR, "Compression size but no data in headerstrip\n");
 +            return -1;
 +        }
 +
          if (!header_size)
              return 0;
  
              newpktdata = av_realloc(pkt_data, pkt_size);
              if (!newpktdata) {
                  inflateEnd(&zstream);
 +                result = AVERROR(ENOMEM);
                  goto failed;
              }
              pkt_data          = newpktdata;
              zstream.avail_out = pkt_size - zstream.total_out;
              zstream.next_out  = pkt_data + zstream.total_out;
 -            result            = inflate(&zstream, Z_NO_FLUSH);
 +            result = inflate(&zstream, Z_NO_FLUSH);
          } while (result == Z_OK && pkt_size < 10000000);
          pkt_size = zstream.total_out;
          inflateEnd(&zstream);
              newpktdata = av_realloc(pkt_data, pkt_size);
              if (!newpktdata) {
                  BZ2_bzDecompressEnd(&bzstream);
 +                result = AVERROR(ENOMEM);
                  goto failed;
              }
              pkt_data           = newpktdata;
              bzstream.avail_out = pkt_size - bzstream.total_out_lo32;
              bzstream.next_out  = pkt_data + bzstream.total_out_lo32;
 -            result             = BZ2_bzDecompress(&bzstream);
 +            result = BZ2_bzDecompress(&bzstream);
          } while (result == BZ_OK && pkt_size < 10000000);
          pkt_size = bzstream.total_out_lo32;
          BZ2_bzDecompressEnd(&bzstream);
@@@ -1468,6 -1201,63 +1468,6 @@@ failed
      return result;
  }
  
 -static void matroska_fix_ass_packet(MatroskaDemuxContext *matroska,
 -                                    AVPacket *pkt, uint64_t display_duration)
 -{
 -    AVBufferRef *line;
 -    char *layer, *ptr = pkt->data, *end = ptr + pkt->size;
 -
 -    for (; *ptr != ',' && ptr < end - 1; ptr++)
 -        ;
 -    if (*ptr == ',')
 -        layer = ++ptr;
 -    for (; *ptr != ',' && ptr < end - 1; ptr++)
 -        ;
 -    if (*ptr == ',') {
 -        int64_t end_pts = pkt->pts + display_duration;
 -        int sc = matroska->time_scale * pkt->pts / 10000000;
 -        int ec = matroska->time_scale * end_pts  / 10000000;
 -        int sh, sm, ss, eh, em, es, len;
 -        sh     = sc / 360000;
 -        sc    -= 360000 * sh;
 -        sm     = sc / 6000;
 -        sc    -= 6000 * sm;
 -        ss     = sc / 100;
 -        sc    -= 100 * ss;
 -        eh     = ec / 360000;
 -        ec    -= 360000 * eh;
 -        em     = ec / 6000;
 -        ec    -= 6000 * em;
 -        es     = ec / 100;
 -        ec    -= 100 * es;
 -        *ptr++ = '\0';
 -        len    = 50 + end - ptr + AV_INPUT_BUFFER_PADDING_SIZE;
 -        if (!(line = av_buffer_alloc(len)))
 -            return;
 -        snprintf(line->data, len,
 -                 "Dialogue: %s,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s\r\n",
 -                 layer, sh, sm, ss, sc, eh, em, es, ec, ptr);
 -        av_buffer_unref(&pkt->buf);
 -        pkt->buf  = line;
 -        pkt->data = line->data;
 -        pkt->size = strlen(line->data);
 -    }
 -}
 -
 -static int matroska_merge_packets(AVPacket *out, AVPacket *in)
 -{
 -    int old_size = out->size;
 -    int ret = av_grow_packet(out, in->size);
 -    if (ret < 0)
 -        return ret;
 -
 -    memcpy(out->data + old_size, in->data, in->size);
 -
 -    av_packet_unref(in);
 -    av_free(in);
 -    return 0;
 -}
 -
  static void matroska_convert_tag(AVFormatContext *s, EbmlList *list,
                                   AVDictionary **metadata, char *prefix)
  {
@@@ -1569,17 -1359,24 +1569,17 @@@ static void matroska_convert_tags(AVFor
  }
  
  static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska,
 -                                         int idx)
 +                                         uint64_t pos)
  {
 -    EbmlList *seekhead_list = &matroska->seekhead;
      uint32_t level_up       = matroska->level_up;
      uint32_t saved_id       = matroska->current_id;
 -    MatroskaSeekhead *seekhead = seekhead_list->elem;
      int64_t before_pos = avio_tell(matroska->ctx->pb);
      MatroskaLevel level;
      int64_t offset;
      int ret = 0;
  
 -    if (idx >= seekhead_list->nb_elem            ||
 -        seekhead[idx].id == MATROSKA_ID_SEEKHEAD ||
 -        seekhead[idx].id == MATROSKA_ID_CLUSTER)
 -        return 0;
 -
      /* seek */
 -    offset = seekhead[idx].pos + matroska->segment_start;
 +    offset = pos + matroska->segment_start;
      if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) == offset) {
          /* We don't want to lose our seekhead level, so we add
           * a dummy. This is a crude hack. */
  static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
  {
      EbmlList *seekhead_list = &matroska->seekhead;
 -    int64_t before_pos = avio_tell(matroska->ctx->pb);
      int i;
  
      // we should not do any seeking in the streaming case
 -    if (!(matroska->ctx->pb->seekable & AVIO_SEEKABLE_NORMAL) ||
 -        (matroska->ctx->flags & AVFMT_FLAG_IGNIDX))
 +    if (!(matroska->ctx->pb->seekable & AVIO_SEEKABLE_NORMAL))
          return;
  
      for (i = 0; i < seekhead_list->nb_elem; i++) {
 -        MatroskaSeekhead *seekhead = seekhead_list->elem;
 -        if (seekhead[i].pos <= before_pos)
 +        MatroskaSeekhead *seekheads = seekhead_list->elem;
 +        uint32_t id  = seekheads[i].id;
 +        uint64_t pos = seekheads[i].pos;
 +
 +        MatroskaLevel1Element *elem = matroska_find_level1_elem(matroska, id);
 +        if (!elem || elem->parsed)
              continue;
  
 +        elem->pos = pos;
 +
          // defer cues parsing until we actually need cue data.
 -        if (seekhead[i].id == MATROSKA_ID_CUES) {
 -            matroska->cues_parsing_deferred = 1;
 +        if (id == MATROSKA_ID_CUES)
              continue;
 -        }
  
 -        if (matroska_parse_seekhead_entry(matroska, i) < 0)
 +        if (matroska_parse_seekhead_entry(matroska, pos) < 0) {
 +            // mark index as broken
 +            matroska->cues_parsing_deferred = -1;
              break;
 +        }
 +
 +        elem->parsed = 1;
      }
  }
  
 -static void matroska_parse_cues(MatroskaDemuxContext *matroska)
 +static void matroska_add_index_entries(MatroskaDemuxContext *matroska)
  {
 -    EbmlList *seekhead_list = &matroska->seekhead;
 -    MatroskaSeekhead *seekhead = seekhead_list->elem;
      EbmlList *index_list;
      MatroskaIndex *index;
 -    int index_scale = 1;
 +    uint64_t index_scale = 1;
      int i, j;
  
 -    for (i = 0; i < seekhead_list->nb_elem; i++)
 -        if (seekhead[i].id == MATROSKA_ID_CUES)
 -            break;
 -    assert(i <= seekhead_list->nb_elem);
 -
 -    matroska_parse_seekhead_entry(matroska, i);
 +    if (matroska->ctx->flags & AVFMT_FLAG_IGNIDX)
 +        return;
  
      index_list = &matroska->index;
      index      = index_list->elem;
 -    if (index_list->nb_elem &&
 -        index[0].time > 1E14 / matroska->time_scale) {
 -        av_log(matroska->ctx, AV_LOG_WARNING, "Working around broken index.\n");
 -        index_scale = matroska->time_scale;
 +    if (index_list->nb_elem < 2)
 +        return;
 +    if (index[1].time > 1E14 / matroska->time_scale) {
 +        av_log(matroska->ctx, AV_LOG_WARNING, "Dropping apparently-broken index.\n");
 +        return;
      }
      for (i = 0; i < index_list->nb_elem; i++) {
          EbmlList *pos_list    = &index[i].pos;
      }
  }
  
 +static void matroska_parse_cues(MatroskaDemuxContext *matroska) {
 +    int i;
 +
 +    if (matroska->ctx->flags & AVFMT_FLAG_IGNIDX)
 +        return;
 +
 +    for (i = 0; i < matroska->num_level1_elems; i++) {
 +        MatroskaLevel1Element *elem = &matroska->level1_elems[i];
 +        if (elem->id == MATROSKA_ID_CUES && !elem->parsed) {
 +            if (matroska_parse_seekhead_entry(matroska, elem->pos) < 0)
 +                matroska->cues_parsing_deferred = -1;
 +            elem->parsed = 1;
 +            break;
 +        }
 +    }
 +
 +    matroska_add_index_entries(matroska);
 +}
 +
  static int matroska_aac_profile(char *codec_id)
  {
      static const char *const aac_profiles[] = { "MAIN", "LC", "SSR" };
@@@ -1720,12 -1496,6 +1720,12 @@@ static int matroska_aac_sri(int sampler
      return sri;
  }
  
 +static void matroska_metadata_creation_time(AVDictionary **metadata, int64_t date_utc)
 +{
 +    /* Convert to seconds and adjust by number of seconds between 2001-01-01 and Epoch */
 +    avpriv_dict_set_timestamp(metadata, "creation_time", date_utc / 1000 + 978307200000000LL);
 +}
 +
  static int matroska_parse_flac(AVFormatContext *s,
                                 MatroskaTrack *track,
                                 int *offset)
      return 0;
  }
  
 -static int mkv_field_order(int64_t field_order)
 +static int mkv_field_order(MatroskaDemuxContext *matroska, int64_t field_order)
  {
 +    int major, minor, micro, bttb = 0;
 +
 +    /* workaround a bug in our Matroska muxer, introduced in version 57.36 alongside
 +     * this function, and fixed in 57.52 */
 +    if (matroska->muxingapp && sscanf(matroska->muxingapp, "Lavf%d.%d.%d", &major, &minor, &micro) == 3)
 +        bttb = (major == 57 && minor >= 36 && minor <= 51 && micro >= 100);
 +
      switch (field_order) {
      case MATROSKA_VIDEO_FIELDORDER_PROGRESSIVE:
          return AV_FIELD_PROGRESSIVE;
      case MATROSKA_VIDEO_FIELDORDER_BB:
          return AV_FIELD_BB;
      case MATROSKA_VIDEO_FIELDORDER_BT:
 -        return AV_FIELD_BT;
 +        return bttb ? AV_FIELD_TB : AV_FIELD_BT;
      case MATROSKA_VIDEO_FIELDORDER_TB:
 -        return AV_FIELD_TB;
 +        return bttb ? AV_FIELD_BT : AV_FIELD_TB;
      default:
          return AV_FIELD_UNKNOWN;
      }
@@@ -1812,122 -1575,29 +1812,122 @@@ static void mkv_stereo_mode_display_mul
                                          int *h_width, int *h_height)
  {
      switch (stereo_mode) {
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_MONO:
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_RL:
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_LR:
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL:
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_LR:
 -        break;
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT:
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_LEFT_RIGHT:
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_RL:
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_LR:
 -        *h_width = 2;
 -        break;
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTTOM_TOP:
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM:
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_RL:
 -    case MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_LR:
 -        *h_height = 2;
 -        break;
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_MONO:
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_RL:
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_LR:
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL:
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_LR:
 +            break;
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT:
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_LEFT_RIGHT:
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_RL:
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_LR:
 +            *h_width = 2;
 +            break;
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTTOM_TOP:
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM:
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_RL:
 +        case MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_LR:
 +            *h_height = 2;
 +            break;
      }
  }
  
 -static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track)
 -{
 +static int mkv_parse_video_color(AVStream *st, const MatroskaTrack *track) {
 +    const MatroskaTrackVideoColor *color = track->video.color.elem;
 +    const MatroskaMasteringMeta *mastering_meta;
 +    int has_mastering_primaries, has_mastering_luminance;
 +
 +    if (!track->video.color.nb_elem)
 +        return 0;
 +
 +    mastering_meta = &color->mastering_meta;
 +    // Mastering primaries are CIE 1931 coords, and must be > 0.
 +    has_mastering_primaries =
 +        mastering_meta->r_x > 0 && mastering_meta->r_y > 0 &&
 +        mastering_meta->g_x > 0 && mastering_meta->g_y > 0 &&
 +        mastering_meta->b_x > 0 && mastering_meta->b_y > 0 &&
 +        mastering_meta->white_x > 0 && mastering_meta->white_y > 0;
 +    has_mastering_luminance = mastering_meta->max_luminance > 0;
 +
 +    if (color->matrix_coefficients != AVCOL_SPC_RESERVED)
 +        st->codecpar->color_space = color->matrix_coefficients;
 +    if (color->primaries != AVCOL_PRI_RESERVED &&
 +        color->primaries != AVCOL_PRI_RESERVED0)
 +        st->codecpar->color_primaries = color->primaries;
 +    if (color->transfer_characteristics != AVCOL_TRC_RESERVED &&
 +        color->transfer_characteristics != AVCOL_TRC_RESERVED0)
 +        st->codecpar->color_trc = color->transfer_characteristics;
 +    if (color->range != AVCOL_RANGE_UNSPECIFIED &&
 +        color->range <= AVCOL_RANGE_JPEG)
 +        st->codecpar->color_range = color->range;
 +    if (color->chroma_siting_horz != MATROSKA_COLOUR_CHROMASITINGHORZ_UNDETERMINED &&
 +        color->chroma_siting_vert != MATROSKA_COLOUR_CHROMASITINGVERT_UNDETERMINED &&
 +        color->chroma_siting_horz  < MATROSKA_COLOUR_CHROMASITINGHORZ_NB &&
 +        color->chroma_siting_vert  < MATROSKA_COLOUR_CHROMASITINGVERT_NB) {
 +        st->codecpar->chroma_location =
 +            avcodec_chroma_pos_to_enum((color->chroma_siting_horz - 1) << 7,
 +                                       (color->chroma_siting_vert - 1) << 7);
 +    }
 +    if (color->max_cll && color->max_fall) {
 +        size_t size = 0;
 +        int ret;
 +        AVContentLightMetadata *metadata = av_content_light_metadata_alloc(&size);
 +        if (!metadata)
 +            return AVERROR(ENOMEM);
 +        ret = av_stream_add_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
 +                                      (uint8_t *)metadata, size);
 +        if (ret < 0) {
 +            av_freep(&metadata);
 +            return ret;
 +        }
 +        metadata->MaxCLL  = color->max_cll;
 +        metadata->MaxFALL = color->max_fall;
 +    }
 +
 +    if (has_mastering_primaries || has_mastering_luminance) {
 +        // Use similar rationals as other standards.
 +        const int chroma_den = 50000;
 +        const int luma_den = 10000;
 +        AVMasteringDisplayMetadata *metadata =
 +            (AVMasteringDisplayMetadata*) av_stream_new_side_data(
 +                st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
 +                sizeof(AVMasteringDisplayMetadata));
 +        if (!metadata) {
 +            return AVERROR(ENOMEM);
 +        }
 +        memset(metadata, 0, sizeof(AVMasteringDisplayMetadata));
 +        if (has_mastering_primaries) {
 +            metadata->display_primaries[0][0] = av_make_q(
 +                round(mastering_meta->r_x * chroma_den), chroma_den);
 +            metadata->display_primaries[0][1] = av_make_q(
 +                round(mastering_meta->r_y * chroma_den), chroma_den);
 +            metadata->display_primaries[1][0] = av_make_q(
 +                round(mastering_meta->g_x * chroma_den), chroma_den);
 +            metadata->display_primaries[1][1] = av_make_q(
 +                round(mastering_meta->g_y * chroma_den), chroma_den);
 +            metadata->display_primaries[2][0] = av_make_q(
 +                round(mastering_meta->b_x * chroma_den), chroma_den);
 +            metadata->display_primaries[2][1] = av_make_q(
 +                round(mastering_meta->b_y * chroma_den), chroma_den);
 +            metadata->white_point[0] = av_make_q(
 +                round(mastering_meta->white_x * chroma_den), chroma_den);
 +            metadata->white_point[1] = av_make_q(
 +                round(mastering_meta->white_y * chroma_den), chroma_den);
 +            metadata->has_primaries = 1;
 +        }
 +        if (has_mastering_luminance) {
 +            metadata->max_luminance = av_make_q(
 +                round(mastering_meta->max_luminance * luma_den), luma_den);
 +            metadata->min_luminance = av_make_q(
 +                round(mastering_meta->min_luminance * luma_den), luma_den);
 +            metadata->has_luminance = 1;
 +        }
 +    }
 +    return 0;
 +}
 +
 +static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track) {
      AVSphericalMapping *spherical;
      enum AVSphericalProjection projection;
      size_t spherical_size;
              if (b >= UINT_MAX - t || r >= UINT_MAX - l) {
                  av_log(NULL, AV_LOG_ERROR,
                         "Invalid bounding rectangle coordinates "
 -                       "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n", l, t, r, b);
 +                       "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n",
 +                       l, t, r, b);
                  return AVERROR_INVALIDDATA;
              }
          } else if (track->video.projection.private.size != 0) {
              return AVERROR_INVALIDDATA;
          }
          break;
+     case MATROSKA_VIDEO_PROJECTION_TYPE_RECTANGULAR:
+         /* No Spherical metadata */
+         return 0;
      default:
+         av_log(NULL, AV_LOG_WARNING,
+                "Unknown spherical metadata type %"PRIu64"\n",
+                track->video.projection.type);
          return 0;
      }
  
      ret = av_stream_add_side_data(st, AV_PKT_DATA_SPHERICAL, (uint8_t *)spherical,
                                    spherical_size);
      if (ret < 0) {
 -        av_free(spherical);
 +        av_freep(&spherical);
          return ret;
      }
  
      return 0;
  }
  
 +static int get_qt_codec(MatroskaTrack *track, uint32_t *fourcc, enum AVCodecID *codec_id)
 +{
 +    const AVCodecTag *codec_tags;
 +
 +    codec_tags = track->type == MATROSKA_TRACK_TYPE_VIDEO ?
 +            ff_codec_movvideo_tags : ff_codec_movaudio_tags;
 +
 +    /* Normalize noncompliant private data that starts with the fourcc
 +     * by expanding/shifting the data by 4 bytes and storing the data
 +     * size at the start. */
 +    if (ff_codec_get_id(codec_tags, AV_RL32(track->codec_priv.data))) {
 +        uint8_t *p = av_realloc(track->codec_priv.data,
 +                                track->codec_priv.size + 4);
 +        if (!p)
 +            return AVERROR(ENOMEM);
 +        memmove(p + 4, p, track->codec_priv.size);
 +        track->codec_priv.data = p;
 +        track->codec_priv.size += 4;
 +        AV_WB32(track->codec_priv.data, track->codec_priv.size);
 +    }
 +
 +    *fourcc = AV_RL32(track->codec_priv.data + 4);
 +    *codec_id = ff_codec_get_id(codec_tags, *fourcc);
 +
 +    return 0;
 +}
 +
  static int matroska_parse_tracks(AVFormatContext *s)
  {
      MatroskaDemuxContext *matroska = s->priv_data;
      MatroskaTrack *tracks = matroska->tracks.elem;
      AVStream *st;
      int i, j, ret;
 +    int k;
  
      for (i = 0; i < matroska->tracks.nb_elem; i++) {
          MatroskaTrack *track = &tracks[i];
          uint8_t *extradata = NULL;
          int extradata_size = 0;
          int extradata_offset = 0;
 +        uint32_t fourcc = 0;
          AVIOContext b;
 +        char* key_id_base64 = NULL;
 +        int bit_depth = -1;
  
          /* Apply some sanity checks. */
          if (track->type != MATROSKA_TRACK_TYPE_VIDEO &&
              track->type != MATROSKA_TRACK_TYPE_AUDIO &&
 -            track->type != MATROSKA_TRACK_TYPE_SUBTITLE) {
 +            track->type != MATROSKA_TRACK_TYPE_SUBTITLE &&
 +            track->type != MATROSKA_TRACK_TYPE_METADATA) {
              av_log(matroska->ctx, AV_LOG_INFO,
                     "Unknown or unsupported track type %"PRIu64"\n",
                     track->type);
          if (!track->codec_id)
              continue;
  
 +        if (track->audio.samplerate < 0 || track->audio.samplerate > INT_MAX ||
 +            isnan(track->audio.samplerate)) {
 +            av_log(matroska->ctx, AV_LOG_WARNING,
 +                   "Invalid sample rate %f, defaulting to 8000 instead.\n",
 +                   track->audio.samplerate);
 +            track->audio.samplerate = 8000;
 +        }
 +
          if (track->type == MATROSKA_TRACK_TYPE_VIDEO) {
              if (!track->default_duration && track->video.frame_rate > 0)
                  track->default_duration = 1000000000 / track->video.frame_rate;
 -            if (!track->video.display_width)
 +            if (track->video.display_width == -1)
                  track->video.display_width = track->video.pixel_width;
 -            if (!track->video.display_height)
 +            if (track->video.display_height == -1)
                  track->video.display_height = track->video.pixel_height;
 +            if (track->video.color_space.size == 4)
 +                fourcc = AV_RL32(track->video.color_space.data);
          } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {
              if (!track->audio.out_samplerate)
                  track->audio.out_samplerate = track->audio.samplerate;
              av_log(matroska->ctx, AV_LOG_ERROR,
                     "Multiple combined encodings not supported");
          } else if (encodings_list->nb_elem == 1) {
 -            if (encodings[0].type ||
 -                (
 +            if (encodings[0].type) {
 +                if (encodings[0].encryption.key_id.size > 0) {
 +                    /* Save the encryption key id to be stored later as a
 +                       metadata tag. */
 +                    const int b64_size = AV_BASE64_SIZE(encodings[0].encryption.key_id.size);
 +                    key_id_base64 = av_malloc(b64_size);
 +                    if (key_id_base64 == NULL)
 +                        return AVERROR(ENOMEM);
 +
 +                    av_base64_encode(key_id_base64, b64_size,
 +                                     encodings[0].encryption.key_id.data,
 +                                     encodings[0].encryption.key_id.size);
 +                } else {
 +                    encodings[0].scope = 0;
 +                    av_log(matroska->ctx, AV_LOG_ERROR,
 +                           "Unsupported encoding type");
 +                }
 +            } else if (
  #if CONFIG_ZLIB
                   encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_ZLIB  &&
  #endif
  #if CONFIG_LZO
                   encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_LZO   &&
  #endif
 -                 encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP)) {
 +                 encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP) {
                  encodings[0].scope = 0;
                  av_log(matroska->ctx, AV_LOG_ERROR,
                         "Unsupported encoding type");
          }
  
          st = track->stream = avformat_new_stream(s, NULL);
 -        if (!st)
 +        if (!st) {
 +            av_free(key_id_base64);
              return AVERROR(ENOMEM);
 +        }
 +
 +        if (key_id_base64) {
 +            /* export encryption key id as base64 metadata tag */
 +            av_dict_set(&st->metadata, "enc_key_id", key_id_base64, 0);
 +            av_freep(&key_id_base64);
 +        }
  
          if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC") &&
 -            track->codec_priv.size >= 40                &&
 +             track->codec_priv.size >= 40               &&
              track->codec_priv.data) {
              track->ms_compat    = 1;
 -            track->video.fourcc = AV_RL32(track->codec_priv.data + 16);
 +            bit_depth           = AV_RL16(track->codec_priv.data + 14);
 +            fourcc              = AV_RL32(track->codec_priv.data + 16);
              codec_id            = ff_codec_get_id(ff_codec_bmp_tags,
 -                                                  track->video.fourcc);
 +                                                  fourcc);
 +            if (!codec_id)
 +                codec_id        = ff_codec_get_id(ff_codec_movvideo_tags,
 +                                                  fourcc);
              extradata_offset    = 40;
          } else if (!strcmp(track->codec_id, "A_MS/ACM") &&
                     track->codec_priv.size >= 14         &&
              ffio_init_context(&b, track->codec_priv.data,
                                track->codec_priv.size,
                                0, NULL, NULL, NULL, NULL);
 -            ret = ff_get_wav_header(s, &b, st->codecpar, track->codec_priv.size);
 +            ret = ff_get_wav_header(s, &b, st->codecpar, track->codec_priv.size, 0);
              if (ret < 0)
                  return ret;
              codec_id         = st->codecpar->codec_id;
 +            fourcc           = st->codecpar->codec_tag;
              extradata_offset = FFMIN(track->codec_priv.size, 18);
 +        } else if (!strcmp(track->codec_id, "A_QUICKTIME")
 +                   /* Normally 36, but allow noncompliant private data */
 +                   && (track->codec_priv.size >= 32)
 +                   && (track->codec_priv.data)) {
 +            uint16_t sample_size;
 +            int ret = get_qt_codec(track, &fourcc, &codec_id);
 +            if (ret < 0)
 +                return ret;
 +            sample_size = AV_RB16(track->codec_priv.data + 26);
 +            if (fourcc == 0) {
 +                if (sample_size == 8) {
 +                    fourcc = MKTAG('r','a','w',' ');
 +                    codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc);
 +                } else if (sample_size == 16) {
 +                    fourcc = MKTAG('t','w','o','s');
 +                    codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc);
 +                }
 +            }
 +            if ((fourcc == MKTAG('t','w','o','s') ||
 +                    fourcc == MKTAG('s','o','w','t')) &&
 +                    sample_size == 8)
 +                codec_id = AV_CODEC_ID_PCM_S8;
          } else if (!strcmp(track->codec_id, "V_QUICKTIME") &&
 -                   (track->codec_priv.size >= 86)          &&
 +                   (track->codec_priv.size >= 21)          &&
                     (track->codec_priv.data)) {
 -            if (track->codec_priv.size == AV_RB32(track->codec_priv.data)) {
 -                track->video.fourcc = AV_RL32(track->codec_priv.data + 4);
 -                codec_id            = ff_codec_get_id(ff_codec_movvideo_tags,
 -                                                      track->video.fourcc);
 -            }
 -            if (codec_id == AV_CODEC_ID_NONE) {
 -                track->video.fourcc = AV_RL32(track->codec_priv.data);
 -                codec_id            = ff_codec_get_id(ff_codec_movvideo_tags,
 -                                                      track->video.fourcc);
 +            int ret = get_qt_codec(track, &fourcc, &codec_id);
 +            if (ret < 0)
 +                return ret;
 +            if (codec_id == AV_CODEC_ID_NONE && AV_RL32(track->codec_priv.data+4) == AV_RL32("SMI ")) {
 +                fourcc = MKTAG('S','V','Q','3');
 +                codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc);
              }
 -            if (codec_id == AV_CODEC_ID_NONE) {
 -                char buf[32];
 -                av_get_codec_tag_string(buf, sizeof(buf), track->video.fourcc);
 +            if (codec_id == AV_CODEC_ID_NONE)
                  av_log(matroska->ctx, AV_LOG_ERROR,
 -                       "mov FourCC not found %s.\n", buf);
 +                       "mov FourCC not found %s.\n", av_fourcc2str(fourcc));
 +            if (track->codec_priv.size >= 86) {
 +                bit_depth = AV_RB16(track->codec_priv.data + 82);
 +                ffio_init_context(&b, track->codec_priv.data,
 +                                  track->codec_priv.size,
 +                                  0, NULL, NULL, NULL, NULL);
 +                if (ff_get_qtpalette(codec_id, &b, track->palette)) {
 +                    bit_depth &= 0x1F;
 +                    track->has_palette = 1;
 +                }
              }
          } else if (codec_id == AV_CODEC_ID_PCM_S16BE) {
              switch (track->audio.bitdepth) {
                  extradata_size = 5;
              } else
                  extradata_size = 2;
 -        } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size) {
 +        } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size && track->codec_priv.size < INT_MAX - 12 - AV_INPUT_BUFFER_PADDING_SIZE) {
              /* Only ALAC's magic cookie is stored in Matroska's track headers.
               * Create the "atom size", "tag", and "tag version" fields the
               * decoder expects manually. */
                     track->codec_priv.size);
          } else if (codec_id == AV_CODEC_ID_TTA) {
              extradata_size = 30;
 -            extradata      = av_mallocz(extradata_size);
 +            extradata      = av_mallocz(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
              if (!extradata)
                  return AVERROR(ENOMEM);
              ffio_init_context(&b, extradata, extradata_size, 1,
                                NULL, NULL, NULL, NULL);
              avio_write(&b, "TTA1", 4);
              avio_wl16(&b, 1);
 +            if (track->audio.channels > UINT16_MAX ||
 +                track->audio.bitdepth > UINT16_MAX) {
 +                av_log(matroska->ctx, AV_LOG_WARNING,
 +                       "Too large audio channel number %"PRIu64
 +                       " or bitdepth %"PRIu64". Skipping track.\n",
 +                       track->audio.channels, track->audio.bitdepth);
 +                av_freep(&extradata);
 +                if (matroska->ctx->error_recognition & AV_EF_EXPLODE)
 +                    return AVERROR_INVALIDDATA;
 +                else
 +                    continue;
 +            }
              avio_wl16(&b, track->audio.channels);
              avio_wl16(&b, track->audio.bitdepth);
 +            if (track->audio.out_samplerate < 0 || track->audio.out_samplerate > INT_MAX)
 +                return AVERROR_INVALIDDATA;
              avio_wl32(&b, track->audio.out_samplerate);
 -            avio_wl32(&b, matroska->ctx->duration *
 -                      track->audio.out_samplerate);
 +            avio_wl32(&b, av_rescale((matroska->duration * matroska->time_scale),
 +                                     track->audio.out_samplerate,
 +                                     AV_TIME_BASE * 1000));
          } else if (codec_id == AV_CODEC_ID_RV10 ||
                     codec_id == AV_CODEC_ID_RV20 ||
                     codec_id == AV_CODEC_ID_RV30 ||
          } else if (codec_id == AV_CODEC_ID_RA_144) {
              track->audio.out_samplerate = 8000;
              track->audio.channels       = 1;
 -        } else if (codec_id == AV_CODEC_ID_RA_288 ||
 -                   codec_id == AV_CODEC_ID_COOK   ||
 -                   codec_id == AV_CODEC_ID_ATRAC3 ||
 -                   codec_id == AV_CODEC_ID_SIPR) {
 +        } else if ((codec_id == AV_CODEC_ID_RA_288 ||
 +                    codec_id == AV_CODEC_ID_COOK   ||
 +                    codec_id == AV_CODEC_ID_ATRAC3 ||
 +                    codec_id == AV_CODEC_ID_SIPR)
 +                      && track->codec_priv.data) {
              int flavor;
 +
              ffio_init_context(&b, track->codec_priv.data,
                                track->codec_priv.size,
                                0, NULL, NULL, NULL, NULL);
              track->audio.sub_packet_h    = avio_rb16(&b);
              track->audio.frame_size      = avio_rb16(&b);
              track->audio.sub_packet_size = avio_rb16(&b);
 -            if (flavor                       <= 0 ||
 +            if (flavor                        < 0 ||
                  track->audio.coded_framesize <= 0 ||
                  track->audio.sub_packet_h    <= 0 ||
                  track->audio.frame_size      <= 0 ||
 -                track->audio.sub_packet_size <= 0)
 +                track->audio.sub_packet_size <= 0 && codec_id != AV_CODEC_ID_SIPR)
                  return AVERROR_INVALIDDATA;
 -            track->audio.buf = av_malloc(track->audio.frame_size *
 -                                         track->audio.sub_packet_h);
 +            track->audio.buf = av_malloc_array(track->audio.sub_packet_h,
 +                                               track->audio.frame_size);
              if (!track->audio.buf)
                  return AVERROR(ENOMEM);
              if (codec_id == AV_CODEC_ID_RA_288) {
              ret = matroska_parse_flac(s, track, &extradata_offset);
              if (ret < 0)
                  return ret;
 +        } else if (codec_id == AV_CODEC_ID_PRORES && track->codec_priv.size == 4) {
 +            fourcc = AV_RL32(track->codec_priv.data);
          }
          track->codec_priv.size -= extradata_offset;
  
          avpriv_set_pts_info(st, 64, matroska->time_scale * track->time_scale,
                              1000 * 1000 * 1000);    /* 64 bit pts in ns */
  
 -        if (track->type == MATROSKA_TRACK_TYPE_AUDIO &&
 -            track->audio.out_samplerate) {
 -            st->codecpar->initial_padding = av_rescale_q(track->codec_delay,
 -                                                         (AVRational){ 1, 1000000000 },
 -                                                         (AVRational){ 1, track->audio.out_samplerate });
 -        }
 -
          /* convert the delay from ns to the track timebase */
 -        track->codec_delay = av_rescale_q(track->codec_delay,
 +        track->codec_delay_in_track_tb = av_rescale_q(track->codec_delay,
                                            (AVRational){ 1, 1000000000 },
                                            st->time_base);
  
          st->codecpar->codec_id = codec_id;
 -        st->start_time      = 0;
 +
          if (strcmp(track->language, "und"))
              av_dict_set(&st->metadata, "language", track->language, 0);
          av_dict_set(&st->metadata, "title", track->name, 0);
                  st->codecpar->extradata      = extradata;
                  st->codecpar->extradata_size = extradata_size;
              } else if (track->codec_priv.data && track->codec_priv.size > 0) {
 -                st->codecpar->extradata = av_mallocz(track->codec_priv.size +
 -                                                     AV_INPUT_BUFFER_PADDING_SIZE);
 -                if (!st->codecpar->extradata)
 +                if (ff_alloc_extradata(st->codecpar, track->codec_priv.size))
                      return AVERROR(ENOMEM);
 -                st->codecpar->extradata_size = track->codec_priv.size;
                  memcpy(st->codecpar->extradata,
                         track->codec_priv.data + extradata_offset,
                         track->codec_priv.size);
          }
  
          if (track->type == MATROSKA_TRACK_TYPE_VIDEO) {
 +            MatroskaTrackPlane *planes = track->operation.combine_planes.elem;
              int display_width_mul  = 1;
              int display_height_mul = 1;
  
              st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
 -            st->codecpar->codec_tag  = track->video.fourcc;
 +            st->codecpar->codec_tag  = fourcc;
 +            if (bit_depth >= 0)
 +                st->codecpar->bits_per_coded_sample = bit_depth;
              st->codecpar->width      = track->video.pixel_width;
              st->codecpar->height     = track->video.pixel_height;
  
              if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_INTERLACED)
 -                st->codecpar->field_order = mkv_field_order(track->video.field_order);
 +                st->codecpar->field_order = mkv_field_order(matroska, track->video.field_order);
 +            else if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_PROGRESSIVE)
 +                st->codecpar->field_order = AV_FIELD_PROGRESSIVE;
  
              if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB)
                  mkv_stereo_mode_display_mul(track->video.stereo_mode, &display_width_mul, &display_height_mul);
  
 -            av_reduce(&st->sample_aspect_ratio.num,
 -                      &st->sample_aspect_ratio.den,
 -                      st->codecpar->height * track->video.display_width  * display_width_mul,
 -                      st->codecpar->width  * track->video.display_height * display_height_mul,
 -                      255);
 -            if (st->codecpar->codec_id != AV_CODEC_ID_H264 &&
 -                st->codecpar->codec_id != AV_CODEC_ID_HEVC)
 +            if (track->video.display_unit < MATROSKA_VIDEO_DISPLAYUNIT_UNKNOWN) {
 +                av_reduce(&st->sample_aspect_ratio.num,
 +                          &st->sample_aspect_ratio.den,
 +                          st->codecpar->height * track->video.display_width  * display_width_mul,
 +                          st->codecpar->width  * track->video.display_height * display_height_mul,
 +                          255);
 +            }
 +            if (st->codecpar->codec_id != AV_CODEC_ID_HEVC)
                  st->need_parsing = AVSTREAM_PARSE_HEADERS;
 +
              if (track->default_duration) {
                  av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
                            1000000000, track->default_duration, 30000);
 +#if FF_API_R_FRAME_RATE
 +                if (   st->avg_frame_rate.num < st->avg_frame_rate.den * 1000LL
 +                    && st->avg_frame_rate.num > st->avg_frame_rate.den * 5LL)
 +                    st->r_frame_rate = st->avg_frame_rate;
 +#endif
 +            }
 +
 +            /* export stereo mode flag as metadata tag */
 +            if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB)
 +                av_dict_set(&st->metadata, "stereo_mode", ff_matroska_video_stereo_mode[track->video.stereo_mode], 0);
 +
 +            /* export alpha mode flag as metadata tag  */
 +            if (track->video.alpha_mode)
 +                av_dict_set(&st->metadata, "alpha_mode", "1", 0);
 +
 +            /* if we have virtual track, mark the real tracks */
 +            for (j=0; j < track->operation.combine_planes.nb_elem; j++) {
 +                char buf[32];
 +                if (planes[j].type >= MATROSKA_VIDEO_STEREO_PLANE_COUNT)
 +                    continue;
 +                snprintf(buf, sizeof(buf), "%s_%d",
 +                         ff_matroska_video_stereo_plane[planes[j].type], i);
 +                for (k=0; k < matroska->tracks.nb_elem; k++)
 +                    if (planes[j].uid == tracks[k].uid && tracks[k].stream) {
 +                        av_dict_set(&tracks[k].stream->metadata,
 +                                    "stereo_mode", buf, 0);
 +                        break;
 +                    }
              }
              // add stream level stereo3d side data if it is a supported format
              if (track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB &&
                      return ret;
              }
  
 +            ret = mkv_parse_video_color(st, track);
 +            if (ret < 0)
 +                return ret;
              ret = mkv_parse_video_projection(st, track);
              if (ret < 0)
                  return ret;
          } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {
              st->codecpar->codec_type  = AVMEDIA_TYPE_AUDIO;
 +            st->codecpar->codec_tag   = fourcc;
              st->codecpar->sample_rate = track->audio.out_samplerate;
              st->codecpar->channels    = track->audio.channels;
 -            if (st->codecpar->codec_id != AV_CODEC_ID_AAC)
 -                st->need_parsing = AVSTREAM_PARSE_HEADERS;
 +            if (!st->codecpar->bits_per_coded_sample)
 +                st->codecpar->bits_per_coded_sample = track->audio.bitdepth;
              if (st->codecpar->codec_id == AV_CODEC_ID_MP3)
                  st->need_parsing = AVSTREAM_PARSE_FULL;
 +            else if (st->codecpar->codec_id != AV_CODEC_ID_AAC)
 +                st->need_parsing = AVSTREAM_PARSE_HEADERS;
 +            if (track->codec_delay > 0) {
 +                st->codecpar->initial_padding = av_rescale_q(track->codec_delay,
 +                                                             (AVRational){1, 1000000000},
 +                                                             (AVRational){1, st->codecpar->codec_id == AV_CODEC_ID_OPUS ?
 +                                                                             48000 : st->codecpar->sample_rate});
 +            }
 +            if (track->seek_preroll > 0) {
 +                st->codecpar->seek_preroll = av_rescale_q(track->seek_preroll,
 +                                                          (AVRational){1, 1000000000},
 +                                                          (AVRational){1, st->codecpar->sample_rate});
 +            }
 +        } else if (codec_id == AV_CODEC_ID_WEBVTT) {
 +            st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
 +
 +            if (!strcmp(track->codec_id, "D_WEBVTT/CAPTIONS")) {
 +                st->disposition |= AV_DISPOSITION_CAPTIONS;
 +            } else if (!strcmp(track->codec_id, "D_WEBVTT/DESCRIPTIONS")) {
 +                st->disposition |= AV_DISPOSITION_DESCRIPTIONS;
 +            } else if (!strcmp(track->codec_id, "D_WEBVTT/METADATA")) {
 +                st->disposition |= AV_DISPOSITION_METADATA;
 +            }
          } else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) {
              st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
 -            if (st->codecpar->codec_id == AV_CODEC_ID_SSA)
 +            if (st->codecpar->codec_id == AV_CODEC_ID_ASS)
                  matroska->contains_ssa = 1;
          }
      }
@@@ -2555,7 -2062,6 +2561,7 @@@ static int matroska_read_header(AVForma
      int i, j, res;
  
      matroska->ctx = s;
 +    matroska->cues_parsing_deferred = 1;
  
      /* First read the EBML header. */
      if (ebml_parse(matroska, ebml_syntax, &ebml) || !ebml.doctype) {
                                        ebml.version, ebml.doctype, ebml.doctype_version);
          ebml_free(ebml_syntax, &ebml);
          return AVERROR_PATCHWELCOME;
 +    } else if (ebml.doctype_version == 3) {
 +        av_log(matroska->ctx, AV_LOG_WARNING,
 +               "EBML header using unsupported features\n"
 +               "(EBML version %"PRIu64", doctype %s, doc version %"PRIu64")\n",
 +               ebml.version, ebml.doctype, ebml.doctype_version);
      }
      for (i = 0; i < FF_ARRAY_ELEMS(matroska_doctypes); i++)
          if (!strcmp(ebml.doctype, matroska_doctypes[i]))
      while (res != 1) {
          res = matroska_resync(matroska, pos);
          if (res < 0)
 -            return res;
 +            goto fail;
          pos = avio_tell(matroska->ctx->pb);
          res = ebml_parse(matroska, matroska_segment, matroska);
      }
          matroska->ctx->duration = matroska->duration * matroska->time_scale *
                                    1000 / AV_TIME_BASE;
      av_dict_set(&s->metadata, "title", matroska->title, 0);
 +    av_dict_set(&s->metadata, "encoder", matroska->muxingapp, 0);
 +
 +    if (matroska->date_utc.size == 8)
 +        matroska_metadata_creation_time(&s->metadata, AV_RB64(matroska->date_utc.data));
  
      res = matroska_parse_tracks(s);
      if (res < 0)
 -        return res;
 +        goto fail;
  
      attachments = attachments_list->elem;
      for (j = 0; j < attachments_list->nb_elem; j++) {
                  st->attached_pic.flags       |= AV_PKT_FLAG_KEY;
              } else {
                  st->codecpar->codec_type = AVMEDIA_TYPE_ATTACHMENT;
 -                st->codecpar->extradata  = av_malloc(attachments[j].bin.size);
 -                if (!st->codecpar->extradata)
 +                if (ff_alloc_extradata(st->codecpar, attachments[j].bin.size))
                      break;
 -
 -                st->codecpar->extradata_size = attachments[j].bin.size;
                  memcpy(st->codecpar->extradata, attachments[j].bin.data,
                         attachments[j].bin.size);
  
                                     (AVRational) { 1, 1000000000 },
                                     chapters[i].start, chapters[i].end,
                                     chapters[i].title);
 -            av_dict_set(&chapters[i].chapter->metadata,
 -                        "title", chapters[i].title, 0);
 +            if (chapters[i].chapter) {
 +                av_dict_set(&chapters[i].chapter->metadata,
 +                            "title", chapters[i].title, 0);
 +            }
              max_start = chapters[i].start;
          }
  
 +    matroska_add_index_entries(matroska);
 +
      matroska_convert_tags(s);
  
      return 0;
 +fail:
 +    matroska_read_close(s);
 +    return res;
  }
  
  /*
@@@ -2703,20 -2196,8 +2709,20 @@@ static int matroska_deliver_packet(Matr
                                     AVPacket *pkt)
  {
      if (matroska->num_packets > 0) {
 +        MatroskaTrack *tracks = matroska->tracks.elem;
 +        MatroskaTrack *track;
          memcpy(pkt, matroska->packets[0], sizeof(AVPacket));
 -        av_free(matroska->packets[0]);
 +        av_freep(&matroska->packets[0]);
 +        track = &tracks[pkt->stream_index];
 +        if (track->has_palette) {
 +            uint8_t *pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
 +            if (!pal) {
 +                av_log(matroska->ctx, AV_LOG_ERROR, "Cannot append palette to packet\n");
 +            } else {
 +                memcpy(pal, track->palette, AVPALETTE_SIZE);
 +            }
 +            track->has_palette = 0;
 +        }
          if (matroska->num_packets > 1) {
              void *newpackets;
              memmove(&matroska->packets[0], &matroska->packets[1],
@@@ -2747,7 -2228,7 +2753,7 @@@ static void matroska_clear_queue(Matros
          int n;
          for (n = 0; n < matroska->num_packets; n++) {
              av_packet_unref(matroska->packets[n]);
 -            av_free(matroska->packets[n]);
 +            av_freep(&matroska->packets[n]);
          }
          av_freep(&matroska->packets);
          matroska->num_packets = 0;
@@@ -2772,7 -2253,7 +2778,7 @@@ static int matroska_parse_laces(Matrosk
          return 0;
      }
  
 -    assert(size > 0);
 +    av_assert0(size > 0);
      *laces    = *data + 1;
      data     += 1;
      size     -= 1;
          uint32_t total = 0;
          for (n = 0; res == 0 && n < *laces - 1; n++) {
              while (1) {
 -                if (size == 0) {
 -                    res = AVERROR_EOF;
 +                if (size <= total) {
 +                    res = AVERROR_INVALIDDATA;
                      break;
                  }
                  temp          = *data;
 +                total        += temp;
                  lace_size[n] += temp;
                  data         += 1;
                  size         -= 1;
                  if (temp != 0xff)
                      break;
              }
 -            total += lace_size[n];
          }
          if (size <= total) {
              res = AVERROR_INVALIDDATA;
          uint64_t num;
          uint64_t total;
          n = matroska_ebmlnum_uint(matroska, data, size, &num);
 -        if (n < 0) {
 +        if (n < 0 || num > INT_MAX) {
              av_log(matroska->ctx, AV_LOG_INFO,
                     "EBML block data error\n");
 -            res = n;
 +            res = n<0 ? n : AVERROR_INVALIDDATA;
              break;
          }
          data += n;
              int64_t snum;
              int r;
              r = matroska_ebmlnum_sint(matroska, data, size, &snum);
 -            if (r < 0) {
 +            if (r < 0 || lace_size[n - 1] + snum > (uint64_t)INT_MAX) {
                  av_log(matroska->ctx, AV_LOG_INFO,
                         "EBML block data error\n");
 -                res = r;
 +                res = r<0 ? r : AVERROR_INVALIDDATA;
                  break;
              }
              data        += r;
  static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska,
                                     MatroskaTrack *track, AVStream *st,
                                     uint8_t *data, int size, uint64_t timecode,
 -                                   uint64_t duration, int64_t pos)
 +                                   int64_t pos)
  {
      int a = st->codecpar->block_align;
      int sps = track->audio.sub_packet_size;
              }
              memcpy(track->audio.buf + y * w, data, w);
          } else {
 -            if (size < sps * w / sps) {
 +            if (size < sps * w / sps || h<=0 || w%sps) {
                  av_log(matroska->ctx, AV_LOG_ERROR,
                         "Corrupt generic RM-style audio packet size\n");
                  return AVERROR_INVALIDDATA;
      return ret;
  }
  
 +static int matroska_parse_webvtt(MatroskaDemuxContext *matroska,
 +                                 MatroskaTrack *track,
 +                                 AVStream *st,
 +                                 uint8_t *data, int data_len,
 +                                 uint64_t timecode,
 +                                 uint64_t duration,
 +                                 int64_t pos)
 +{
 +    AVPacket *pkt;
 +    uint8_t *id, *settings, *text, *buf;
 +    int id_len, settings_len, text_len;
 +    uint8_t *p, *q;
 +    int err;
 +
 +    if (data_len <= 0)
 +        return AVERROR_INVALIDDATA;
 +
 +    p = data;
 +    q = data + data_len;
 +
 +    id = p;
 +    id_len = -1;
 +    while (p < q) {
 +        if (*p == '\r' || *p == '\n') {
 +            id_len = p - id;
 +            if (*p == '\r')
 +                p++;
 +            break;
 +        }
 +        p++;
 +    }
 +
 +    if (p >= q || *p != '\n')
 +        return AVERROR_INVALIDDATA;
 +    p++;
 +
 +    settings = p;
 +    settings_len = -1;
 +    while (p < q) {
 +        if (*p == '\r' || *p == '\n') {
 +            settings_len = p - settings;
 +            if (*p == '\r')
 +                p++;
 +            break;
 +        }
 +        p++;
 +    }
 +
 +    if (p >= q || *p != '\n')
 +        return AVERROR_INVALIDDATA;
 +    p++;
 +
 +    text = p;
 +    text_len = q - p;
 +    while (text_len > 0) {
 +        const int len = text_len - 1;
 +        const uint8_t c = p[len];
 +        if (c != '\r' && c != '\n')
 +            break;
 +        text_len = len;
 +    }
 +
 +    if (text_len <= 0)
 +        return AVERROR_INVALIDDATA;
 +
 +    pkt = av_mallocz(sizeof(*pkt));
 +    if (!pkt)
 +        return AVERROR(ENOMEM);
 +    err = av_new_packet(pkt, text_len);
 +    if (err < 0) {
 +        av_free(pkt);
 +        return AVERROR(err);
 +    }
 +
 +    memcpy(pkt->data, text, text_len);
 +
 +    if (id_len > 0) {
 +        buf = av_packet_new_side_data(pkt,
 +                                      AV_PKT_DATA_WEBVTT_IDENTIFIER,
 +                                      id_len);
 +        if (!buf) {
 +            av_free(pkt);
 +            return AVERROR(ENOMEM);
 +        }
 +        memcpy(buf, id, id_len);
 +    }
 +
 +    if (settings_len > 0) {
 +        buf = av_packet_new_side_data(pkt,
 +                                      AV_PKT_DATA_WEBVTT_SETTINGS,
 +                                      settings_len);
 +        if (!buf) {
 +            av_free(pkt);
 +            return AVERROR(ENOMEM);
 +        }
 +        memcpy(buf, settings, settings_len);
 +    }
 +
 +    // Do we need this for subtitles?
 +    // pkt->flags = AV_PKT_FLAG_KEY;
 +
 +    pkt->stream_index = st->index;
 +    pkt->pts = timecode;
 +
 +    // Do we need this for subtitles?
 +    // pkt->dts = timecode;
 +
 +    pkt->duration = duration;
 +    pkt->pos = pos;
 +
 +    dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
 +    matroska->prev_pkt = pkt;
 +
 +    return 0;
 +}
 +
  static int matroska_parse_frame(MatroskaDemuxContext *matroska,
                                  MatroskaTrack *track, AVStream *st,
                                  uint8_t *data, int pkt_size,
 -                                uint64_t timecode, uint64_t duration,
 -                                int64_t pos, int is_keyframe)
 +                                uint64_t timecode, uint64_t lace_duration,
 +                                int64_t pos, int is_keyframe,
 +                                uint8_t *additional, uint64_t additional_id, int additional_size,
 +                                int64_t discard_padding)
  {
      MatroskaTrackEncoding *encodings = track->encodings.elem;
      uint8_t *pkt_data = data;
      int offset = 0, res;
      AVPacket *pkt;
  
 -    if (encodings && encodings->scope & 1) {
 +    if (encodings && !encodings->type && encodings->scope & 1) {
          res = matroska_decode_buffer(&pkt_data, &pkt_size, track);
          if (res < 0)
              return res;
          pkt_data = wv_data;
      }
  
 -    if (st->codecpar->codec_id == AV_CODEC_ID_PRORES)
 +    if (st->codecpar->codec_id == AV_CODEC_ID_PRORES &&
 +        AV_RB32(&data[4]) != MKBETAG('i', 'c', 'p', 'f'))
          offset = 8;
  
      pkt = av_mallocz(sizeof(AVPacket));
      if (!pkt) {
 -        av_freep(&pkt_data);
 +        if (pkt_data != data)
 +            av_freep(&pkt_data);
          return AVERROR(ENOMEM);
      }
      /* XXX: prevent data copy... */
      if (av_new_packet(pkt, pkt_size + offset) < 0) {
          av_free(pkt);
 -        av_freep(&pkt_data);
 -        return AVERROR(ENOMEM);
 +        res = AVERROR(ENOMEM);
 +        goto fail;
      }
  
 -    if (st->codecpar->codec_id == AV_CODEC_ID_PRORES) {
 +    if (st->codecpar->codec_id == AV_CODEC_ID_PRORES && offset == 8) {
          uint8_t *buf = pkt->data;
          bytestream_put_be32(&buf, pkt_size);
          bytestream_put_be32(&buf, MKBETAG('i', 'c', 'p', 'f'));
      memcpy(pkt->data + offset, pkt_data, pkt_size);
  
      if (pkt_data != data)
 -        av_free(pkt_data);
 +        av_freep(&pkt_data);
  
      pkt->flags        = is_keyframe;
      pkt->stream_index = st->index;
  
 +    if (additional_size > 0) {
 +        uint8_t *side_data = av_packet_new_side_data(pkt,
 +                                                     AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
 +                                                     additional_size + 8);
 +        if (!side_data) {
 +            av_packet_unref(pkt);
 +            av_free(pkt);
 +            return AVERROR(ENOMEM);
 +        }
 +        AV_WB64(side_data, additional_id);
 +        memcpy(side_data + 8, additional, additional_size);
 +    }
 +
 +    if (discard_padding) {
 +        uint8_t *side_data = av_packet_new_side_data(pkt,
 +                                                     AV_PKT_DATA_SKIP_SAMPLES,
 +                                                     10);
 +        if (!side_data) {
 +            av_packet_unref(pkt);
 +            av_free(pkt);
 +            return AVERROR(ENOMEM);
 +        }
 +        discard_padding = av_rescale_q(discard_padding,
 +                                            (AVRational){1, 1000000000},
 +                                            (AVRational){1, st->codecpar->sample_rate});
 +        if (discard_padding > 0) {
 +            AV_WL32(side_data + 4, discard_padding);
 +        } else {
 +            AV_WL32(side_data, -discard_padding);
 +        }
 +    }
 +
      if (track->ms_compat)
          pkt->dts = timecode;
      else
          pkt->pts = timecode;
      pkt->pos = pos;
 -    if (track->type != MATROSKA_TRACK_TYPE_SUBTITLE || st->codecpar->codec_id == AV_CODEC_ID_SRT)
 -        pkt->duration = duration;
 +    pkt->duration = lace_duration;
 +
  #if FF_API_CONVERGENCE_DURATION
  FF_DISABLE_DEPRECATION_WARNINGS
 -    if (st->codecpar->codec_id == AV_CODEC_ID_SRT)
 -        pkt->convergence_duration = duration;
 +    if (st->codecpar->codec_id == AV_CODEC_ID_SUBRIP) {
 +        pkt->convergence_duration = lace_duration;
 +    }
  FF_ENABLE_DEPRECATION_WARNINGS
  #endif
  
 -    if (st->codecpar->codec_id == AV_CODEC_ID_SSA)
 -        matroska_fix_ass_packet(matroska, pkt, duration);
 -
 -    if (matroska->prev_pkt                                 &&
 -        timecode                         != AV_NOPTS_VALUE &&
 -        matroska->prev_pkt->pts          == timecode       &&
 -        matroska->prev_pkt->stream_index == st->index      &&
 -        st->codecpar->codec_id == AV_CODEC_ID_SSA)
 -        matroska_merge_packets(matroska->prev_pkt, pkt);
 -    else {
 -        dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
 -        matroska->prev_pkt = pkt;
 -    }
 +    dynarray_add(&matroska->packets, &matroska->num_packets, pkt);
 +    matroska->prev_pkt = pkt;
  
      return 0;
  
@@@ -3260,8 -2599,7 +3266,8 @@@ fail
  static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
                                  int size, int64_t pos, uint64_t cluster_time,
                                  uint64_t block_duration, int is_keyframe,
 -                                int64_t cluster_pos)
 +                                uint8_t *additional, uint64_t additional_id, int additional_size,
 +                                int64_t cluster_pos, int64_t discard_padding)
  {
      uint64_t timecode = AV_NOPTS_VALUE;
      MatroskaTrack *track;
      int16_t block_time;
      uint32_t *lace_size = NULL;
      int n, flags, laces = 0;
 -    uint64_t num, duration;
 +    uint64_t num;
 +    int trust_default_duration = 1;
  
      if ((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0) {
          av_log(matroska->ctx, AV_LOG_ERROR, "EBML block data error\n");
      st = track->stream;
      if (st->discard >= AVDISCARD_ALL)
          return res;
 +    av_assert1(block_duration != AV_NOPTS_VALUE);
  
 -    block_time = AV_RB16(data);
 +    block_time = sign_extend(AV_RB16(data), 16);
      data      += 2;
      flags      = *data++;
      size      -= 3;
  
      if (cluster_time != (uint64_t) -1 &&
          (block_time >= 0 || cluster_time >= -block_time)) {
 -        timecode = cluster_time + block_time - track->codec_delay;
 +        timecode = cluster_time + block_time - track->codec_delay_in_track_tb;
          if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE &&
              timecode < track->end_timecode)
              is_keyframe = 0;  /* overlapping subtitles are not key frame */
 -        if (is_keyframe)
 +        if (is_keyframe) {
 +            ff_reduce_index(matroska->ctx, st->index);
              av_add_index_entry(st, cluster_pos, timecode, 0, 0,
                                 AVINDEX_KEYFRAME);
 +        }
      }
  
      if (matroska->skip_to_keyframe &&
          track->type != MATROSKA_TRACK_TYPE_SUBTITLE) {
 -        if (!is_keyframe || timecode < matroska->skip_to_timecode)
 +        // Compare signed timecodes. Timecode may be negative due to codec delay
 +        // offset. We don't support timestamps greater than int64_t anyway - see
 +        // AVPacket's pts.
 +        if ((int64_t)timecode < (int64_t)matroska->skip_to_timecode)
              return res;
 -        matroska->skip_to_keyframe = 0;
 +        if (is_keyframe)
 +            matroska->skip_to_keyframe = 0;
 +        else if (!st->skip_to_keyframe) {
 +            av_log(matroska->ctx, AV_LOG_ERROR, "File is broken, keyframes not correctly marked!\n");
 +            matroska->skip_to_keyframe = 0;
 +        }
      }
  
      res = matroska_parse_laces(matroska, &data, &size, (flags & 0x06) >> 1,
      if (res)
          goto end;
  
 -    if (block_duration != AV_NOPTS_VALUE) {
 -        duration = block_duration / laces;
 -        if (block_duration != duration * laces) {
 -            av_log(matroska->ctx, AV_LOG_WARNING,
 -                   "Incorrect block_duration, possibly corrupted container");
 +    if (track->audio.samplerate == 8000) {
 +        // If this is needed for more codecs, then add them here
 +        if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
 +            if (track->audio.samplerate != st->codecpar->sample_rate || !st->codecpar->frame_size)
 +                trust_default_duration = 0;
          }
 -    } else {
 -        duration       = track->default_duration / matroska->time_scale;
 -        block_duration = duration * laces;
      }
  
 -    if (timecode != AV_NOPTS_VALUE)
 +    if (!block_duration && trust_default_duration)
 +        block_duration = track->default_duration * laces / matroska->time_scale;
 +
 +    if (cluster_time != (uint64_t)-1 && (block_time >= 0 || cluster_time >= -block_time))
          track->end_timecode =
              FFMAX(track->end_timecode, timecode + block_duration);
  
      for (n = 0; n < laces; n++) {
 +        int64_t lace_duration = block_duration*(n+1) / laces - block_duration*n / laces;
 +
 +        if (lace_size[n] > size) {
 +            av_log(matroska->ctx, AV_LOG_ERROR, "Invalid packet size\n");
 +            break;
 +        }
 +
          if ((st->codecpar->codec_id == AV_CODEC_ID_RA_288 ||
               st->codecpar->codec_id == AV_CODEC_ID_COOK   ||
               st->codecpar->codec_id == AV_CODEC_ID_SIPR   ||
              st->codecpar->block_align && track->audio.sub_packet_size) {
              res = matroska_parse_rm_audio(matroska, track, st, data,
                                            lace_size[n],
 -                                          timecode, duration, pos);
 +                                          timecode, pos);
 +            if (res)
 +                goto end;
 +
 +        } else if (st->codecpar->codec_id == AV_CODEC_ID_WEBVTT) {
 +            res = matroska_parse_webvtt(matroska, track, st,
 +                                        data, lace_size[n],
 +                                        timecode, lace_duration,
 +                                        pos);
              if (res)
                  goto end;
          } else {
              res = matroska_parse_frame(matroska, track, st, data, lace_size[n],
 -                                       timecode, duration, pos,
 -                                       !n ? is_keyframe : 0);
 +                                       timecode, lace_duration, pos,
 +                                       !n ? is_keyframe : 0,
 +                                       additional, additional_id, additional_size,
 +                                       discard_padding);
              if (res)
                  goto end;
          }
  
          if (timecode != AV_NOPTS_VALUE)
 -            timecode = duration ? timecode + duration : AV_NOPTS_VALUE;
 +            timecode = lace_duration ? timecode + lace_duration : AV_NOPTS_VALUE;
          data += lace_size[n];
 +        size -= lace_size[n];
      }
  
  end:
@@@ -3434,22 -2742,19 +3440,22 @@@ static int matroska_parse_cluster_incre
          matroska->current_cluster_num_blocks = blocks_list->nb_elem;
          i                                    = blocks_list->nb_elem - 1;
          if (blocks[i].bin.size > 0 && blocks[i].bin.data) {
 -            int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1;
 +            int is_keyframe = blocks[i].non_simple ? blocks[i].reference == INT64_MIN : -1;
 +            uint8_t* additional = blocks[i].additional.size > 0 ?
 +                                    blocks[i].additional.data : NULL;
              if (!blocks[i].non_simple)
 -                blocks[i].duration = AV_NOPTS_VALUE;
 +                blocks[i].duration = 0;
              res = matroska_parse_block(matroska, blocks[i].bin.data,
                                         blocks[i].bin.size, blocks[i].bin.pos,
                                         matroska->current_cluster.timecode,
                                         blocks[i].duration, is_keyframe,
 -                                       matroska->current_cluster_pos);
 +                                       additional, blocks[i].additional_id,
 +                                       blocks[i].additional.size,
 +                                       matroska->current_cluster_pos,
 +                                       blocks[i].discard_padding);
          }
      }
  
 -    if (res < 0)
 -        matroska->done = 1;
      return res;
  }
  
@@@ -3470,14 -2775,15 +3476,14 @@@ static int matroska_parse_cluster(Matro
      res         = ebml_parse(matroska, matroska_clusters, &cluster);
      blocks_list = &cluster.blocks;
      blocks      = blocks_list->elem;
 -    for (i = 0; i < blocks_list->nb_elem && !res; i++)
 +    for (i = 0; i < blocks_list->nb_elem; i++)
          if (blocks[i].bin.size > 0 && blocks[i].bin.data) {
 -            int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1;
 -            if (!blocks[i].non_simple)
 -                blocks[i].duration = AV_NOPTS_VALUE;
 +            int is_keyframe = blocks[i].non_simple ? blocks[i].reference == INT64_MIN : -1;
              res = matroska_parse_block(matroska, blocks[i].bin.data,
                                         blocks[i].bin.size, blocks[i].bin.pos,
                                         cluster.timecode, blocks[i].duration,
 -                                       is_keyframe, pos);
 +                                       is_keyframe, NULL, 0, 0, pos,
 +                                       blocks[i].discard_padding);
          }
      ebml_free(matroska_cluster, &cluster);
      return res;
@@@ -3488,14 -2794,19 +3494,14 @@@ static int matroska_read_packet(AVForma
      MatroskaDemuxContext *matroska = s->priv_data;
      int ret = 0;
  
 -    while (!ret && matroska_deliver_packet(matroska, pkt)) {
 +    while (matroska_deliver_packet(matroska, pkt)) {
          int64_t pos = avio_tell(matroska->ctx->pb);
          if (matroska->done)
 -            return AVERROR_EOF;
 +            return (ret < 0) ? ret : AVERROR_EOF;
          if (matroska_parse_cluster(matroska) < 0)
              ret = matroska_resync(matroska, pos);
      }
  
 -    if (ret == AVERROR_INVALIDDATA && pkt->data) {
 -        pkt->flags |= AV_PKT_FLAG_CORRUPT;
 -        return 0;
 -    }
 -
      return ret;
  }
  
@@@ -3505,23 -2816,23 +3511,23 @@@ static int matroska_read_seek(AVFormatC
      MatroskaDemuxContext *matroska = s->priv_data;
      MatroskaTrack *tracks = NULL;
      AVStream *st = s->streams[stream_index];
 -    int i, index, index_sub, index_min;
 +    int i, index, index_min;
  
      /* Parse the CUES now since we need the index data to seek. */
 -    if (matroska->cues_parsing_deferred) {
 -        matroska_parse_cues(matroska);
 +    if (matroska->cues_parsing_deferred > 0) {
          matroska->cues_parsing_deferred = 0;
 +        matroska_parse_cues(matroska);
      }
  
      if (!st->nb_index_entries)
 -        return 0;
 +        goto err;
      timestamp = FFMAX(timestamp, st->index_entries[0].timestamp);
  
 -    if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) {
 +    if ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->nb_index_entries - 1) {
          avio_seek(s->pb, st->index_entries[st->nb_index_entries - 1].pos,
                    SEEK_SET);
          matroska->current_id = 0;
 -        while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0) {
 +        while ((index = av_index_search_timestamp(st, timestamp, flags)) < 0 || index == st->nb_index_entries - 1) {
              matroska_clear_queue(matroska);
              if (matroska_parse_cluster(matroska) < 0)
                  break;
      }
  
      matroska_clear_queue(matroska);
 -    if (index < 0)
 -        return 0;
 +    if (index < 0 || (matroska->cues_parsing_deferred < 0 && index == st->nb_index_entries - 1))
 +        goto err;
  
      index_min = index;
      tracks = matroska->tracks.elem;
          tracks[i].audio.sub_packet_cnt = 0;
          tracks[i].audio.buf_timecode   = AV_NOPTS_VALUE;
          tracks[i].end_timecode         = 0;
 -        if (tracks[i].type == MATROSKA_TRACK_TYPE_SUBTITLE &&
 -            tracks[i].stream->discard != AVDISCARD_ALL) {
 -            index_sub = av_index_search_timestamp(
 -                tracks[i].stream, st->index_entries[index].timestamp,
 -                AVSEEK_FLAG_BACKWARD);
 -            if (index_sub >= 0 &&
 -                st->index_entries[index_sub].pos < st->index_entries[index_min].pos &&
 -                st->index_entries[index].timestamp -
 -                st->index_entries[index_sub].timestamp < 30000000000 / matroska->time_scale)
 -                index_min = index_sub;
 -        }
      }
  
      avio_seek(s->pb, st->index_entries[index_min].pos, SEEK_SET);
      matroska->current_id       = 0;
 -    matroska->skip_to_keyframe = !(flags & AVSEEK_FLAG_ANY);
 -    matroska->skip_to_timecode = st->index_entries[index].timestamp;
 +    if (flags & AVSEEK_FLAG_ANY) {
 +        st->skip_to_keyframe = 0;
 +        matroska->skip_to_timecode = timestamp;
 +    } else {
 +        st->skip_to_keyframe = 1;
 +        matroska->skip_to_timecode = st->index_entries[index].timestamp;
 +    }
 +    matroska->skip_to_keyframe = 1;
      matroska->done             = 0;
 +    matroska->num_levels       = 0;
      ff_update_cur_dts(s, st, st->index_entries[index].timestamp);
      return 0;
 +err:
 +    // slightly hackish but allows proper fallback to
 +    // the generic seeking code.
 +    matroska_clear_queue(matroska);
 +    matroska->current_id = 0;
 +    st->skip_to_keyframe =
 +    matroska->skip_to_keyframe = 0;
 +    matroska->done = 0;
 +    matroska->num_levels = 0;
 +    return -1;
  }
  
  static int matroska_read_close(AVFormatContext *s)
  
      for (n = 0; n < matroska->tracks.nb_elem; n++)
          if (tracks[n].type == MATROSKA_TRACK_TYPE_AUDIO)
 -            av_free(tracks[n].audio.buf);
 +            av_freep(&tracks[n].audio.buf);
      ebml_free(matroska_cluster, &matroska->current_cluster);
      ebml_free(matroska_segment, matroska);
  
      return 0;
  }
  
 +typedef struct {
 +    int64_t start_time_ns;
 +    int64_t end_time_ns;
 +    int64_t start_offset;
 +    int64_t end_offset;
 +} CueDesc;
 +
 +/* This function searches all the Cues and returns the CueDesc corresponding to
 + * the timestamp ts. Returned CueDesc will be such that start_time_ns <= ts <
 + * end_time_ns. All 4 fields will be set to -1 if ts >= file's duration.
 + */
 +static CueDesc get_cue_desc(AVFormatContext *s, int64_t ts, int64_t cues_start) {
 +    MatroskaDemuxContext *matroska = s->priv_data;
 +    CueDesc cue_desc;
 +    int i;
 +    int nb_index_entries = s->streams[0]->nb_index_entries;
 +    AVIndexEntry *index_entries = s->streams[0]->index_entries;
 +    if (ts >= matroska->duration * matroska->time_scale) return (CueDesc) {-1, -1, -1, -1};
 +    for (i = 1; i < nb_index_entries; i++) {
 +        if (index_entries[i - 1].timestamp * matroska->time_scale <= ts &&
 +            index_entries[i].timestamp * matroska->time_scale > ts) {
 +            break;
 +        }
 +    }
 +    --i;
 +    cue_desc.start_time_ns = index_entries[i].timestamp * matroska->time_scale;
 +    cue_desc.start_offset = index_entries[i].pos - matroska->segment_start;
 +    if (i != nb_index_entries - 1) {
 +        cue_desc.end_time_ns = index_entries[i + 1].timestamp * matroska->time_scale;
 +        cue_desc.end_offset = index_entries[i + 1].pos - matroska->segment_start;
 +    } else {
 +        cue_desc.end_time_ns = matroska->duration * matroska->time_scale;
 +        // FIXME: this needs special handling for files where Cues appear
 +        // before Clusters. the current logic assumes Cues appear after
 +        // Clusters.
 +        cue_desc.end_offset = cues_start - matroska->segment_start;
 +    }
 +    return cue_desc;
 +}
 +
 +static int webm_clusters_start_with_keyframe(AVFormatContext *s)
 +{
 +    MatroskaDemuxContext *matroska = s->priv_data;
 +    int64_t cluster_pos, before_pos;
 +    int index, rv = 1;
 +    if (s->streams[0]->nb_index_entries <= 0) return 0;
 +    // seek to the first cluster using cues.
 +    index = av_index_search_timestamp(s->streams[0], 0, 0);
 +    if (index < 0)  return 0;
 +    cluster_pos = s->streams[0]->index_entries[index].pos;
 +    before_pos = avio_tell(s->pb);
 +    while (1) {
 +        int64_t cluster_id = 0, cluster_length = 0;
 +        AVPacket *pkt;
 +        avio_seek(s->pb, cluster_pos, SEEK_SET);
 +        // read cluster id and length
 +        ebml_read_num(matroska, matroska->ctx->pb, 4, &cluster_id);
 +        ebml_read_length(matroska, matroska->ctx->pb, &cluster_length);
 +        if (cluster_id != 0xF43B675) { // done with all clusters
 +            break;
 +        }
 +        avio_seek(s->pb, cluster_pos, SEEK_SET);
 +        matroska->current_id = 0;
 +        matroska_clear_queue(matroska);
 +        if (matroska_parse_cluster(matroska) < 0 ||
 +            matroska->num_packets <= 0) {
 +            break;
 +        }
 +        pkt = matroska->packets[0];
 +        cluster_pos += cluster_length + 12; // 12 is the offset of the cluster id and length.
 +        if (!(pkt->flags & AV_PKT_FLAG_KEY)) {
 +            rv = 0;
 +            break;
 +        }
 +    }
 +    avio_seek(s->pb, before_pos, SEEK_SET);
 +    return rv;
 +}
 +
 +static int buffer_size_after_time_downloaded(int64_t time_ns, double search_sec, int64_t bps,
 +                                             double min_buffer, double* buffer,
 +                                             double* sec_to_download, AVFormatContext *s,
 +                                             int64_t cues_start)
 +{
 +    double nano_seconds_per_second = 1000000000.0;
 +    double time_sec = time_ns / nano_seconds_per_second;
 +    int rv = 0;
 +    int64_t time_to_search_ns = (int64_t)(search_sec * nano_seconds_per_second);
 +    int64_t end_time_ns = time_ns + time_to_search_ns;
 +    double sec_downloaded = 0.0;
 +    CueDesc desc_curr = get_cue_desc(s, time_ns, cues_start);
 +    if (desc_curr.start_time_ns == -1)
 +      return -1;
 +    *sec_to_download = 0.0;
 +
 +    // Check for non cue start time.
 +    if (time_ns > desc_curr.start_time_ns) {
 +      int64_t cue_nano = desc_curr.end_time_ns - time_ns;
 +      double percent = (double)(cue_nano) / (desc_curr.end_time_ns - desc_curr.start_time_ns);
 +      double cueBytes = (desc_curr.end_offset - desc_curr.start_offset) * percent;
 +      double timeToDownload = (cueBytes * 8.0) / bps;
 +
 +      sec_downloaded += (cue_nano / nano_seconds_per_second) - timeToDownload;
 +      *sec_to_download += timeToDownload;
 +
 +      // Check if the search ends within the first cue.
 +      if (desc_curr.end_time_ns >= end_time_ns) {
 +          double desc_end_time_sec = desc_curr.end_time_ns / nano_seconds_per_second;
 +          double percent_to_sub = search_sec / (desc_end_time_sec - time_sec);
 +          sec_downloaded = percent_to_sub * sec_downloaded;
 +          *sec_to_download = percent_to_sub * *sec_to_download;
 +      }
 +
 +      if ((sec_downloaded + *buffer) <= min_buffer) {
 +          return 1;
 +      }
 +
 +      // Get the next Cue.
 +      desc_curr = get_cue_desc(s, desc_curr.end_time_ns, cues_start);
 +    }
 +
 +    while (desc_curr.start_time_ns != -1) {
 +        int64_t desc_bytes = desc_curr.end_offset - desc_curr.start_offset;
 +        int64_t desc_ns = desc_curr.end_time_ns - desc_curr.start_time_ns;
 +        double desc_sec = desc_ns / nano_seconds_per_second;
 +        double bits = (desc_bytes * 8.0);
 +        double time_to_download = bits / bps;
 +
 +        sec_downloaded += desc_sec - time_to_download;
 +        *sec_to_download += time_to_download;
 +
 +        if (desc_curr.end_time_ns >= end_time_ns) {
 +            double desc_end_time_sec = desc_curr.end_time_ns / nano_seconds_per_second;
 +            double percent_to_sub = search_sec / (desc_end_time_sec - time_sec);
 +            sec_downloaded = percent_to_sub * sec_downloaded;
 +            *sec_to_download = percent_to_sub * *sec_to_download;
 +
 +            if ((sec_downloaded + *buffer) <= min_buffer)
 +                rv = 1;
 +            break;
 +        }
 +
 +        if ((sec_downloaded + *buffer) <= min_buffer) {
 +            rv = 1;
 +            break;
 +        }
 +
 +        desc_curr = get_cue_desc(s, desc_curr.end_time_ns, cues_start);
 +    }
 +    *buffer = *buffer + sec_downloaded;
 +    return rv;
 +}
 +
 +/* This function computes the bandwidth of the WebM file with the help of
 + * buffer_size_after_time_downloaded() function. Both of these functions are
 + * adapted from WebM Tools project and are adapted to work with FFmpeg's
 + * Matroska parsing mechanism.
 + *
 + * Returns the bandwidth of the file on success; -1 on error.
 + * */
 +static int64_t webm_dash_manifest_compute_bandwidth(AVFormatContext *s, int64_t cues_start)
 +{
 +    MatroskaDemuxContext *matroska = s->priv_data;
 +    AVStream *st = s->streams[0];
 +    double bandwidth = 0.0;
 +    int i;
 +
 +    for (i = 0; i < st->nb_index_entries; i++) {
 +        int64_t prebuffer_ns = 1000000000;
 +        int64_t time_ns = st->index_entries[i].timestamp * matroska->time_scale;
 +        double nano_seconds_per_second = 1000000000.0;
 +        int64_t prebuffered_ns = time_ns + prebuffer_ns;
 +        double prebuffer_bytes = 0.0;
 +        int64_t temp_prebuffer_ns = prebuffer_ns;
 +        int64_t pre_bytes, pre_ns;
 +        double pre_sec, prebuffer, bits_per_second;
 +        CueDesc desc_beg = get_cue_desc(s, time_ns, cues_start);
 +
 +        // Start with the first Cue.
 +        CueDesc desc_end = desc_beg;
 +
 +        // Figure out how much data we have downloaded for the prebuffer. This will
 +        // be used later to adjust the bits per sample to try.
 +        while (desc_end.start_time_ns != -1 && desc_end.end_time_ns < prebuffered_ns) {
 +            // Prebuffered the entire Cue.
 +            prebuffer_bytes += desc_end.end_offset - desc_end.start_offset;
 +            temp_prebuffer_ns -= desc_end.end_time_ns - desc_end.start_time_ns;
 +            desc_end = get_cue_desc(s, desc_end.end_time_ns, cues_start);
 +        }
 +        if (desc_end.start_time_ns == -1) {
 +            // The prebuffer is larger than the duration.
 +            if (matroska->duration * matroska->time_scale >= prebuffered_ns)
 +              return -1;
 +            bits_per_second = 0.0;
 +        } else {
 +            // The prebuffer ends in the last Cue. Estimate how much data was
 +            // prebuffered.
 +            pre_bytes = desc_end.end_offset - desc_end.start_offset;
 +            pre_ns = desc_end.end_time_ns - desc_end.start_time_ns;
 +            pre_sec = pre_ns / nano_seconds_per_second;
 +            prebuffer_bytes +=
 +                pre_bytes * ((temp_prebuffer_ns / nano_seconds_per_second) / pre_sec);
 +
 +            prebuffer = prebuffer_ns / nano_seconds_per_second;
 +
 +            // Set this to 0.0 in case our prebuffer buffers the entire video.
 +            bits_per_second = 0.0;
 +            do {
 +                int64_t desc_bytes = desc_end.end_offset - desc_beg.start_offset;
 +                int64_t desc_ns = desc_end.end_time_ns - desc_beg.start_time_ns;
 +                double desc_sec = desc_ns / nano_seconds_per_second;
 +                double calc_bits_per_second = (desc_bytes * 8) / desc_sec;
 +
 +                // Drop the bps by the percentage of bytes buffered.
 +                double percent = (desc_bytes - prebuffer_bytes) / desc_bytes;
 +                double mod_bits_per_second = calc_bits_per_second * percent;
 +
 +                if (prebuffer < desc_sec) {
 +                    double search_sec =
 +                        (double)(matroska->duration * matroska->time_scale) / nano_seconds_per_second;
 +
 +                    // Add 1 so the bits per second should be a little bit greater than file
 +                    // datarate.
 +                    int64_t bps = (int64_t)(mod_bits_per_second) + 1;
 +                    const double min_buffer = 0.0;
 +                    double buffer = prebuffer;
 +                    double sec_to_download = 0.0;
 +
 +                    int rv = buffer_size_after_time_downloaded(prebuffered_ns, search_sec, bps,
 +                                                               min_buffer, &buffer, &sec_to_download,
 +                                                               s, cues_start);
 +                    if (rv < 0) {
 +                        return -1;
 +                    } else if (rv == 0) {
 +                        bits_per_second = (double)(bps);
 +                        break;
 +                    }
 +                }
 +
 +                desc_end = get_cue_desc(s, desc_end.end_time_ns, cues_start);
 +            } while (desc_end.start_time_ns != -1);
 +        }
 +        if (bandwidth < bits_per_second) bandwidth = bits_per_second;
 +    }
 +    return (int64_t)bandwidth;
 +}
 +
 +static int webm_dash_manifest_cues(AVFormatContext *s, int64_t init_range)
 +{
 +    MatroskaDemuxContext *matroska = s->priv_data;
 +    EbmlList *seekhead_list = &matroska->seekhead;
 +    MatroskaSeekhead *seekhead = seekhead_list->elem;
 +    char *buf;
 +    int64_t cues_start = -1, cues_end = -1, before_pos, bandwidth;
 +    int i;
 +    int end = 0;
 +
 +    // determine cues start and end positions
 +    for (i = 0; i < seekhead_list->nb_elem; i++)
 +        if (seekhead[i].id == MATROSKA_ID_CUES)
 +            break;
 +
 +    if (i >= seekhead_list->nb_elem) return -1;
 +
 +    before_pos = avio_tell(matroska->ctx->pb);
 +    cues_start = seekhead[i].pos + matroska->segment_start;
 +    if (avio_seek(matroska->ctx->pb, cues_start, SEEK_SET) == cues_start) {
 +        // cues_end is computed as cues_start + cues_length + length of the
 +        // Cues element ID + EBML length of the Cues element. cues_end is
 +        // inclusive and the above sum is reduced by 1.
 +        uint64_t cues_length = 0, cues_id = 0, bytes_read = 0;
 +        bytes_read += ebml_read_num(matroska, matroska->ctx->pb, 4, &cues_id);
 +        bytes_read += ebml_read_length(matroska, matroska->ctx->pb, &cues_length);
 +        cues_end = cues_start + cues_length + bytes_read - 1;
 +    }
 +    avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
 +    if (cues_start == -1 || cues_end == -1) return -1;
 +
 +    // parse the cues
 +    matroska_parse_cues(matroska);
 +
 +    // cues start
 +    av_dict_set_int(&s->streams[0]->metadata, CUES_START, cues_start, 0);
 +
 +    // cues end
 +    av_dict_set_int(&s->streams[0]->metadata, CUES_END, cues_end, 0);
 +
 +    // if the file has cues at the start, fix up the init range so tht
 +    // it does not include it
 +    if (cues_start <= init_range)
 +        av_dict_set_int(&s->streams[0]->metadata, INITIALIZATION_RANGE, cues_start - 1, 0);
 +
 +    // bandwidth
 +    bandwidth = webm_dash_manifest_compute_bandwidth(s, cues_start);
 +    if (bandwidth < 0) return -1;
 +    av_dict_set_int(&s->streams[0]->metadata, BANDWIDTH, bandwidth, 0);
 +
 +    // check if all clusters start with key frames
 +    av_dict_set_int(&s->streams[0]->metadata, CLUSTER_KEYFRAME, webm_clusters_start_with_keyframe(s), 0);
 +
 +    // store cue point timestamps as a comma separated list for checking subsegment alignment in
 +    // the muxer. assumes that each timestamp cannot be more than 20 characters long.
 +    buf = av_malloc_array(s->streams[0]->nb_index_entries, 20 * sizeof(char));
 +    if (!buf) return -1;
 +    strcpy(buf, "");
 +    for (i = 0; i < s->streams[0]->nb_index_entries; i++) {
 +        int ret = snprintf(buf + end, 20 * sizeof(char),
 +                           "%" PRId64, s->streams[0]->index_entries[i].timestamp);
 +        if (ret <= 0 || (ret == 20 && i ==  s->streams[0]->nb_index_entries - 1)) {
 +            av_log(s, AV_LOG_ERROR, "timestamp too long.\n");
 +            av_free(buf);
 +            return AVERROR_INVALIDDATA;
 +        }
 +        end += ret;
 +        if (i != s->streams[0]->nb_index_entries - 1) {
 +            strncat(buf, ",", sizeof(char));
 +            end++;
 +        }
 +    }
 +    av_dict_set(&s->streams[0]->metadata, CUE_TIMESTAMPS, buf, 0);
 +    av_free(buf);
 +
 +    return 0;
 +}
 +
 +static int webm_dash_manifest_read_header(AVFormatContext *s)
 +{
 +    char *buf;
 +    int ret = matroska_read_header(s);
 +    int64_t init_range;
 +    MatroskaTrack *tracks;
 +    MatroskaDemuxContext *matroska = s->priv_data;
 +    if (ret) {
 +        av_log(s, AV_LOG_ERROR, "Failed to read file headers\n");
 +        return -1;
 +    }
 +    if (!s->nb_streams) {
 +        matroska_read_close(s);
 +        av_log(s, AV_LOG_ERROR, "No streams found\n");
 +        return AVERROR_INVALIDDATA;
 +    }
 +
 +    if (!matroska->is_live) {
 +        buf = av_asprintf("%g", matroska->duration);
 +        if (!buf) return AVERROR(ENOMEM);
 +        av_dict_set(&s->streams[0]->metadata, DURATION, buf, 0);
 +        av_free(buf);
 +
 +        // initialization range
 +        // 5 is the offset of Cluster ID.
 +        init_range = avio_tell(s->pb) - 5;
 +        av_dict_set_int(&s->streams[0]->metadata, INITIALIZATION_RANGE, init_range, 0);
 +    }
 +
 +    // basename of the file
 +    buf = strrchr(s->filename, '/');
 +    av_dict_set(&s->streams[0]->metadata, FILENAME, buf ? ++buf : s->filename, 0);
 +
 +    // track number
 +    tracks = matroska->tracks.elem;
 +    av_dict_set_int(&s->streams[0]->metadata, TRACK_NUMBER, tracks[0].num, 0);
 +
 +    // parse the cues and populate Cue related fields
 +    if (!matroska->is_live) {
 +        ret = webm_dash_manifest_cues(s, init_range);
 +        if (ret < 0) {
 +            av_log(s, AV_LOG_ERROR, "Error parsing Cues\n");
 +            return ret;
 +        }
 +    }
 +
 +    // use the bandwidth from the command line if it was provided
 +    if (matroska->bandwidth > 0) {
 +        av_dict_set_int(&s->streams[0]->metadata, BANDWIDTH,
 +                        matroska->bandwidth, 0);
 +    }
 +    return 0;
 +}
 +
 +static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt)
 +{
 +    return AVERROR_EOF;
 +}
 +
 +#define OFFSET(x) offsetof(MatroskaDemuxContext, x)
 +static const AVOption options[] = {
 +    { "live", "flag indicating that the input is a live file that only has the headers.", OFFSET(is_live), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
 +    { "bandwidth", "bandwidth of this stream to be specified in the DASH manifest.", OFFSET(bandwidth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
 +    { NULL },
 +};
 +
 +static const AVClass webm_dash_class = {
 +    .class_name = "WebM DASH Manifest demuxer",
 +    .item_name  = av_default_item_name,
 +    .option     = options,
 +    .version    = LIBAVUTIL_VERSION_INT,
 +};
 +
  AVInputFormat ff_matroska_demuxer = {
      .name           = "matroska,webm",
      .long_name      = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
      .read_seek      = matroska_read_seek,
      .mime_type      = "audio/webm,audio/x-matroska,video/webm,video/x-matroska"
  };
 +
 +AVInputFormat ff_webm_dash_manifest_demuxer = {
 +    .name           = "webm_dash_manifest",
 +    .long_name      = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"),
 +    .priv_data_size = sizeof(MatroskaDemuxContext),
 +    .read_header    = webm_dash_manifest_read_header,
 +    .read_packet    = webm_dash_manifest_read_packet,
 +    .read_close     = matroska_read_close,
 +    .priv_class     = &webm_dash_class,
 +};