Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions spatialmedia/metadata_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ def inject_spatial_video_v2_atoms(in_fh, video_media_atom, projection, stereo_mo
sample_description.add(st3d_atom)

if projection:
svhd_atom = mpeg.sv3d.SVHDBox.create()

proj_atom = mpeg.container.Container(header_size=8)
proj_atom.name = mpeg.constants.TAG_PROJ

Expand All @@ -252,6 +254,7 @@ def inject_spatial_video_v2_atoms(in_fh, video_media_atom, projection, stereo_mo
sv3d_atom = mpeg.container.Container(header_size=8)
sv3d_atom.name = mpeg.constants.TAG_SV3D

sv3d_atom.add(svhd_atom)
sv3d_atom.add(proj_atom)

sample_description.remove(sv3d_atom.name)
Expand Down
50 changes: 48 additions & 2 deletions spatialmedia/mpeg/sv3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@

def is_supported_box_name(name):
"""Returns true if the box name is a supported sv3d box."""
return (name == constants.TAG_PRHD or
return (name == constants.TAG_SVHD or
name == constants.TAG_PRHD or
name == constants.TAG_EQUI or
name == constants.TAG_ST3D)

Expand All @@ -51,7 +52,9 @@ def load(fh, position=None, end=None):
size = struct.unpack(">I", fh.read(4))[0]
name = fh.read(4)

if name == constants.TAG_PRHD:
if name == constants.TAG_SVHD:
box = SVHDBox()
elif name == constants.TAG_PRHD:
box = PRHDBox()
elif name == constants.TAG_EQUI:
box = EQUIBox()
Expand All @@ -67,6 +70,49 @@ def load(fh, position=None, end=None):
return box


class SVHDBox(box.Box):
"""Spherical Video Header (svhd) FullBox; mandatory first child of sv3d per v2 RFC."""

def __init__(self, metadata_source="Spherical Metadata Tool"):
box.Box.__init__(self)
self.name = constants.TAG_SVHD
self.header_size = 8
self.metadata_source = metadata_source
self.content_size = 4 + len(self._metadata_source_bytes()) + 1

def _metadata_source_bytes(self):
return self.metadata_source.encode("utf-8")

@staticmethod
def create(metadata_source="Spherical Metadata Tool"):
return SVHDBox(metadata_source=metadata_source)

def print_box(self, console):
"""Prints the contents of this box to console."""
console("\t\t\tSVHD {")
console("\t\t\t\tMetadata Source: %s" % self.metadata_source)
console("\t\t\t}")

def save(self, in_fh, out_fh, delta):
if self.header_size == 16:
out_fh.write(struct.pack(">I", 1))
out_fh.write(struct.pack(">Q", self.size()))
out_fh.write(self.name)
elif self.header_size == 8:
out_fh.write(struct.pack(">I", self.size()))
out_fh.write(self.name)
out_fh.write(struct.pack(">I", 0)) # Version and flags
out_fh.write(self._metadata_source_bytes() + b"\0")

def load_content(self, in_fh):
in_fh.read(4) # Version and flags
raw = in_fh.read(self.content_size - 4)
if raw.endswith(b"\0"):
raw = raw[:-1]
self.metadata_source = raw.decode("utf-8")
self.content_size = 4 + len(self._metadata_source_bytes()) + 1


class PRHDBox(box.Box):
def __init__(self):
box.Box.__init__(self)
Expand Down
12 changes: 12 additions & 0 deletions spatialmedia_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ def test_inject_v2_equirect_mono(self):
'data/testsrc_320x240_h264.mp4',
f'{_OUTPUT_DIR}/equirect_mono.mp4'])
self.assertTrue(contents.find('SV3D') >= 0)
self.assertTrue(contents.find('SVHD') >= 0)
self.assertTrue(
contents.find('Spherical Metadata Tool') >= 0)
self.assertTrue(contents.find('PRHD') >= 0)
self.assertTrue(contents.find('EQUI') >= 0)
self.assertFalse(contents.find('ST3D') >= 0)
Expand All @@ -83,6 +86,9 @@ def test_inject_v2_equirect_mono_with_bounds(self):
'data/testsrc_320x240_h264.mp4',
f'{_OUTPUT_DIR}/equirect_mono.mp4'])
self.assertTrue(contents.find('SV3D') >= 0)
self.assertTrue(contents.find('SVHD') >= 0)
self.assertTrue(
contents.find('Spherical Metadata Tool') >= 0)
self.assertTrue(contents.find('PRHD') >= 0)
self.assertTrue(contents.find('EQUI') >= 0)
self.assertFalse(contents.find('ST3D') >= 0)
Expand All @@ -98,6 +104,9 @@ def test_inject_v2_equirect_mono_vp9(self):
f'{_OUTPUT_DIR}/equirect_mono_vp9.mp4'])

self.assertTrue(contents.find('SV3D') >= 0)
self.assertTrue(contents.find('SVHD') >= 0)
self.assertTrue(
contents.find('Spherical Metadata Tool') >= 0)
self.assertTrue(contents.find('PRHD') >= 0)
self.assertTrue(contents.find('EQUI') >= 0)
self.assertFalse(contents.find('ST3D') >= 0)
Expand All @@ -108,6 +117,9 @@ def test_inject_v2_equirect_mono_prores(self):
'data/testsrc_32x24_prores.mov',
f'{_OUTPUT_DIR}/equirect_mono_prores.mov'])
self.assertTrue(contents.find('SV3D') >= 0)
self.assertTrue(contents.find('SVHD') >= 0)
self.assertTrue(
contents.find('Spherical Metadata Tool') >= 0)
self.assertTrue(contents.find('PRHD') >= 0)
self.assertTrue(contents.find('EQUI') >= 0)
self.assertFalse(contents.find('ST3D') >= 0)
Expand Down