summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavi Artigas <xartigas@fluendo.com>2013-05-16 17:39:15 +0200
committerXavi Artigas <xartigas@fluendo.com>2013-05-16 17:39:15 +0200
commit079ad1bf151a8578b81ac1f916e206ef37f286e4 (patch)
tree0751f59c54ee1006a9b08b0f6b99cef77c33dfe2
parenteeb4755a3bfbb4998e8ec5060b70ab60fd4ba80f (diff)
Add slider and text widgets that track the current position and duration. The slider is not seekable yet.
-rw-r--r--gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackend.m46
-rw-r--r--gst-sdk/tutorials/xcode iOS/Tutorial 4/GStreamerBackendDelegate.h3
-rw-r--r--gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.h3
-rw-r--r--gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.m44
-rw-r--r--gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPad.storyboard45
-rw-r--r--gst-sdk/tutorials/xcode iOS/Tutorial 4/en.lproj/MainStoryboard_iPhone.storyboard23
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 60758cc..f789edc 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 739461c..01981fa 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 37064f1..fe3d0ff 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 6b4ae04..57e224c 100644
--- a/gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.m
+++ b/gst-sdk/tutorials/xcode iOS/Tutorial 4/VideoViewController.m
@@ -15,6 +15,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 c1b9cf2..e12bdb7 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 @@
<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"/>
@@ -68,7 +67,21 @@
<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>
@@ -92,6 +105,9 @@
<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"/>
@@ -154,31 +170,6 @@
<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"/>
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 58347d2..5d9108b 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 @@
<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>
@@ -94,6 +106,8 @@
<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"/>
@@ -178,6 +192,9 @@
<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"/>