@@ -4,10 +4,10 @@ use gstreamer_video as gst_video;
4
4
use gstreamer_app as gst_app;
5
5
use std:: path:: Path ;
6
6
use log:: { info, error, debug} ;
7
- use gst:: MessageView ;
8
7
use gst:: prelude:: * ;
9
-
8
+ use gstreamer_pbutils :: Discoverer ;
10
9
pub use gst_video:: video_frame:: { VideoFrame , Readable , Writable , VideoFrameExt } ;
10
+ use gstreamer_pbutils:: prelude:: * ;
11
11
12
12
pub struct VideoProcessor {
13
13
pipeline : gst:: Pipeline ,
@@ -159,32 +159,117 @@ impl VideoProcessor {
159
159
let appsrc_weak = appsrc. downgrade ( ) ;
160
160
let frame_count = std:: sync:: atomic:: AtomicUsize :: new ( 0 ) ;
161
161
let total_frames = {
162
- // a temporary pipeline for frame estimation
163
- let pipeline_str = format ! (
164
- "filesrc location=\" {}\" ! decodebin ! videoconvert ! fakesink" ,
165
- input_path. to_str( ) . unwrap( )
166
- ) ;
167
- let temp_pipeline = gst:: parse:: launch ( & pipeline_str) ?;
168
- let temp_pipeline = temp_pipeline. downcast :: < gst:: Pipeline > ( ) . unwrap ( ) ;
169
- temp_pipeline. set_state ( gst:: State :: Playing ) ?;
170
-
171
- // on here, wait for pipeline to process the file
172
- let bus = temp_pipeline. bus ( ) . unwrap ( ) ;
173
162
let mut frames = 0 ;
174
- for msg in bus. iter_timed ( gst:: ClockTime :: from_seconds ( 5 ) ) {
175
- if let MessageView :: Eos ( ..) = msg. view ( ) {
176
- if let Some ( duration) = temp_pipeline. query_duration :: < gst:: ClockTime > ( ) {
177
- // Estimate frames based on duration. Most videos are 30 fps, but we can adjust this later
178
- frames = ( duration. seconds ( ) * 30 ) as usize ;
163
+ //a discoverer with 5 second timeout
164
+ let timeout = gst:: ClockTime :: from_seconds ( 5 ) ;
165
+ match Discoverer :: new ( timeout) {
166
+ Ok ( discoverer) => {
167
+ // Win Path
168
+ let path_str = input_path. to_str ( ) . unwrap ( ) ;
169
+ let uri = if path_str. starts_with ( "file://" ) {
170
+ path_str. to_string ( )
171
+ } else {
172
+ // Convert win path to URI
173
+ let absolute_path = std:: fs:: canonicalize ( & input_path)
174
+ . unwrap_or ( input_path. to_path_buf ( ) ) ;
175
+ let path_str = absolute_path. to_str ( ) . unwrap ( )
176
+ . trim_start_matches ( "\\ ?\\ " )
177
+ . replace ( "\\ " , "/" ) ;
178
+ format ! ( "file:///{}" , path_str)
179
+ } ;
180
+
181
+ info ! ( "Trying to discover URI: {}" , uri) ;
182
+ match discoverer. discover_uri ( & uri) {
183
+ Ok ( info) => {
184
+ //at this point, we will get the video info from the discoverer things going ugly in terms of code
185
+ if let Some ( video_info) = info. video_streams ( ) . get ( 0 ) {
186
+ if let Some ( caps) = video_info. caps ( ) {
187
+ if let Some ( s) = caps. structure ( 0 ) {
188
+ if let Ok ( framerate) = s. get :: < gst:: Fraction > ( "framerate" ) {
189
+ let duration = info. duration ( ) ;
190
+ let duration_secs = duration. unwrap_or ( gst:: ClockTime :: ZERO ) . seconds ( ) ;
191
+ let fps = framerate. numer ( ) as f64 / framerate. denom ( ) as f64 ;
192
+ frames = ( duration_secs as f64 * fps) as usize ;
193
+ info ! ( "Got exact framerate {}/{} fps, duration {}s, calculated frames: {}" ,
194
+ framerate. numer( ) , framerate. denom( ) ,
195
+ duration_secs, frames) ;
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }
201
+ Err ( e) => {
202
+ info ! ( "Failed to discover video: {}" , e) ;
203
+ }
204
+ }
205
+ }
206
+ Err ( e) => {
207
+ info ! ( "Failed to create discoverer: {}" , e) ;
208
+ }
209
+ }
210
+
211
+ // If we still couldn't get frames, try the pipeline method as fallback
212
+ if frames == 0 {
213
+ info ! ( "Falling back to pipeline method" ) ;
214
+ let input_str = input_path. to_str ( ) . unwrap ( ) . replace ( "\\ " , "\\ \\ " ) ;
215
+ let pipeline_str = format ! (
216
+ "filesrc location=\" {}\" ! decodebin ! video/x-raw ! fakesink" ,
217
+ input_str
218
+ ) ;
219
+
220
+ match gst:: parse:: launch ( & pipeline_str) {
221
+ Ok ( temp_element) => {
222
+ let temp_pipeline = temp_element. dynamic_cast :: < gst:: Pipeline > ( ) . unwrap ( ) ;
223
+
224
+ // Set pipeline to PAUSED and wait a bit
225
+ if temp_pipeline. set_state ( gst:: State :: Paused ) . is_ok ( ) {
226
+ // Give some time for the pipeline to settle
227
+ std:: thread:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) ;
228
+
229
+ if let Some ( duration) = temp_pipeline. query_duration :: < gst:: ClockTime > ( ) {
230
+ // Try to get framerate from the decoder pad
231
+ if let Some ( sink) = temp_pipeline. by_name ( "fakesink0" ) {
232
+ if let Some ( sink_pad) = sink. static_pad ( "sink" ) {
233
+ if let Some ( caps) = sink_pad. current_caps ( ) {
234
+ if let Some ( s) = caps. structure ( 0 ) {
235
+ if let Ok ( framerate) = s. get :: < gst:: Fraction > ( "framerate" ) {
236
+ let duration_secs = duration. seconds ( ) ;
237
+ frames = ( ( duration_secs as f64 ) *
238
+ ( framerate. numer ( ) as f64 /
239
+ framerate. denom ( ) as f64 ) ) as usize ;
240
+ info ! ( "Got framerate from pipeline: {}/{} fps" ,
241
+ framerate. numer( ) , framerate. denom( ) ) ;
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+ // If still no frames, use 30fps estimate
248
+ if frames == 0 {
249
+ frames = ( duration. seconds ( ) * 30 ) as usize ;
250
+ info ! ( "Using estimated 30fps - duration: {}s, frames: {}" ,
251
+ duration. seconds( ) , frames) ;
252
+ }
253
+ }
254
+
255
+ let _ = temp_pipeline. set_state ( gst:: State :: Null ) ;
256
+ }
257
+ }
258
+ Err ( e) => {
259
+ info ! ( "Failed to create pipeline: {}" , e) ;
179
260
}
180
- break ;
181
261
}
182
262
}
183
- temp_pipeline. set_state ( gst:: State :: Null ) ?;
263
+ // still no luck?
264
+ if frames == 0 {
265
+ frames = 1000 ;
266
+ info ! ( "Using default frame count: {}" , frames) ;
267
+ }
268
+
184
269
frames
185
270
} ;
186
271
187
- info ! ( "Estimated total frames: {}" , total_frames) ;
272
+ info ! ( "Final total frames: {}" , total_frames) ;
188
273
// Clone for new_sample closure
189
274
let appsrc_weak_cb = appsrc_weak. clone ( ) ;
190
275
// Clone for eos closure
0 commit comments