@@ -4625,6 +4625,8 @@ static int mov_read_sv3d(MOVContext *c, AVIOContext *pb, MOVAtom atom)
MOVStreamContext *sc;
int size;
int32_t yaw, pitch, roll;
+ size_t l, t, r, b;
+ size_t padding = 0;
uint32_t tag;
enum AVSphericalProjection projection;
@@ -4686,9 +4688,25 @@ static int mov_read_sv3d(MOVContext *c, AVIOContext *pb, MOVAtom atom)
switch (tag) {
case MKTAG('c','b','m','p'):
projection = AV_SPHERICAL_CUBEMAP;
+ padding = avio_rb32(pb);
break;
case MKTAG('e','q','u','i'):
- projection = AV_SPHERICAL_EQUIRECTANGULAR;
+ t = avio_rb32(pb);
+ b = avio_rb32(pb);
+ l = avio_rb32(pb);
+ r = avio_rb32(pb);
+
+ if (b >= UINT_MAX - t || r >= UINT_MAX - l) {
+ av_log(c->fc, AV_LOG_ERROR,
+ "Invalid bounding rectangle coordinates "
+ "%zu,%zu,%zu,%zu\n", l, t, r, b);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (l || t || r || b)
+ projection = AV_SPHERICAL_EQUIRECTANGULAR_TILE;
+ else
+ projection = AV_SPHERICAL_EQUIRECTANGULAR;
break;
default:
av_log(c->fc, AV_LOG_ERROR, "Unknown projection type\n");
@@ -4705,6 +4723,19 @@ static int mov_read_sv3d(MOVContext *c, AVIOContext *pb, MOVAtom atom)
sc->spherical->pitch = pitch;
sc->spherical->roll = roll;
+ sc->spherical->padding = padding;
+
+ if (projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) {
+ /* conversion from 0.32 coordinates to pixels */
+ size_t orig_width = (size_t) sc->width * UINT32_MAX / (UINT32_MAX - r - l);
+ size_t orig_height = (size_t) sc->height * UINT32_MAX / (UINT32_MAX - b - t);
+
+ /* add a (UINT32_MAX - 1) to round up integer division */
+ sc->spherical->bound_left = (orig_width * l + UINT32_MAX - 1) / UINT32_MAX;
+ sc->spherical->bound_top = (orig_height * t + UINT32_MAX - 1) / UINT32_MAX;
+ sc->spherical->bound_right = orig_width - sc->width - sc->spherical->bound_left;
+ sc->spherical->bound_bottom = orig_height - sc->height - sc->spherical->bound_top;
+ }
return 0;
}
@@ -4763,6 +4794,26 @@ static int mov_parse_uuid_spherical(MOVStreamContext *sc, AVIOContext *pb, size_
val = av_stristr(buffer, "<GSpherical:InitialViewRollDegrees>");
if (val)
sc->spherical->roll = strtol(val, NULL, 10) * (1 << 16);
+
+ /* tiling */
+ val = av_stristr(buffer, "<GSpherical:CroppedAreaLeftPixels>");
+ if (val)
+ sc->spherical->bound_left = strtol(val, NULL, 10);
+ val = av_stristr(buffer, "<GSpherical:CroppedAreaTopPixels>");
+ if (val)
+ sc->spherical->bound_top = strtol(val, NULL, 10);
+ val = av_stristr(buffer, "<GSpherical:CroppedAreaImageWidthPixels>");
+ if (val)
+ sc->spherical->bound_right =
+ sc->width - sc->spherical->bound_left - strtol(val, NULL, 10);
+ val = av_stristr(buffer, "<GSpherical:CroppedAreaImageHeightPixels>");
+ if (val)
+ sc->spherical->bound_bottom =
+ sc->height - sc->spherical->bound_top - strtol(val, NULL, 10);
+
+ if (sc->spherical->bound_left || sc->spherical->bound_top ||
+ sc->spherical->bound_right || sc->spherical->bound_bottom)
+ sc->spherical->projection = AV_SPHERICAL_EQUIRECTANGULAR_TILE;
}
out:
@@ -8,7 +8,11 @@ inverted=0
[SIDE_DATA]
side_data_type=Spherical Mapping
side_data_size=56
-projection=equirectangular
+projection=tiled equirectangular
+bound_left=148
+bound_top=73
+bound_right=147
+bound_bottom=72
yaw=45
pitch=30
roll=15