Add slider and text widgets that track the current position and duration. The slider...
authorXavi Artigas <xartigas@fluendo.com>
Thu, 16 May 2013 15:39:15 +0000 (17:39 +0200)
committerXavi Artigas <xartigas@fluendo.com>
Thu, 16 May 2013 15:39:15 +0000 (17:39 +0200)
gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackend.m
gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackendDelegate.h
gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.h
gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.m
gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPad.storyboard
gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPhone.storyboard

index 60758cc..f789edc 100644 (file)
@@ -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);
index 739461c..01981fa 100644 (file)
@@ -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
index 37064f1..fe3d0ff 100644 (file)
@@ -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;
index 6b4ae04..57e224c 100644 (file)
 @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;
         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);
 }
 
 /*
     });
 }
 
+-(void) setCurrentPosition:(NSInteger)position duration:(NSInteger)duration
+{
+    dispatch_async(dispatch_get_main_queue(), ^{
+        time_slider.maximumValue = duration;
+        time_slider.value = position;
+        [self updateTimeWidget];
+    });
+}
+
 @end
index c1b9cf2..e12bdb7 100644 (file)
@@ -57,7 +57,6 @@
                                     <constraint firstAttribute="height" constant="44" type="user" id="EwL-Ma-A4v"/>
                                 </constraints>
                                 <items>
-                                    <barButtonItem style="plain" systemItem="flexibleSpace" id="onU-hf-FS4"/>
                                     <barButtonItem systemItem="play" id="UlF-Ga-2VX">
                                         <connections>
                                             <action selector="play:" destination="z7O-8l-Zeo" id="5xC-uv-9lM"/>
                                             <action selector="pause:" destination="z7O-8l-Zeo" id="BYM-2X-Tel"/>
                                         </connections>
                                     </barButtonItem>
-                                    <barButtonItem style="plain" systemItem="flexibleSpace" id="urI-U7-ALw"/>
+                                    <barButtonItem style="plain" id="s39-nx-e0L">
+                                        <textField key="customView" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="00:00:00 / 00:00:00" borderStyle="roundedRect" textAlignment="center" minimumFontSize="17" id="G8q-Tu-Qx0" userLabel="Time">
+                                            <rect key="frame" x="90" y="7" width="140" height="30"/>
+                                            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                            <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <textInputTraits key="textInputTraits"/>
+                                        </textField>
+                                    </barButtonItem>
+                                    <barButtonItem style="plain" id="2n0-TO-8Ss">
+                                        <slider key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="1" id="ufs-E5-87w" userLabel="Slider">
+                                            <rect key="frame" x="240" y="11" width="118" height="23"/>
+                                            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                        </slider>
+                                    </barButtonItem>
                                 </items>
                             </toolbar>
                         </subviews>
                         <outlet property="message_label" destination="iLX-h1-Ko5" id="Q0Y-3J-zis"/>
                         <outlet property="pause_button" destination="J3O-8j-Tkt" id="Dls-sg-FPm"/>
                         <outlet property="play_button" destination="UlF-Ga-2VX" id="243-yq-GEe"/>
+                        <outlet property="time_label" destination="G8q-Tu-Qx0" id="jpR-6u-zp0"/>
+                        <outlet property="time_slider" destination="ufs-E5-87w" id="ZUd-jO-4qN"/>
+                        <outlet property="toolbar" destination="MUi-CE-Ydy" id="Lme-0P-4Xq"/>
                         <outlet property="video_container_view" destination="xWd-bg-0b6" id="7dL-Mp-QGc"/>
                         <outlet property="video_height_constraint" destination="A9A-eK-7QX" id="rMe-ze-8l5"/>
                         <outlet property="video_view" destination="6tN-97-YoQ" id="Q0n-dR-hqv"/>
             <point key="canvasLocation" x="-478" y="-199"/>
         </scene>
     </scenes>
-    <classes>
-        <class className="EaglUIView" superclassName="UIView">
-            <source key="sourceIdentifier" type="project" relativePath="./Classes/EaglUIView.h"/>
-        </class>
-        <class className="LibraryViewController" superclassName="UITableViewController">
-            <source key="sourceIdentifier" type="project" relativePath="./Classes/LibraryViewController.h"/>
-            <relationships>
-                <relationship kind="action" name="refresh:"/>
-            </relationships>
-        </class>
-        <class className="VideoViewController" superclassName="UIViewController">
-            <source key="sourceIdentifier" type="project" relativePath="./Classes/VideoViewController.h"/>
-            <relationships>
-                <relationship kind="action" name="pause:"/>
-                <relationship kind="action" name="play:"/>
-                <relationship kind="outlet" name="message_label" candidateClass="UILabel"/>
-                <relationship kind="outlet" name="pause_button" candidateClass="UIBarButtonItem"/>
-                <relationship kind="outlet" name="play_button" candidateClass="UIBarButtonItem"/>
-                <relationship kind="outlet" name="video_container_view" candidateClass="UIView"/>
-                <relationship kind="outlet" name="video_height_constraint" candidateClass="NSLayoutConstraint"/>
-                <relationship kind="outlet" name="video_view" candidateClass="UIView"/>
-                <relationship kind="outlet" name="video_width_constraint" candidateClass="NSLayoutConstraint"/>
-            </relationships>
-        </class>
-    </classes>
     <simulatedMetricsContainer key="defaultSimulatedMetrics">
         <simulatedStatusBarMetrics key="statusBar" statusBarStyle="blackTranslucent"/>
         <simulatedOrientationMetrics key="orientation"/>
index 58347d2..5d9108b 100644 (file)
                                             <action selector="pause:" destination="iMo-Z9-PrL" id="mef-Ij-Agl"/>
                                         </connections>
                                     </barButtonItem>
+                                    <barButtonItem style="plain" id="VUJ-y8-aWS">
+                                        <textField key="customView" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="00:00:00 / 00:00:00" borderStyle="roundedRect" textAlignment="center" minimumFontSize="17" id="R6T-PH-VPd" userLabel="Time">
+                                            <rect key="frame" x="132" y="7" width="139" height="30"/>
+                                            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                            <textInputTraits key="textInputTraits"/>
+                                        </textField>
+                                    </barButtonItem>
                                     <barButtonItem style="plain" systemItem="flexibleSpace" id="LSl-TA-0qV"/>
                                 </items>
                             </toolbar>
+                            <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="4Ns-t9-gs7" userLabel="Slider"/>
                         </subviews>
                         <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                         <constraints>
-                            <constraint firstItem="A2w-65-QSF" firstAttribute="bottom" secondItem="zgN-eK-M4Q" secondAttribute="bottom" constant="20" symbolic="YES" type="user" id="0OY-rR-W60"/>
-                            <constraint firstItem="LmB-wv-Ztp" firstAttribute="top" secondItem="nA3-W2-kn7" secondAttribute="bottom" type="user" id="7xY-sq-nDU"/>
+                            <constraint firstItem="LmB-wv-Ztp" firstAttribute="top" secondItem="nA3-W2-kn7" secondAttribute="bottom" type="user" id="5TL-Gq-YnE"/>
                             <constraint firstItem="A2w-65-QSF" firstAttribute="leading" secondItem="zgN-eK-M4Q" secondAttribute="leading" constant="20" symbolic="YES" type="user" id="Aki-Hx-2C9"/>
+                            <constraint firstItem="4Ns-t9-gs7" firstAttribute="trailing" secondItem="zgN-eK-M4Q" secondAttribute="trailing" type="default" id="HFi-uV-wdo"/>
+                            <constraint firstItem="4Ns-t9-gs7" firstAttribute="bottom" secondItem="A2w-65-QSF" secondAttribute="top" type="default" id="Iml-yC-EDE"/>
+                            <constraint firstItem="4Ns-t9-gs7" firstAttribute="top" secondItem="LmB-wv-Ztp" secondAttribute="bottom" type="user" id="TUy-Wk-veP"/>
                             <constraint firstItem="A2w-65-QSF" firstAttribute="trailing" secondItem="zgN-eK-M4Q" secondAttribute="trailing" constant="20" symbolic="YES" type="user" id="WWj-l6-D2k"/>
-                            <constraint firstItem="LmB-wv-Ztp" firstAttribute="bottom" secondItem="A2w-65-QSF" secondAttribute="top" constant="8" symbolic="YES" type="user" id="cGe-Ok-mYU"/>
                             <constraint firstAttribute="trailing" secondItem="LmB-wv-Ztp" secondAttribute="trailing" constant="20" symbolic="YES" type="user" id="ddw-6a-Ccz"/>
+                            <constraint firstItem="A2w-65-QSF" firstAttribute="bottom" secondItem="zgN-eK-M4Q" secondAttribute="bottom" type="default" id="e3s-lP-iPh"/>
                             <constraint firstAttribute="trailing" secondItem="nA3-W2-kn7" secondAttribute="trailing" type="user" id="lOJ-ew-ZyI"/>
                             <constraint firstItem="nA3-W2-kn7" firstAttribute="top" secondItem="zgN-eK-M4Q" secondAttribute="top" type="user" id="lUb-ik-h6u"/>
+                            <constraint firstItem="4Ns-t9-gs7" firstAttribute="leading" secondItem="zgN-eK-M4Q" secondAttribute="leading" type="default" id="ma4-AV-cQs"/>
                             <constraint firstItem="LmB-wv-Ztp" firstAttribute="leading" secondItem="zgN-eK-M4Q" secondAttribute="leading" constant="20" symbolic="YES" type="user" id="nfT-8Y-Tvw"/>
                             <constraint firstItem="nA3-W2-kn7" firstAttribute="leading" secondItem="zgN-eK-M4Q" secondAttribute="leading" type="user" id="p8G-QE-uZ8"/>
                         </constraints>
                         <outlet property="message_label" destination="LmB-wv-Ztp" id="YqJ-GW-DBG"/>
                         <outlet property="pause_button" destination="nH5-s3-C0i" id="VWV-EW-jB6"/>
                         <outlet property="play_button" destination="8Yb-MS-rAF" id="5SI-l2-mAJ"/>
+                        <outlet property="time_label" destination="R6T-PH-VPd" id="bGs-Zr-rv3"/>
+                        <outlet property="time_slider" destination="4Ns-t9-gs7" id="9Ne-1N-clc"/>
                         <outlet property="video_container_view" destination="nA3-W2-kn7" id="lEY-hP-YHD"/>
                         <outlet property="video_height_constraint" destination="5z2-ux-czd" id="9R7-fg-G0e"/>
                         <outlet property="video_view" destination="WHW-sv-bWc" id="c6W-td-a74"/>
                 <relationship kind="outlet" name="message_label" candidateClass="UILabel"/>
                 <relationship kind="outlet" name="pause_button" candidateClass="UIBarButtonItem"/>
                 <relationship kind="outlet" name="play_button" candidateClass="UIBarButtonItem"/>
+                <relationship kind="outlet" name="time_label" candidateClass="UITextField"/>
+                <relationship kind="outlet" name="time_slider" candidateClass="UISlider"/>
+                <relationship kind="outlet" name="toolbar" candidateClass="UIToolbar"/>
                 <relationship kind="outlet" name="video_container_view" candidateClass="UIView"/>
                 <relationship kind="outlet" name="video_height_constraint" candidateClass="NSLayoutConstraint"/>
                 <relationship kind="outlet" name="video_view" candidateClass="UIView"/>