]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/matroskadec.c
avformat/hlsenc: second_levels flags process function extract
[ffmpeg] / libavformat / matroskadec.c
index f79511e4f1ee3b7668bdd77e98e11f43e4a070a3..fdb23ab05e8b2e4f4deba4a4daedd9c314fa6ffb 100644 (file)
@@ -43,6 +43,7 @@
 #include "libavutil/mathematics.h"
 #include "libavutil/opt.h"
 #include "libavutil/time_internal.h"
+#include "libavutil/spherical.h"
 
 #include "libavcodec/bytestream.h"
 #include "libavcodec/flac.h"
@@ -88,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;
@@ -161,6 +163,14 @@ typedef struct MatroskaTrackVideoColor {
     MatroskaMasteringMeta mastering_meta;
 } MatroskaTrackVideoColor;
 
+typedef struct MatroskaTrackVideoProjection {
+    uint64_t type;
+    EbmlBin private;
+    double yaw;
+    double pitch;
+    double roll;
+} MatroskaTrackVideoProjection;
+
 typedef struct MatroskaTrackVideo {
     double   frame_rate;
     uint64_t display_width;
@@ -173,7 +183,8 @@ typedef struct MatroskaTrackVideo {
     uint64_t field_order;
     uint64_t stereo_mode;
     uint64_t alpha_mode;
-    MatroskaTrackVideoColor color;
+    EbmlList color;
+    MatroskaTrackVideoProjection projection;
 } MatroskaTrackVideo;
 
 typedef struct MatroskaTrackAudio {
@@ -424,6 +435,15 @@ static const EbmlSyntax matroska_track_video_color[] = {
     { 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 } },
+    { 0 }
+};
+
 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), { .u=-1 } },
@@ -432,7 +452,8 @@ static const EbmlSyntax matroska_track_video[] = {
     { MATROSKA_ID_VIDEOPIXELHEIGHT,    EBML_UINT,  0, offsetof(MatroskaTrackVideo, pixel_height) },
     { 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,  0, offsetof(MatroskaTrackVideo, color), { .n = matroska_track_video_color } },
+    { 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 },
@@ -676,7 +697,7 @@ static const EbmlSyntax matroska_blockgroup[] = {
     { MATROSKA_ID_SIMPLEBLOCK,    EBML_BIN,  0, offsetof(MatroskaBlock, bin) },
     { 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) },
+    { 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 }
@@ -1051,6 +1072,9 @@ static int ebml_parse_nest(MatroskaDemuxContext *matroska, EbmlSyntax *syntax,
 
     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;
@@ -1237,6 +1261,7 @@ static void ebml_free(EbmlSyntax *syntax, void *data)
                      j++, ptr += syntax[i].list_elem_size)
                     ebml_free(syntax[i].def.n, ptr);
                 av_freep(&list->elem);
+                list->nb_elem = 0;
             } else
                 ebml_free(syntax[i].def.n, data_off);
         default:
@@ -1806,34 +1831,40 @@ static void mkv_stereo_mode_display_mul(int stereo_mode,
 }
 
 static int mkv_parse_video_color(AVStream *st, const MatroskaTrack *track) {
-    const MatroskaMasteringMeta* mastering_meta =
-        &track->video.color.mastering_meta;
+    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.
-    const int has_mastering_primaries =
+    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;
-    const int has_mastering_luminance = mastering_meta->max_luminance > 0;
-
-    if (track->video.color.matrix_coefficients != AVCOL_SPC_RESERVED)
-        st->codecpar->color_space = track->video.color.matrix_coefficients;
-    if (track->video.color.primaries != AVCOL_PRI_RESERVED &&
-        track->video.color.primaries != AVCOL_PRI_RESERVED0)
-        st->codecpar->color_primaries = track->video.color.primaries;
-    if (track->video.color.transfer_characteristics != AVCOL_TRC_RESERVED &&
-        track->video.color.transfer_characteristics != AVCOL_TRC_RESERVED0)
-        st->codecpar->color_trc = track->video.color.transfer_characteristics;
-    if (track->video.color.range != AVCOL_RANGE_UNSPECIFIED &&
-        track->video.color.range <= AVCOL_RANGE_JPEG)
-        st->codecpar->color_range = track->video.color.range;
-    if (track->video.color.chroma_siting_horz != MATROSKA_COLOUR_CHROMASITINGHORZ_UNDETERMINED &&
-        track->video.color.chroma_siting_vert != MATROSKA_COLOUR_CHROMASITINGVERT_UNDETERMINED &&
-        track->video.color.chroma_siting_horz  < MATROSKA_COLOUR_CHROMASITINGHORZ_NB &&
-        track->video.color.chroma_siting_vert  < MATROSKA_COLOUR_CHROMASITINGVERT_NB) {
+    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((track->video.color.chroma_siting_horz - 1) << 7,
-                                       (track->video.color.chroma_siting_vert - 1) << 7);
+            avcodec_chroma_pos_to_enum((color->chroma_siting_horz - 1) << 7,
+                                       (color->chroma_siting_vert - 1) << 7);
     }
 
     if (has_mastering_primaries || has_mastering_luminance) {
@@ -1878,6 +1909,99 @@ static int mkv_parse_video_color(AVStream *st, const MatroskaTrack *track) {
     return 0;
 }
 
+static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track) {
+    AVSphericalMapping *spherical;
+    enum AVSphericalProjection projection;
+    size_t spherical_size;
+    size_t l = 0, t = 0, r = 0, b = 0;
+    size_t padding = 0;
+    int ret;
+    GetByteContext gb;
+
+    bytestream2_init(&gb, track->video.projection.private.data,
+                     track->video.projection.private.size);
+
+    if (bytestream2_get_byte(&gb) != 0) {
+        av_log(NULL, AV_LOG_WARNING, "Unknown spherical metadata\n");
+        return 0;
+    }
+
+    bytestream2_skip(&gb, 3); // flags
+
+    switch (track->video.projection.type) {
+    case MATROSKA_VIDEO_PROJECTION_TYPE_EQUIRECTANGULAR:
+        if (track->video.projection.private.size == 20) {
+            t = bytestream2_get_be32(&gb);
+            b = bytestream2_get_be32(&gb);
+            l = bytestream2_get_be32(&gb);
+            r = bytestream2_get_be32(&gb);
+
+            if (b >= UINT_MAX - t || r >= UINT_MAX - l) {
+                av_log(NULL, AV_LOG_ERROR,
+                       "Invalid bounding rectangle coordinates "
+                       "%"SIZE_SPECIFIER",%"SIZE_SPECIFIER","
+                       "%"SIZE_SPECIFIER",%"SIZE_SPECIFIER"\n",
+                       l, t, r, b);
+                return AVERROR_INVALIDDATA;
+            }
+        } else if (track->video.projection.private.size != 0) {
+            av_log(NULL, AV_LOG_ERROR, "Unknown spherical metadata\n");
+            return AVERROR_INVALIDDATA;
+        }
+
+        if (l || t || r || b)
+            projection = AV_SPHERICAL_EQUIRECTANGULAR_TILE;
+        else
+            projection = AV_SPHERICAL_EQUIRECTANGULAR;
+        break;
+    case MATROSKA_VIDEO_PROJECTION_TYPE_CUBEMAP:
+        if (track->video.projection.private.size < 4) {
+            av_log(NULL, AV_LOG_ERROR, "Missing projection private properties\n");
+            return AVERROR_INVALIDDATA;
+        } else if (track->video.projection.private.size == 12) {
+            uint32_t layout = bytestream2_get_be32(&gb);
+            if (layout) {
+                av_log(NULL, AV_LOG_WARNING,
+                       "Unknown spherical cubemap layout %"PRIu32"\n", layout);
+                return 0;
+            }
+            projection = AV_SPHERICAL_CUBEMAP;
+            padding = bytestream2_get_be32(&gb);
+        } else {
+            av_log(NULL, AV_LOG_ERROR, "Unknown spherical metadata\n");
+            return AVERROR_INVALIDDATA;
+        }
+        break;
+    default:
+        return 0;
+    }
+
+    spherical = av_spherical_alloc(&spherical_size);
+    if (!spherical)
+        return AVERROR(ENOMEM);
+    spherical->projection = projection;
+
+    spherical->yaw   = (int32_t)(track->video.projection.yaw   * (1 << 16));
+    spherical->pitch = (int32_t)(track->video.projection.pitch * (1 << 16));
+    spherical->roll  = (int32_t)(track->video.projection.roll  * (1 << 16));
+
+    spherical->padding = padding;
+
+    spherical->bound_left   = l;
+    spherical->bound_top    = t;
+    spherical->bound_right  = r;
+    spherical->bound_bottom = b;
+
+    ret = av_stream_add_side_data(st, AV_PKT_DATA_SPHERICAL, (uint8_t *)spherical,
+                                  spherical_size);
+    if (ret < 0) {
+        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;
@@ -2220,7 +2344,7 @@ static int matroska_parse_tracks(AVFormatContext *s)
                 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_array(track->audio.sub_packet_h,
                                                track->audio.frame_size);
@@ -2355,11 +2479,12 @@ static int matroska_parse_tracks(AVFormatContext *s)
                     return ret;
             }
 
-            if (s->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) {
-                int ret = mkv_parse_video_color(st, track);
-                if (ret < 0)
-                    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;
@@ -3166,9 +3291,11 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,
         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 &&
@@ -3293,7 +3420,7 @@ static int matroska_parse_cluster_incremental(MatroskaDemuxContext *matroska)
         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)
@@ -3331,7 +3458,7 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska)
     blocks      = blocks_list->elem;
     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;
+            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,
@@ -3364,7 +3491,7 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
     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 > 0) {
@@ -3398,18 +3525,6 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
         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 &&
-            tracks[i].stream->discard != AVDISCARD_ALL) {
-            index_sub = av_index_search_timestamp(
-                tracks[i].stream, st->index_entries[index].timestamp,
-                AVSEEK_FLAG_BACKWARD);
-            while (index_sub >= 0 &&
-                  index_min > 0 &&
-                  tracks[i].stream->index_entries[index_sub].pos < st->index_entries[index_min].pos &&
-                  st->index_entries[index].timestamp - tracks[i].stream->index_entries[index_sub].timestamp < 30000000000 / matroska->time_scale)
-                index_min--;
-        }
     }
 
     avio_seek(s->pb, st->index_entries[index_min].pos, SEEK_SET);