4949 "</GSpherical:StitchingSoftware>" \
5050 "<GSpherical:ProjectionType>equirectangular</GSpherical:ProjectionType>"
5151
52+ NOT_SPHERICAL_XML_CONTENTS = \
53+ "<GSpherical:Spherical>false</GSpherical:Spherical>" \
54+ "<GSpherical:Stitched>false</GSpherical:Stitched>" \
55+ "<GSpherical:StitchingSoftware>" \
56+ "Spherical Metadata Tool" \
57+ "</GSpherical:StitchingSoftware>" \
58+ "<GSpherical:ProjectionType>rectangular</GSpherical:ProjectionType>"
59+
5260SPHERICAL_XML_CONTENTS_TOP_BOTTOM = \
5361 "<GSpherical:StereoMode>top-bottom</GSpherical:StereoMode>"
5462SPHERICAL_XML_CONTENTS_LEFT_RIGHT = \
8795]
8896
8997class Metadata (object ):
90- def __init__ (self ):
98+ def __init__ (self , projection = None , stereo_mode = None ):
99+ self .projection = None if (not projection or projection == "none" ) else projection
100+ self .stereo_mode = None if (not stereo_mode or stereo_mode == "none" ) else stereo_mode
91101 self .video = None
92102 self .audio = None
93103
@@ -144,7 +154,7 @@ def spherical_uuid(metadata):
144154 return uuid_leaf
145155
146156
147- def mpeg4_add_spherical (mpeg4_file , in_fh , metadata ):
157+ def mpeg4_add_spherical_xml_v1 (mpeg4_file , in_fh , metadata ):
148158 """Adds a spherical uuid box to an mpeg4 file for all video tracks.
149159
150160 Args:
@@ -176,6 +186,73 @@ def mpeg4_add_spherical(mpeg4_file, in_fh, metadata):
176186 mpeg4_file .resize ()
177187 return True
178188
189+ def mpeg4_add_spherical_v2 (mpeg4_file , in_fh , projection , stereo_mode ):
190+ for element in mpeg4_file .moov_box .contents :
191+ if element .name == mpeg .constants .TAG_TRAK :
192+ for sub_element in element .contents :
193+ if sub_element .name != mpeg .constants .TAG_MDIA :
194+ continue
195+ for mdia_sub_element in sub_element .contents :
196+ if mdia_sub_element .name != mpeg .constants .TAG_HDLR :
197+ continue
198+ position = mdia_sub_element .content_start () + 8
199+ in_fh .seek (position )
200+ if in_fh .read (4 ) == mpeg .constants .TAG_VIDE :
201+ ret = inject_spatial_video_v2_atoms (
202+ in_fh , sub_element , projection , stereo_mode )
203+ mpeg4_file .resize ()
204+ return ret
205+
206+
207+ def inject_spatial_video_v2_atoms (in_fh , video_media_atom , projection , stereo_mode ):
208+ """Adds spherical v2 boxes to an mpeg4 file for all video tracks.
209+
210+ Args:
211+ mpeg4_file: mpeg4, Mpeg4 file structure to add metadata.
212+ in_fh: file handle, Source for uncached file contents.
213+ metadata: string, xml metadata to inject into spherical tag.
214+ """
215+ for atom in video_media_atom .contents :
216+ if atom .name != mpeg .constants .TAG_MINF :
217+ continue
218+ for element in atom .contents :
219+ if element .name != mpeg .constants .TAG_STBL :
220+ continue
221+ for sub_element in element .contents :
222+ if sub_element .name != mpeg .constants .TAG_STSD :
223+ continue
224+ for sample_description in sub_element .contents :
225+ if sample_description .name in \
226+ mpeg .constants .VIDEO_SAMPLE_DESCRIPTIONS :
227+ in_fh .seek (sample_description .position +
228+ sample_description .header_size + 16 )
229+ # Should remove any existing boxes...
230+ if stereo_mode :
231+ st3d_atom = mpeg .sv3d .ST3DBox .create ()
232+ st3d_atom .name = mpeg .constants .TAG_ST3D
233+ st3d_atom .set_stereo_mode_from_string (stereo_mode )
234+
235+ sample_description .remove (st3d_atom .name )
236+ sample_description .add (st3d_atom )
237+
238+ if projection :
239+ proj_atom = mpeg .container .Container (header_size = 8 )
240+ proj_atom .name = mpeg .constants .TAG_PROJ
241+
242+ proj_atom .add (mpeg .sv3d .PRHDBox .create ())
243+ proj_atom .add (mpeg .sv3d .EQUIBox .create ())
244+
245+ sv3d_atom = mpeg .container .Container (header_size = 8 )
246+ sv3d_atom .name = mpeg .constants .TAG_SV3D
247+
248+ sv3d_atom .add (proj_atom )
249+
250+ sample_description .remove (sv3d_atom .name )
251+ sample_description .add (sv3d_atom )
252+
253+ return True
254+
255+
179256def mpeg4_add_spatial_audio (mpeg4_file , in_fh , audio_metadata , console ):
180257 """Adds spatial audio metadata to the first audio track of the input
181258 mpeg4_file. Returns False on failure.
@@ -346,6 +423,21 @@ def parse_spherical_mpeg4(mpeg4_file, fh, console):
346423 if sa3d_elem .name == mpeg .constants .TAG_SA3D :
347424 sa3d_elem .print_box (console )
348425 metadata .audio = sa3d_elem
426+
427+ for sv3d_container_elem in stsd_elem .contents :
428+ if sv3d_container_elem .name not in \
429+ mpeg .constants .VIDEO_SAMPLE_DESCRIPTIONS :
430+ continue
431+ for sub_elem in sv3d_container_elem .contents :
432+ if sub_elem .name == mpeg .constants .TAG_SV3D :
433+ console ("\t \t SV3D {" )
434+ sub_elem .print_box (console )
435+ console ("\t \t }" )
436+ elif sub_elem .name == mpeg .constants .TAG_ST3D :
437+ console ("\t \t ST3D {" )
438+ sub_elem .print_box (console )
439+ console ("\t \t } " )
440+
349441 return metadata
350442
351443def parse_mpeg4 (input_file , console ):
@@ -369,9 +461,13 @@ def inject_mpeg4(input_file, output_file, metadata, console):
369461 if mpeg4_file is None :
370462 console ("Error file could not be opened." )
371463
372- if not mpeg4_add_spherical (mpeg4_file , in_fh , metadata .video ):
464+ if metadata . video and not mpeg4_add_spherical_xml_v1 (mpeg4_file , in_fh , metadata .video ):
373465 console ("Error failed to insert spherical data" )
374466
467+ if ((metadata .projection or metadata .stereo_mode )
468+ and not mpeg4_add_spherical_v2 (mpeg4_file , in_fh , metadata .projection , metadata .stereo_mode )):
469+ console ("Error failed to insert spherical data v2" )
470+
375471 if metadata .audio :
376472 if not mpeg4_add_audio_metadata (
377473 mpeg4_file , in_fh , metadata .audio , console ):
@@ -433,7 +529,7 @@ def inject_metadata(src, dest, metadata, console):
433529 console ("Unknown file type" )
434530
435531
436- def generate_spherical_xml (stereo = None , crop = None ):
532+ def generate_spherical_xml (projection = "equiretangular" , stereo = None , crop = None ):
437533 # Configure inject xml.
438534 additional_xml = ""
439535 if stereo == "top-bottom" :
@@ -499,7 +595,8 @@ def generate_spherical_xml(stereo=None, crop=None):
499595 cropped_offset_left_pixels , cropped_offset_top_pixels )
500596
501597 spherical_xml = (SPHERICAL_XML_HEADER +
502- SPHERICAL_XML_CONTENTS +
598+ (SPHERICAL_XML_CONTENTS if projection == "equirectangular"
599+ else NOT_SPHERICAL_XML_CONTENTS ) +
503600 additional_xml +
504601 SPHERICAL_XML_FOOTER )
505602 return spherical_xml
0 commit comments