mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 18:51:11 +00:00
Add slider and text widgets that track the current position and duration. The slider is not seekable yet.
This commit is contained in:
parent
eeb4755a3b
commit
079ad1bf15
6 changed files with 133 additions and 31 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
Loading…
Reference in a new issue