11import os
2+ import subprocess
23from enum import Enum
3- from functools import partial
44from typing import Dict , Union
5+ from pathlib import Path
6+ from functools import partial
57from musicalgestures ._video import MgVideo
68from musicalgestures ._utils import ffmpeg_cmd , get_length , generate_outfilename
79
@@ -44,6 +46,7 @@ class Projection(Enum):
4446
4547 equirectangular = 30 # extra option for equirectangular
4648 erp = 31
49+ gopro_360 = 32 # special gopro .360 format
4750
4851 def __str__ (self ):
4952 # collapse all aliases of erp
@@ -125,6 +128,7 @@ def convert_projection(
125128 target_projection : Union [Projection , str ],
126129 options : Dict [str , str ] = None ,
127130 print_cmd : bool = False ,
131+ test : bool = False ,
128132 ):
129133 """
130134 Convert the video to a different projection.
@@ -138,6 +142,48 @@ def convert_projection(
138142 if target_projection == self .projection :
139143 print (f"{ self } is already in target projection { target_projection } ." )
140144 return
145+ elif self .projection == Projection .gopro_360 :
146+ if test :
147+ print (
148+ f"=> Test mode: would convert { self .filename } to { target_projection } with options { options } ."
149+ )
150+
151+ # use special gopro conversion scripts
152+ assert target_projection in [
153+ Projection .equirect ,
154+ Projection .equirectangular ,
155+ Projection .dfisheye ,
156+ ], (
157+ f"Invalid target projection from gopro_360: { target_projection } , only equirect, equirectangular, and dfisheye are supported."
158+ )
159+
160+ output_name = generate_outfilename (
161+ f"{ self .filename .split ('.' )[0 ]} _{ target_projection } .mp4"
162+ )
163+ if target_projection == Projection .dfisheye :
164+ script = "ffmpeg-convert-dual-fisheye.sh"
165+ else :
166+ script = "ffmpeg-convert-v3.sh"
167+ script_path = Path (__file__ ).parent / "gopromax-conversion-tools/scripts" / script
168+
169+ cmds = [
170+ script_path ,
171+ "-i" ,
172+ self .filename ,
173+ "-n" ,
174+ output_name ,
175+ ]
176+ if options :
177+ for k , v in options .items ():
178+ cmds .append (k )
179+ cmds .append (v )
180+
181+ if test :
182+ print (f"=> Command: { ' ' .join ([str (cmd ) for cmd in cmds ])} " )
183+ subprocess .run (cmds )
184+ self .filename = output_name
185+ self .projection = target_projection
186+
141187 else :
142188 output_name = generate_outfilename (
143189 f"{ self .filename .split ('.' )[0 ]} _{ target_projection } .mp4"
@@ -191,3 +237,24 @@ def _parse_projection(self, projection: Union[str, Projection]):
191237 return projection
192238 else :
193239 raise TypeError (f"Unsupported projection type: '{ type (projection )} '." )
240+
241+
242+ ## testing gopro_360 conversion
243+ if __name__ == "__main__" :
244+ video = Mg360Video ("2023-01-01-GS010008.360" , Projection .gopro_360 )
245+ video .convert_projection (
246+ Projection .equirect ,
247+ options = {"-r" : "0:180:0" , "-s" : "00:01:10" , "-t" : "00:01:20" },
248+ test = True ,
249+ )
250+ print (f"=> Converted video path: { video .filename } " )
251+ print (f"=> Converted video projection: { video .projection } " )
252+
253+ video = Mg360Video ("2023-01-01-GS010008.360" , Projection .gopro_360 )
254+ video .convert_projection (
255+ Projection .dfisheye ,
256+ options = {"-s" : "00:01:10" , "-t" : "00:01:20" },
257+ test = True ,
258+ )
259+ print (f"=> Converted video path: { video .filename } " )
260+ print (f"=> Converted video projection: { video .projection } " )
0 commit comments