diff options
author | Xavi Artigas <xartigas@fluendo.com> | 2013-05-16 17:39:15 +0200 |
---|---|---|
committer | Xavi Artigas <xartigas@fluendo.com> | 2013-05-16 17:39:15 +0200 |
commit | 079ad1bf151a8578b81ac1f916e206ef37f286e4 (patch) | |
tree | 0751f59c54ee1006a9b08b0f6b99cef77c33dfe2 | |
parent | eeb4755a3bfbb4998e8ec5060b70ab60fd4ba80f (diff) |
Add slider and text widgets that track the current position and duration. The slider is not seekable yet.
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"/> |