Add slider and text widgets that track the current position and duration. The slider is not seekable yet.

This commit is contained in:
Xavi Artigas 2013-05-16 17:39:15 +02:00
parent eeb4755a3b
commit 079ad1bf15
6 changed files with 133 additions and 31 deletions

View 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);

View 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

View 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;

View file

@ -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

View 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"/>
@ -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"/>

View file

@ -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"/>