From 079ad1bf151a8578b81ac1f916e206ef37f286e4 Mon Sep 17 00:00:00 2001 From: Xavi Artigas Date: Thu, 16 May 2013 17:39:15 +0200 Subject: [PATCH] Add slider and text widgets that track the current position and duration. The slider is not seekable yet. --- .../xcode iOS/Tutorial 4/GStreamerBackend.m | 46 ++++++++++++++++++- .../Tutorial 4/GStreamerBackendDelegate.h | 3 ++ .../Tutorial 4/VideoViewController.h | 3 ++ .../Tutorial 4/VideoViewController.m | 44 ++++++++++++++++++ .../en.lproj/MainStoryboard_iPad.storyboard | 45 ++++++++---------- .../en.lproj/MainStoryboard_iPhone.storyboard | 23 ++++++++-- 6 files changed, 133 insertions(+), 31 deletions(-) diff --git a/gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackend.m b/gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackend.m index 60758cc336..f789edc1ed 100644 --- a/gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackend.m +++ b/gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackend.m @@ -21,6 +21,8 @@ GST_DEBUG_CATEGORY_STATIC (debug_category); GMainLoop *main_loop; /* GLib main loop */ gboolean initialized; /* To avoid informing the UI multiple times about the initialization */ UIView *ui_video_view; /* UIView that holds the video */ + GstState state; /* Current pipeline state */ + gint64 duration; /* Cached clip duration */ } /* @@ -33,6 +35,7 @@ GST_DEBUG_CATEGORY_STATIC (debug_category); { self->ui_delegate = uiDelegate; self->ui_video_view = video_view; + self->duration = GST_CLOCK_TIME_NONE; GST_DEBUG_CATEGORY_INIT (debug_category, "tutorial-4", 0, "iOS tutorial 4"); gst_debug_set_threshold_for_name("tutorial-4", GST_LEVEL_DEBUG); @@ -84,6 +87,39 @@ GST_DEBUG_CATEGORY_STATIC (debug_category); } } +/* Tell the application what is the current position and clip duration */ +-(void) setCurrentUIPosition:(gint)pos duration:(gint)dur +{ + if(ui_delegate && [ui_delegate respondsToSelector:@selector(setCurrentPosition:duration:)]) + { + [ui_delegate setCurrentPosition:pos duration:dur]; + } +} + +/* If we have pipeline and it is running, query the current position and clip duration and inform + * the application */ +static gboolean refresh_ui (GStreamerBackend *self) { + GstFormat fmt = GST_FORMAT_TIME; + gint64 position; + + /* We do not want to update anything unless we have a working pipeline in the PAUSED or PLAYING state */ + if (!self || !self->pipeline || self->state < GST_STATE_PAUSED) + return TRUE; + + /* If we didn't know it yet, query the stream duration */ + if (!GST_CLOCK_TIME_IS_VALID (self->duration)) { + if (!gst_element_query_duration (self->pipeline, &fmt, &self->duration)) { + GST_WARNING ("Could not query current duration"); + } + } + + if (gst_element_query_position (self->pipeline, &fmt, &position)) { + /* The UI expects these values in milliseconds, and GStreamer provides nanoseconds */ + [self setCurrentUIPosition:position / GST_MSECOND duration:self->duration / GST_MSECOND]; + } + return TRUE; +} + static void check_media_size (GStreamerBackend *self) { GstElement *video_sink; GstPad *video_sink_pad; @@ -138,6 +174,7 @@ static void state_changed_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *se gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state); /* Only pay attention to messages coming from the pipeline, not its children */ if (GST_MESSAGE_SRC (msg) == GST_OBJECT (self->pipeline)) { + self->state = new_state; gchar *message = g_strdup_printf("State changed to %s", gst_element_state_get_name(new_state)); [self setUIMessage:message]; g_free (message); @@ -167,6 +204,7 @@ static void state_changed_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *se -(void) app_function { GstBus *bus; + GSource *timeout_source; GSource *bus_source; GError *error = NULL; @@ -205,7 +243,13 @@ static void state_changed_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *se g_signal_connect (G_OBJECT (bus), "message::error", (GCallback)error_cb, (__bridge void *)self); g_signal_connect (G_OBJECT (bus), "message::state-changed", (GCallback)state_changed_cb, (__bridge void *)self); gst_object_unref (bus); - + + /* Register a function that GLib will call 4 times per second */ + timeout_source = g_timeout_source_new (250); + g_source_set_callback (timeout_source, (GSourceFunc)refresh_ui, (__bridge void *)self, NULL); + g_source_attach (timeout_source, context); + g_source_unref (timeout_source); + /* Create a GLib Main Loop and set it to run */ GST_DEBUG ("Entering main loop..."); main_loop = g_main_loop_new (context, FALSE); diff --git a/gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackendDelegate.h b/gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackendDelegate.h index 739461cfaa..01981fad95 100644 --- a/gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackendDelegate.h +++ b/gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackendDelegate.h @@ -14,4 +14,7 @@ /* Called when the media size is first discovered or it changes */ -(void) mediaSizeChanged:(NSInteger)width height:(NSInteger)height; +/* Called when the media position changes. Times in milliseconds */ +-(void) setCurrentPosition:(NSInteger)position duration:(NSInteger)duration; + @end diff --git a/gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.h b/gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.h index 37064f1541..fe3d0ff6fd 100644 --- a/gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.h +++ b/gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.h @@ -9,6 +9,9 @@ IBOutlet UIView *video_container_view; IBOutlet NSLayoutConstraint *video_width_constraint; IBOutlet NSLayoutConstraint *video_height_constraint; + IBOutlet UIToolbar *toolbar; + IBOutlet UITextField *time_label; + IBOutlet UISlider *time_slider; } @property (retain,nonatomic) NSString *uri; diff --git a/gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.m b/gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.m index 6b4ae04372..57e224c3ab 100644 --- a/gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.m +++ b/gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.m @@ -14,6 +14,37 @@ @synthesize uri; +/* + * Private methods + */ +- (void) updateTimeWidget +{ + NSInteger position = time_slider.value / 1000; + NSInteger duration = time_slider.maximumValue / 1000; + NSString *position_txt = @" -- "; + NSString *duration_txt = @" -- "; + + if (duration > 0) { + NSUInteger hours = duration / (60 * 60); + NSUInteger minutes = (duration / 60) % 60; + NSUInteger seconds = duration % 60; + + duration_txt = [NSString stringWithFormat:@"%02u:%02u:%02u", hours, minutes, seconds]; + } + if (position > 0) { + NSUInteger hours = position / (60 * 60); + NSUInteger minutes = (position / 60) % 60; + NSUInteger seconds = position % 60; + + position_txt = [NSString stringWithFormat:@"%02u:%02u:%02u", hours, minutes, seconds]; + } + + NSString *text = [NSString stringWithFormat:@"%@ / %@", + position_txt, duration_txt]; + + time_label.text = text; +} + /* * Methods from UIViewController */ @@ -58,6 +89,8 @@ [gst_backend pause]; } +/* Called when the size of the main view has changed, so we can + * resize the sub-views in ways not allowed by storyboarding. */ - (void)viewDidLayoutSubviews { CGFloat view_width = video_container_view.bounds.size.width; @@ -73,6 +106,8 @@ video_width_constraint.constant = correct_width; video_height_constraint.constant = view_height; } + + time_slider.frame = CGRectMake(time_slider.frame.origin.x, time_slider.frame.origin.y, toolbar.frame.size.width - time_slider.frame.origin.x - 8, time_slider.frame.size.height); } /* @@ -107,4 +142,13 @@ }); } +-(void) setCurrentPosition:(NSInteger)position duration:(NSInteger)duration +{ + dispatch_async(dispatch_get_main_queue(), ^{ + time_slider.maximumValue = duration; + time_slider.value = position; + [self updateTimeWidget]; + }); +} + @end diff --git a/gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPad.storyboard b/gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPad.storyboard index c1b9cf238f..e12bdb7d78 100644 --- a/gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPad.storyboard +++ b/gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPad.storyboard @@ -57,7 +57,6 @@ - @@ -68,7 +67,21 @@ - + + + + + + + + + + + + + + + @@ -92,6 +105,9 @@ + + + @@ -154,31 +170,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPhone.storyboard b/gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPhone.storyboard index 58347d24b7..5d9108be37 100644 --- a/gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPhone.storyboard +++ b/gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPhone.storyboard @@ -69,20 +69,32 @@ + + + + + + + + + - - + + + + - + + @@ -94,6 +106,8 @@ + + @@ -178,6 +192,9 @@ + + +