1818
1919
2020class ConvertTester :
21- def __init__ (self , convert_binary , output_dir = None , input_dir = None , reference_dir = None ):
21+ def __init__ (self , convert_binary , output_dir = None , input_dir = None , reference_dir = None , use_gstreamer = False , gst_plugin_path = None ):
2222 """Initialize the tester with the path to the convert binary."""
2323 self .convert_binary = convert_binary
2424 self .output_dir = output_dir
2525 self .input_dir = input_dir
2626 self .reference_dir = reference_dir
27- if not os .path .exists (convert_binary ):
27+ self .use_gstreamer = use_gstreamer
28+ self .gst_plugin_path = gst_plugin_path
29+
30+ if not use_gstreamer and not os .path .exists (convert_binary ):
2831 raise FileNotFoundError (f"Convert binary not found: { convert_binary } " )
2932
3033 # Test cases: (input_file, output_file, input_format, output_format, reference_file)
@@ -34,25 +37,104 @@ def __init__(self, convert_binary, output_dir=None, input_dir=None, reference_di
3437 "output_file" : "out_4056x3050_12168s_rgb888.rgb" ,
3538 "input_format" : "4056:3040:4056:YUV420P" ,
3639 "output_format" : "4056:3040:12168:RGB888" ,
37- "reference_file" : "ref_4056x3050_12168s_rgb888.rgb"
40+ "reference_file" : "ref_4056x3050_12168s_rgb888.rgb" ,
41+ "skip_gst" : False
3842 },
3943 {
4044 "input_file" : "conv_800x600_1200s_422_yuyv.yuv" ,
4145 "output_file" : "out_1600x1200_422p.yuv" ,
4246 "input_format" : "800:600:1600:YUYV" ,
4347 "output_format" : "1600:1200:1600:YUV422P" ,
44- "reference_file" : "ref_1600x1200_422p.yuv"
48+ "reference_file" : "ref_1600x1200_1600_422p.yuv" ,
49+ "skip_gst" : False
4550 },
4651 {
4752 "input_file" : "conv_rgb888_800x600_2432s.rgb" ,
4853 "output_file" : "out_4000x3000_4032s.yuv" ,
4954 "input_format" : "800:600:2432:RGB888" ,
50- "output_format" : "4000:3000:0:YUV444P" ,
51- "reference_file" : "ref_4000x3000_4032s.yuv"
55+ "output_format" : "4000:3000:4032:YUV444P" ,
56+ "reference_file" : "ref_4000x3000_4032s.yuv" ,
57+ "skip_gst" : True
5258 },
5359 # Add more test cases here as needed
5460 ]
5561
62+ def _parse_format (self , format_str ):
63+ """Parse format string like '4056:3040:4056:YUV420P' into components."""
64+ parts = format_str .split (':' )
65+ if len (parts ) != 4 :
66+ raise ValueError (f"Invalid format string: { format_str } " )
67+ return {
68+ 'width' : int (parts [0 ]),
69+ 'height' : int (parts [1 ]),
70+ 'stride' : int (parts [2 ]),
71+ 'format' : parts [3 ]
72+ }
73+
74+ def _pisp_to_gst_format (self , pisp_format ):
75+ """Convert PiSP format to GStreamer format string."""
76+ format_map = {
77+ 'YUV420P' : 'I420' ,
78+ 'YVU420P' : 'YV12' ,
79+ 'YUV422P' : 'Y42B' ,
80+ 'YUV444P' : 'Y444' ,
81+ 'YUYV' : 'YUY2' ,
82+ 'UYVY' : 'UYVY' ,
83+ 'RGB888' : 'RGB' ,
84+ }
85+ return format_map .get (pisp_format , pisp_format )
86+
87+ def run_gstreamer (self , input_file , output_file , input_format , output_format ):
88+ """Run GStreamer pipeline with pispconvert."""
89+ # Use input directory if specified
90+ if self .input_dir :
91+ input_file = os .path .join (self .input_dir , input_file )
92+
93+ # Use output directory if specified
94+ if self .output_dir :
95+ output_file = os .path .join (self .output_dir , output_file )
96+
97+ # Parse format strings
98+ in_fmt = self ._parse_format (input_format )
99+ out_fmt = self ._parse_format (output_format )
100+
101+ # Convert to GStreamer format names
102+ gst_in_format = self ._pisp_to_gst_format (in_fmt ['format' ])
103+ gst_out_format = self ._pisp_to_gst_format (out_fmt ['format' ])
104+
105+ # Build GStreamer pipeline
106+ pipeline = [
107+ 'gst-launch-1.0' ,
108+ 'filesrc' , f'location={ input_file } ' , '!' ,
109+ 'rawvideoparse' ,
110+ f'width={ in_fmt ["width" ]} ' ,
111+ f'height={ in_fmt ["height" ]} ' ,
112+ f'format={ gst_in_format .lower ()} ' ,
113+ 'framerate=30/1' , '!' ,
114+ 'pispconvert' , '!' ,
115+ f'video/x-raw,format={ gst_out_format } ,width={ out_fmt ["width" ]} ,height={ out_fmt ["height" ]} ' , '!' ,
116+ 'filesink' , f'location={ output_file } '
117+ ]
118+
119+ print (f"Running GStreamer pipeline:" )
120+ print (' ' .join (pipeline ))
121+
122+ # Set GST_PLUGIN_PATH environment variable if specified
123+ env = os .environ .copy ()
124+ if self .gst_plugin_path :
125+ env ['GST_PLUGIN_PATH' ] = self .gst_plugin_path
126+ print (f"GST_PLUGIN_PATH={ self .gst_plugin_path } " )
127+
128+ try :
129+ result = subprocess .run (pipeline , capture_output = True , text = True , check = True , env = env )
130+ print ("GStreamer pipeline completed successfully" )
131+ return True
132+ except subprocess .CalledProcessError as e :
133+ print (f"GStreamer pipeline failed with exit code { e .returncode } " )
134+ print (f"stdout: { e .stdout } " )
135+ print (f"stderr: { e .stderr } " )
136+ return False
137+
56138 def run_convert (self , input_file , output_file , input_format , output_format ):
57139 """Run the convert utility with the specified parameters."""
58140 # Use input directory if specified
@@ -137,13 +219,26 @@ def run_test_case(self, test_case):
137219 print (f"Error: Input file { input_file } does not exist" )
138220 return False
139221
140- # Run the convert utility
141- success = self .run_convert (
142- test_case ['input_file' ],
143- test_case ['output_file' ],
144- test_case ['input_format' ],
145- test_case ['output_format' ]
146- )
222+ # Skip GStreamer test if marked to skip
223+ if self .use_gstreamer and test_case .get ('skip_gst' , False ):
224+ print (f"SKIPPED: Test case marked as skip_gst=True" )
225+ return None # Return None to indicate skipped
226+
227+ # Run the convert utility or GStreamer pipeline
228+ if self .use_gstreamer :
229+ success = self .run_gstreamer (
230+ test_case ['input_file' ],
231+ test_case ['output_file' ],
232+ test_case ['input_format' ],
233+ test_case ['output_format' ]
234+ )
235+ else :
236+ success = self .run_convert (
237+ test_case ['input_file' ],
238+ test_case ['output_file' ],
239+ test_case ['input_format' ],
240+ test_case ['output_format' ]
241+ )
147242
148243 if not success :
149244 return False
@@ -161,12 +256,17 @@ def run_test_case(self, test_case):
161256 output_file = os .path .join (self .output_dir , test_case ['output_file' ])
162257 return self .compare_files (output_file , reference_file )
163258 else :
164- print (f"Reference file { reference_file } not found, skipping comparison " )
165- return True
259+ print (f"Reference file { reference_file } not found" )
260+ return False
166261
167262 def run_all_tests (self ):
168263 """Run all test cases."""
169- print (f"Testing convert utility: { self .convert_binary } " )
264+ if self .use_gstreamer :
265+ print ("Testing with GStreamer pispconvert plugin" )
266+ if self .gst_plugin_path :
267+ print (f"GST_PLUGIN_PATH: { self .gst_plugin_path } " )
268+ else :
269+ print (f"Testing convert utility: { self .convert_binary } " )
170270 if self .input_dir :
171271 print (f"Input directory: { self .input_dir } " )
172272 if self .output_dir :
@@ -177,11 +277,16 @@ def run_all_tests(self):
177277
178278 passed = 0
179279 failed = 0
280+ skipped = 0
180281
181282 for i , test_case in enumerate (self .test_cases , 1 ):
182283 print (f"\n --- Test case { i } /{ len (self .test_cases )} ---" )
183284
184- if self .run_test_case (test_case ):
285+ result = self .run_test_case (test_case )
286+ if result is None :
287+ skipped += 1
288+ print ("⊘ Test SKIPPED" )
289+ elif result :
185290 passed += 1
186291 print ("✓ Test PASSED" )
187292 else :
@@ -191,23 +296,39 @@ def run_all_tests(self):
191296 print (f"\n === Test Summary ===" )
192297 print (f"Passed: { passed } " )
193298 print (f"Failed: { failed } " )
299+ print (f"Skipped: { skipped } " )
194300 print (f"Total: { len (self .test_cases )} " )
195301
196302 return failed == 0
197303
198304
199305def main ():
200306 parser = argparse .ArgumentParser (description = "Test script for libpisp convert utility" )
201- parser .add_argument ("convert_binary" , help = "Path to the convert binary" )
307+ parser .add_argument ("convert_binary" , nargs = '?' , default = None , help = "Path to the convert binary (not needed with --gst-plugin-path) " )
202308 parser .add_argument ("--test-dir" , help = "Directory containing test files" )
203309 parser .add_argument ("--in" , dest = "input_dir" , help = "Directory containing input files" )
204310 parser .add_argument ("--out" , help = "Directory where output files will be written" )
205311 parser .add_argument ("--ref" , help = "Directory containing reference files" )
312+ parser .add_argument ("--gst-plugin-path" , help = "Path to GStreamer plugin directory (enables GStreamer testing)" )
206313
207314 args = parser .parse_args ()
208315
209316 try :
210- tester = ConvertTester (args .convert_binary , args .out , args .input_dir , args .ref )
317+ # Determine if using GStreamer based on --gst-plugin-path
318+ use_gstreamer = args .gst_plugin_path is not None
319+
320+ # Validate arguments
321+ if not use_gstreamer and not args .convert_binary :
322+ parser .error ("convert_binary is required unless --gst-plugin-path is specified" )
323+
324+ tester = ConvertTester (
325+ args .convert_binary ,
326+ args .out ,
327+ args .input_dir ,
328+ args .ref ,
329+ use_gstreamer = use_gstreamer ,
330+ gst_plugin_path = args .gst_plugin_path
331+ )
211332
212333 # Change to test directory if specified
213334 if args .test_dir :
0 commit comments