[rygel] core,plugins: Add gst-based MediaRenderer
- From: Zeeshan Ali Khattak <zeeshanak src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [rygel] core,plugins: Add gst-based MediaRenderer
- Date: Fri, 23 Oct 2009 17:13:16 +0000 (UTC)
commit a1bbaa24fa4925f885dbfd44e921ce4c9e00fbbd
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date: Tue Jun 16 22:33:32 2009 +0300
core,plugins: Add gst-based MediaRenderer
This is mostly code stolen (and heavily addapted) from
gupnp-media-renderer and libowl-av.
configure.ac | 1 +
data/xml/AVTransport2.xml | 788 ++++++++++
data/xml/Makefile.am | 7 +-
data/xml/MediaRenderer2.xml | 29 +
data/xml/RenderingControl2.xml | 953 +++++++++++++
src/plugins/Makefile.am | 7 +-
src/plugins/gst-renderer/Makefile.am | 55 +
src/plugins/gst-renderer/owl-video-widget.c | 1503 ++++++++++++++++++++
src/plugins/gst-renderer/owl-video-widget.h | 128 ++
src/plugins/gst-renderer/owl-video-widget.vapi | 31 +
.../gst-renderer/rygel-gst-av-transport.vala | 415 ++++++
src/plugins/gst-renderer/rygel-gst-changelog.vala | 92 ++
.../gst-renderer/rygel-gst-connection-manager.vala | 98 ++
src/plugins/gst-renderer/rygel-gst-plugin.vala | 53 +
.../gst-renderer/rygel-gst-rendering-control.vala | 251 ++++
.../gst-renderer/rygel-gst-video-window.vala | 197 +++
16 files changed, 4604 insertions(+), 4 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index f09425c..1d4c7b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -239,6 +239,7 @@ src/plugins/external/Makefile
src/plugins/gst-launch/Makefile
src/plugins/mediathek/Makefile
src/plugins/tracker/Makefile
+src/plugins/gst-renderer/Makefile
src/plugins/test/Makefile
data/Makefile
data/xml/Makefile
diff --git a/data/xml/AVTransport2.xml b/data/xml/AVTransport2.xml
new file mode 100644
index 0000000..902a775
--- /dev/null
+++ b/data/xml/AVTransport2.xml
@@ -0,0 +1,788 @@
+<!--============================================================
+Title: UPnP AV AV-Transport Service (AVT) Template
+
+Purpose:
+To identify the required/optional actions and state variables
+and the required allowed values defined by this service type.
+
+Note:
+This file uses tabs (not spaces) for block indentation.
+Any updates to this file should maintain this convention.
+This includes disabling any automatic tab-to-space conversion
+feature provided by your editor.
+================================================================-->
+<scpd>
+ <serviceStateTable>
+ <stateVariable>
+ <name>TransportState</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>STOPPED</allowedValue>
+ <allowedValue>PLAYING</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+
+ <stateVariable>
+ <name>TransportStatus</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>OK</allowedValue>
+ <allowedValue>ERROR_OCCURRED</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+
+ <stateVariable>
+ <name>CurrentMediaCategory</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>NO_MEDIA</allowedValue>
+ <allowedValue>TRACK_AWARE</allowedValue>
+ <allowedValue>TRACK_UNAWARE</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+
+ <stateVariable>
+ <name>PlaybackStorageMedium</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>RecordStorageMedium</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>PossiblePlaybackStorageMedia</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>PossibleRecordStorageMedia</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>CurrentPlayMode</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>NORMAL</allowedValue>
+ </allowedValueList>
+ <defaultValue>NORMAL</defaultValue>
+ </stateVariable>
+
+ <stateVariable>
+ <name>TransportPlaySpeed</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>1</allowedValue>
+ </allowedValueList>
+ <defaultValue>1</defaultValue>
+ </stateVariable>
+
+ <stateVariable>
+ <name>RecordMediumWriteStatus</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>CurrentRecordQualityMode</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>PossibleRecordQualityModes</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>NumberOfTracks</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui4</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <name>CurrentTrack</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui4</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <name>CurrentTrackDuration</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>CurrentMediaDuration</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>CurrentTrackMetaData</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>CurrentTrackURI</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>AVTransportURI</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>AVTransportURIMetaData</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>NextAVTransportURI</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>NextAVTransportURIMetaData</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>RelativeTimePosition</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>AbsoluteTimePosition</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>RelativeCounterPosition</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>i4</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>AbsoluteCounterPosition</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>i4</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>CurrentTransportActions</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>LastChange</name>
+ <sendEventsAttribute>yes</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>DRMState</name>
+ <sendEventsAttribute>yes</sendEventsAttribute>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>OK</allowedValue>
+ </allowedValueList>
+ <defaultValue>UNKNOWN</defaultValue>
+ </stateVariable>
+
+ <stateVariable>
+ <name>A_ARG_TYPE_SeekMode</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>TRACK_NR</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+
+ <stateVariable>
+ <name>A_ARG_TYPE_SeekTarget</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>A_ARG_TYPE_InstanceID</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui4</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_DeviceUDN</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_ServiceType</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_ServiceID</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_StateVariableValuePairs</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_StateVariableList</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+ </serviceStateTable>
+
+ <actionList>
+ <action>
+ <name>SetAVTransportURI</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentURI</name>
+ <direction>in</direction>
+ <relatedStateVariable>AVTransportURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentURIMetaData</name>
+ <direction>in</direction>
+ <relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetNextAVTransportURI</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NextURI</name>
+ <direction>in</direction>
+ <relatedStateVariable>NextAVTransportURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NextURIMetaData</name>
+ <direction>in</direction>
+ <relatedStateVariable>NextAVTransportURIMetaData</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>GetMediaInfo</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NrTracks</name>
+ <direction>out</direction>
+ <relatedStateVariable>NumberOfTracks</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>MediaDuration</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentMediaDuration</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentURI</name>
+ <direction>out</direction>
+ <relatedStateVariable>AVTransportURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentURIMetaData</name>
+ <direction>out</direction>
+ <relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NextURI</name>
+ <direction>out</direction>
+ <relatedStateVariable>NextAVTransportURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NextURIMetaData</name>
+ <direction>out</direction>
+ <relatedStateVariable>NextAVTransportURIMetaData</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PlayMedium</name>
+ <direction>out</direction>
+ <relatedStateVariable>PlaybackStorageMedium</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RecordMedium</name>
+ <direction>out</direction>
+ <relatedStateVariable>RecordStorageMedium</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>WriteStatus</name>
+ <direction>out</direction>
+ <relatedStateVariable>RecordMediumWriteStatus</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>GetMediaInfo_Ext</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentType</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentMediaCategory</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NrTracks</name>
+ <direction>out</direction>
+ <relatedStateVariable>NumberOfTracks</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>MediaDuration</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentMediaDuration</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentURI</name>
+ <direction>out</direction>
+ <relatedStateVariable>AVTransportURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentURIMetaData</name>
+ <direction>out</direction>
+ <relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NextURI</name>
+ <direction>out</direction>
+ <relatedStateVariable>NextAVTransportURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NextURIMetaData</name>
+ <direction>out</direction>
+ <relatedStateVariable>NextAVTransportURIMetaData</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PlayMedium</name>
+ <direction>out</direction>
+ <relatedStateVariable>PlaybackStorageMedium</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RecordMedium</name>
+ <direction>out</direction>
+ <relatedStateVariable>RecordStorageMedium</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>WriteStatus</name>
+ <direction>out</direction>
+ <relatedStateVariable>RecordMediumWriteStatus</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>GetTransportInfo</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentTransportState</name>
+ <direction>out</direction>
+ <relatedStateVariable>TransportState</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentTransportStatus</name>
+ <direction>out</direction>
+ <relatedStateVariable>TransportStatus</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentSpeed</name>
+ <direction>out</direction>
+ <relatedStateVariable>TransportPlaySpeed</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>GetPositionInfo</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Track</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentTrack</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>TrackDuration</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentTrackDuration</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>TrackMetaData</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentTrackMetaData</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>TrackURI</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentTrackURI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RelTime</name>
+ <direction>out</direction>
+ <relatedStateVariable>RelativeTimePosition</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>AbsTime</name>
+ <direction>out</direction>
+ <relatedStateVariable>AbsoluteTimePosition</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RelCount</name>
+ <direction>out</direction>
+ <relatedStateVariable>RelativeCounterPosition</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>AbsCount</name>
+ <direction>out</direction>
+ <relatedStateVariable>AbsoluteCounterPosition</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>GetDeviceCapabilities</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PlayMedia</name>
+ <direction>out</direction>
+ <relatedStateVariable>PossiblePlaybackStorageMedia</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RecMedia</name>
+ <direction>out</direction>
+ <relatedStateVariable>PossibleRecordStorageMedia</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RecQualityModes</name>
+ <direction>out</direction>
+ <relatedStateVariable>PossibleRecordQualityModes</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>GetTransportSettings</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PlayMode</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentPlayMode</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RecQualityMode</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentRecordQualityMode</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>Stop</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>Play</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Speed</name>
+ <direction>in</direction>
+ <relatedStateVariable>TransportPlaySpeed</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>Pause</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>Record</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>Seek</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Unit</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_SeekMode</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Target</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_SeekTarget</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>Next</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>Previous</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetPlayMode</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NewPlayMode</name>
+ <direction>in</direction>
+ <relatedStateVariable>CurrentPlayMode</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetRecordQualityMode</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>NewRecordQualityMode</name>
+ <direction>in</direction>
+ <relatedStateVariable>CurrentRecordQualityMode</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetCurrentTransportActions</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Actions</name>
+ <direction>out</direction>
+ <relatedStateVariable>CurrentTransportActions</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetDRMState</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentDRMState</name>
+ <direction>out</direction>
+ <relatedStateVariable>DRMState</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetStateVariables</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>StateVariableList</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_StateVariableList</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>StateVariableValuePairs</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_StateVariableValuePairs</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetStateVariables</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>AVTransportUDN</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_DeviceUDN</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ServiceType</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ServiceType</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ServiceId</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ServiceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>StateVariableValuePairs</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_StateVariableValuePairs</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>StateVariableList</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_StateVariableList</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ </actionList>
+</scpd>
+
diff --git a/data/xml/Makefile.am b/data/xml/Makefile.am
index 3c3d864..1883aee 100644
--- a/data/xml/Makefile.am
+++ b/data/xml/Makefile.am
@@ -1,11 +1,12 @@
xml_DATA = MediaServer2.xml \
+ MediaRenderer2.xml \
ContentDirectory.xml \
- ConnectionManager.xml
+ ConnectionManager.xml \
+ AVTransport2.xml \
+ RenderingControl2.xml
xmldir = $(datadir)/rygel/xml
EXTRA_DIST = $(xml_DATA)
MAINTAINERCLEANFILES = Makefile.in
-
-
diff --git a/data/xml/MediaRenderer2.xml b/data/xml/MediaRenderer2.xml
new file mode 100644
index 0000000..7eb9d12
--- /dev/null
+++ b/data/xml/MediaRenderer2.xml
@@ -0,0 +1,29 @@
+<!--============================================================
+Title: UPnP AV MediaRenderer:2 Device Template
+
+Purpose:
+To identify the required and optional services
+defined by this device type.
+
+NOTE: This file uses tabs (not spaces) for block indentation.
+Any updates to this file should maintain this convention.
+This includes disabling any automatic tab-to-space conversion
+feature enabled by your editor.
+============================================================-->
+<device>
+ <serviceList>
+ <service>
+ <serviceType>urn:schemas-upnp-org:service:RenderingControl:2</serviceType>
+ <serviceId>RenderingControl</serviceId>
+ </service>
+ <service>
+ <serviceType>urn:schemas-upnp-org:service:ConnectionManager:2</serviceType>
+ <serviceId>ConnectionManager</serviceId>
+ </service>
+ <service>
+ <Optional/>
+ <serviceType>urn:schemas-upnp-org:service:AVTransport:2</serviceType>
+ <serviceId>AVTransport</serviceId>
+ </service>
+ </serviceList>
+</device>
\ No newline at end of file
diff --git a/data/xml/RenderingControl2.xml b/data/xml/RenderingControl2.xml
new file mode 100644
index 0000000..2918f8a
--- /dev/null
+++ b/data/xml/RenderingControl2.xml
@@ -0,0 +1,953 @@
+<!--============================================================
+Title: UPnP AV Rendering Control Service (RCS) Template
+
+Purpose:
+To identify the required/optional actions and state variables
+and the required allowed values defined by this service type.
+
+Note:
+This file uses tabs (not spaces) for block indentation.
+Any updates to this file should maintain this convention.
+This includes disabling any automatic tab-to-space conversion
+feature provided by your editor.
+================================================================-->
+<scpd>
+ <serviceStateTable>
+ <stateVariable>
+ <name>LastChange</name>
+ <sendEventsAttribute>yes</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>PresetNameList</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>Brightness</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>Contrast</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>Sharpness</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>RedVideoGain</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>GreenVideoGain</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>BlueVideoGain</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>RedVideoBlackLevel</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>GreenVideoBlackLevel</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>BlueVideoBlackLevel</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>ColorTemperature</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>HorizontalKeystone</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>i2</dataType>
+ <allowedValueRange>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>VerticalKeystone</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>i2</dataType>
+ <allowedValueRange>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>Mute</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>boolean</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>Volume</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui2</dataType>
+ <allowedValueRange>
+ <minimum>0</minimum>
+ <step>1</step>
+ </allowedValueRange>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>VolumeDB</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>i2</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>Loudness</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>boolean</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_Channel</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>Master</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+
+ <stateVariable>
+ <name>A_ARG_TYPE_InstanceID</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>ui4</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <name>A_ARG_TYPE_PresetName</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ <allowedValueList>
+ <allowedValue>FactoryDefaults</allowedValue>
+ </allowedValueList>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_DeviceUDN</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_ServiceType</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_ServiceID</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_StateVariableValuePairs</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable>
+ <Optional/>
+ <name>A_ARG_TYPE_StateVariableList</name>
+ <sendEventsAttribute>no</sendEventsAttribute>
+ <dataType>string</dataType>
+ </stateVariable>
+ </serviceStateTable>
+
+ <actionList>
+ <action>
+ <name>ListPresets</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentPresetNameList</name>
+ <direction>out</direction>
+ <relatedStateVariable>PresetNameList</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>SelectPreset</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>PresetName</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_PresetName</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetBrightness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentBrightness</name>
+ <direction>out</direction>
+ <relatedStateVariable>Brightness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetBrightness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredBrightness</name>
+ <direction>in</direction>
+ <relatedStateVariable>Brightness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetContrast</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentContrast</name>
+ <direction>out</direction>
+ <relatedStateVariable>Contrast</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action><Optional/>
+ <name>SetContrast</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredContrast</name>
+ <direction>in</direction>
+ <relatedStateVariable>Contrast</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetSharpness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentSharpness</name>
+ <direction>out</direction>
+ <relatedStateVariable>Sharpness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetSharpness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredSharpness</name>
+ <direction>in</direction>
+ <relatedStateVariable>Sharpness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetRedVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentRedVideoGain</name>
+ <direction>out</direction>
+ <relatedStateVariable>RedVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetRedVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredRedVideoGain</name>
+ <direction>in</direction>
+ <relatedStateVariable>RedVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetGreenVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentGreenVideoGain</name>
+ <direction>out</direction>
+ <relatedStateVariable>GreenVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetGreenVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredGreenVideoGain</name>
+ <direction>in</direction>
+ <relatedStateVariable>GreenVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetBlueVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentBlueVideoGain</name>
+ <direction>out</direction>
+ <relatedStateVariable>BlueVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetBlueVideoGain</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredBlueVideoGain</name>
+ <direction>in</direction>
+ <relatedStateVariable>BlueVideoGain</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetRedVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentRedVideoBlackLevel</name>
+ <direction>out</direction>
+ <relatedStateVariable>RedVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetRedVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredRedVideoBlackLevel</name>
+ <direction>in</direction>
+ <relatedStateVariable>RedVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetGreenVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentGreenVideoBlackLevel</name>
+ <direction>out</direction>
+ <relatedStateVariable>GreenVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetGreenVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredGreenVideoBlackLevel</name>
+ <direction>in</direction>
+ <relatedStateVariable>GreenVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetBlueVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentBlueVideoBlackLevel</name>
+ <direction>out</direction>
+ <relatedStateVariable>BlueVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetBlueVideoBlackLevel</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredBlueVideoBlackLevel</name>
+ <direction>in</direction>
+ <relatedStateVariable>BlueVideoBlackLevel</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetColorTemperature</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentColorTemperature</name>
+ <direction>out</direction>
+ <relatedStateVariable>ColorTemperature</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetColorTemperature</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredColorTemperature</name>
+ <direction>in</direction>
+ <relatedStateVariable>ColorTemperature</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetHorizontalKeystone</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentHorizontalKeystone</name>
+ <direction>out</direction>
+ <relatedStateVariable>HorizontalKeystone</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetHorizontalKeystone</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredHorizontalKeystone</name>
+ <direction>in</direction>
+ <relatedStateVariable>HorizontalKeystone</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetVerticalKeystone</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentVerticalKeystone</name>
+ <direction>out</direction>
+ <relatedStateVariable>VerticalKeystone</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetVerticalKeystone</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredVerticalKeystone</name>
+ <direction>in</direction>
+ <relatedStateVariable>VerticalKeystone</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetMute</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentMute</name>
+ <direction>out</direction>
+ <relatedStateVariable>Mute</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetMute</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredMute</name>
+ <direction>in</direction>
+ <relatedStateVariable>Mute</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetVolume</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentVolume</name>
+ <direction>out</direction>
+ <relatedStateVariable>Volume</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetVolume</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredVolume</name>
+ <direction>in</direction>
+ <relatedStateVariable>Volume</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetVolumeDB</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentVolume</name>
+ <direction>out</direction>
+ <relatedStateVariable>VolumeDB</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetVolumeDB</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredVolume</name>
+ <direction>in</direction>
+ <relatedStateVariable>VolumeDB</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetVolumeDBRange</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>MinValue</name>
+ <direction>out</direction>
+ <relatedStateVariable>VolumeDB</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>MaxValue</name>
+ <direction>out</direction>
+ <relatedStateVariable>VolumeDB</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetLoudness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>CurrentLoudness</name>
+ <direction>out</direction>
+ <relatedStateVariable>Loudness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetLoudness</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Channel</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DesiredLoudness</name>
+ <direction>in</direction>
+ <relatedStateVariable>Loudness</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>GetStateVariables</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>StateVariableList</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_StateVariableList</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>StateVariableValuePairs</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_StateVariableValuePairs</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <Optional/>
+ <name>SetStateVariables</name>
+ <argumentList>
+ <argument>
+ <name>InstanceID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>RenderingControlUDN</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_DeviceUDN</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ServiceType</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ServiceType</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ServiceId</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ServiceID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>StateVariableValuePairs</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_StateVariableValuePairs</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>StateVariableList</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_StateVariableList</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ </actionList>
+</scpd>
\ No newline at end of file
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index e7affd8..4472416 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -22,11 +22,16 @@ if BUILD_GST_LAUNCH_PLUGIN
GST_LAUNCH_PLUGIN = gst-launch
endif
+if BUILD_UI
+GST_RENDERER = gst-renderer
+endif
+
SUBDIRS = $(TEST_PLUGIN) \
$(TRACKER_PLUGIN) \
$(MEDIATHEK_PLUGIN) \
$(MEDIA_EXPORT_PLUGIN) \
$(EXTERNAL_PLUGIN) \
- $(GST_LAUNCH_PLUGIN)
+ $(GST_LAUNCH_PLUGIN) \
+ $(GST_RENDERER)
MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/plugins/gst-renderer/Makefile.am b/src/plugins/gst-renderer/Makefile.am
new file mode 100644
index 0000000..79f0594
--- /dev/null
+++ b/src/plugins/gst-renderer/Makefile.am
@@ -0,0 +1,55 @@
+plugindir = $(libdir)/rygel-1.0
+
+plugin_LTLIBRARIES = librygel-gst.la
+
+AM_CFLAGS = $(LIBGUPNP_CFLAGS) \
+ $(LIBGUPNP_AV_CFLAGS) \
+ $(GEE_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(LIBGSTREAMER_CFLAGS) \
+ $(LIBGCONF_CFLAGS) \
+ -I$(top_srcdir)/src/rygel -DDATA_DIR='"$(datadir)"'
+
+BUILT_SOURCES = rygel-gst-connection-manager.c \
+ rygel-gst-rendering-control.c \
+ rygel-gst-av-transport.c \
+ rygel-gst-video-window.c \
+ rygel-gst-changelog.c \
+ rygel-gst-plugin.c
+
+$(BUILT_SOURCES) : rygel-gst.stamp
+
+librygel_gst_la_SOURCES = rygel-gst-connection-manager.c \
+ rygel-gst-connection-manager.vala \
+ rygel-gst-rendering-control.c \
+ rygel-gst-rendering-control.vala \
+ rygel-gst-av-transport.c \
+ rygel-gst-av-transport.vala \
+ rygel-gst-video-window.c \
+ rygel-gst-video-window.vala \
+ rygel-gst-changelog.c \
+ rygel-gst-changelog.vala \
+ rygel-gst-plugin.c \
+ rygel-gst-plugin.vala \
+ owl-video-widget.c \
+ owl-video-widget.h
+
+rygel-gst.stamp: $(filter %.vala,$(librygel_gst_la_SOURCES))
+ $(VALAC) -C --vapidir=$(srcdir) --vapidir=$(top_srcdir)/src/rygel \
+ --pkg rygel-1.0 --pkg cstuff --pkg gupnp-1.0 --pkg gupnp-av-1.0 \
+ --pkg owl-video-widget --pkg gee-1.0 --pkg gstreamer-0.10 \
+ --pkg gconf-2.0 --pkg gtk+-2.0 \
+ $^
+ touch $@
+
+librygel_gst_la_LIBADD = $(LIBGUPNP_LIBS) \
+ $(LIBGUPNP_AV_LIBS) \
+ $(LIBGSTREAMER_LIBS) \
+ $(GEE_LIBS) \
+ $(GTK_LIBS) \
+ $(LIBGCONF_LIBS)
+librygel_gst_la_LDFLAGS = -shared -fPIC -module -avoid-version
+
+CLEANFILES = $(BUILT_SOURCES) rygel-gst.stamp
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST = $(BUILT_SOURCES) rygel-gst.stamp owl-video-widget.vapi
diff --git a/src/plugins/gst-renderer/owl-video-widget.c b/src/plugins/gst-renderer/owl-video-widget.c
new file mode 100644
index 0000000..e200155
--- /dev/null
+++ b/src/plugins/gst-renderer/owl-video-widget.c
@@ -0,0 +1,1503 @@
+/*
+ * Copyright (C) 2006, 2008 OpenedHand Ltd.
+ *
+ * OpenedHand Widget Library Video Widget - A GStreamer video GTK+ widget
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ */
+
+#include <gdk/gdkx.h>
+#include <gst/gst.h>
+#include <gst/interfaces/xoverlay.h>
+
+#include "owl-video-widget.h"
+
+/** TODO
+ * o Possibly implement colour balance properties.
+ * xvimagesink supports the following, on a range -1000 - 1000:
+ * - contrast
+ * - brightness
+ * - hue
+ * - saturation
+ **/
+
+G_DEFINE_TYPE (OwlVideoWidget,
+ owl_video_widget,
+ GTK_TYPE_BIN);
+
+struct _OwlVideoWidgetPrivate {
+ GstElement *playbin;
+ GstXOverlay *overlay;
+
+ GMutex *overlay_lock;
+
+ GdkWindow *dummy_window;
+
+ char *uri;
+
+ gboolean can_seek;
+
+ int buffer_percent;
+
+ int duration;
+
+ gboolean force_aspect_ratio;
+
+ guint tick_timeout_id;
+};
+
+enum {
+ PROP_0,
+ PROP_URI,
+ PROP_PLAYING,
+ PROP_POSITION,
+ PROP_VOLUME,
+ PROP_CAN_SEEK,
+ PROP_BUFFER_PERCENT,
+ PROP_DURATION,
+ PROP_FORCE_ASPECT_RATIO
+};
+
+enum {
+ TAG_LIST_AVAILABLE,
+ EOS,
+ ERROR,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+#define TICK_TIMEOUT 0.5
+
+/* TODO: Possibly retrieve these through introspection. The problem is that we
+ * need them in class_init already. */
+#define GST_VOL_DEFAULT 1.0
+#define GST_VOL_MAX 4.0
+
+/**
+ * Synchronise the force-aspect-ratio property with the videosink.
+ **/
+static void
+sync_force_aspect_ratio (OwlVideoWidget *video_widget)
+{
+ GObjectClass *class;
+
+ class = G_OBJECT_GET_CLASS (video_widget->priv->overlay);
+
+ if (!g_object_class_find_property (class, "force-aspect-ratio")) {
+ g_warning ("Unable to find 'force-aspect-ratio' "
+ "property.");
+
+ return;
+ }
+
+ g_object_set (video_widget->priv->overlay,
+ "force-aspect-ratio",
+ video_widget->priv->force_aspect_ratio,
+ NULL);
+}
+
+/**
+ * Ensures the existance of a dummy window and returns its XID.
+ **/
+static XID
+create_dummy_window (OwlVideoWidget *video_widget)
+{
+ GdkWindowAttr attributes;
+
+ if (video_widget->priv->dummy_window)
+ return GDK_WINDOW_XID (video_widget->priv->dummy_window);
+
+ attributes.width = 0;
+ attributes.height = 0;
+ attributes.window_type = GDK_WINDOW_TOPLEVEL;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.event_mask = 0;
+
+ video_widget->priv->dummy_window = gdk_window_new (NULL,
+ &attributes,
+ 0);
+
+ /**
+ * Sync, so that the window is definetely there when the videosink
+ * starts looking at it.
+ **/
+ XSync (GDK_WINDOW_XDISPLAY (video_widget->priv->dummy_window), FALSE);
+
+ return GDK_WINDOW_XID (video_widget->priv->dummy_window);
+}
+
+/**
+ * Destroys the dummy window, if any.
+ **/
+static void
+destroy_dummy_window (OwlVideoWidget *video_widget)
+{
+ if (video_widget->priv->dummy_window) {
+ g_object_unref (video_widget->priv->dummy_window);
+ video_widget->priv->dummy_window = NULL;
+ }
+}
+
+/**
+ * A message arrived synchronously on the bus: See if the overlay becomes available.
+ **/
+static GstBusSyncReply
+bus_sync_handler_cb (GstBus *bus,
+ GstMessage *message,
+ OwlVideoWidget *video_widget)
+{
+
+ const GstStructure *str;
+ XID xid;
+
+ str = gst_message_get_structure (message);
+ if (!str)
+ return GST_BUS_PASS;
+
+ if (!gst_structure_has_name (str, "prepare-xwindow-id"))
+ return GST_BUS_PASS;
+
+ /**
+ * Lock.
+ **/
+ g_mutex_lock (video_widget->priv->overlay_lock);
+
+ gdk_threads_enter ();
+
+ /**
+ * Take in the new overlay.
+ **/
+ if (video_widget->priv->overlay) {
+ g_object_remove_weak_pointer
+ (G_OBJECT (video_widget->priv->overlay),
+ (gpointer) &video_widget->priv->overlay);
+ }
+
+ video_widget->priv->overlay = GST_X_OVERLAY (GST_MESSAGE_SRC (message));
+
+ g_object_add_weak_pointer (G_OBJECT (video_widget->priv->overlay),
+ (gpointer) &video_widget->priv->overlay);
+
+ g_object_set (video_widget->priv->overlay,
+ "handle-expose", FALSE,
+ NULL);
+
+ sync_force_aspect_ratio (video_widget);
+
+ /**
+ * Connect the new overlay to our window.
+ **/
+ if (GTK_WIDGET_REALIZED (video_widget))
+ xid = GDK_WINDOW_XID (GTK_WIDGET (video_widget)->window);
+ else
+ xid = create_dummy_window (video_widget);
+
+ gst_x_overlay_set_xwindow_id (video_widget->priv->overlay, xid);
+
+ /**
+ * And expose.
+ **/
+ if (GTK_WIDGET_REALIZED (video_widget))
+ gst_x_overlay_expose (video_widget->priv->overlay);
+
+ /**
+ * Unlock.
+ **/
+ gdk_threads_leave ();
+
+ g_mutex_unlock (video_widget->priv->overlay_lock);
+
+ /**
+ * Drop this message.
+ **/
+ gst_message_unref (message);
+
+ return GST_BUS_DROP;
+}
+
+/**
+ * An error occured.
+ **/
+static void
+bus_message_error_cb (GstBus *bus,
+ GstMessage *message,
+ OwlVideoWidget *video_widget)
+{
+ GError *error;
+
+ error = NULL;
+ gst_message_parse_error (message,
+ &error,
+ NULL);
+
+ g_signal_emit (video_widget,
+ signals[ERROR],
+ 0,
+ error);
+
+ g_error_free (error);
+}
+
+/**
+ * End of stream reached.
+ **/
+static void
+bus_message_eos_cb (GstBus *bus,
+ GstMessage *message,
+ OwlVideoWidget *video_widget)
+{
+ /**
+ * Make sure UI is in sync.
+ **/
+ g_object_notify (G_OBJECT (video_widget), "position");
+
+ /**
+ * Emit EOS signal.
+ **/
+ g_signal_emit (video_widget,
+ signals[EOS],
+ 0);
+}
+
+/**
+ * Tag list available.
+ **/
+static void
+bus_message_tag_cb (GstBus *bus,
+ GstMessage *message,
+ OwlVideoWidget *video_widget)
+{
+ GstTagList *tag_list;
+
+ gst_message_parse_tag (message, &tag_list);
+
+ g_signal_emit (video_widget,
+ signals[TAG_LIST_AVAILABLE],
+ 0,
+ tag_list);
+
+ gst_tag_list_free (tag_list);
+}
+
+/**
+ * Buffering information available.
+ **/
+static void
+bus_message_buffering_cb (GstBus *bus,
+ GstMessage *message,
+ OwlVideoWidget *video_widget)
+{
+ const GstStructure *str;
+
+ str = gst_message_get_structure (message);
+ if (!str)
+ return;
+
+ if (!gst_structure_get_int (str,
+ "buffer-percent",
+ &video_widget->priv->buffer_percent))
+ return;
+
+ g_object_notify (G_OBJECT (video_widget), "buffer-percent");
+}
+
+/**
+ * Duration information available.
+ **/
+static void
+bus_message_duration_cb (GstBus *bus,
+ GstMessage *message,
+ OwlVideoWidget *video_widget)
+{
+ GstFormat format;
+ gint64 duration;
+
+ gst_message_parse_duration (message,
+ &format,
+ &duration);
+
+ if (format != GST_FORMAT_TIME)
+ return;
+
+ video_widget->priv->duration = duration / GST_SECOND;
+
+ g_object_notify (G_OBJECT (video_widget), "duration");
+}
+
+/**
+ * A state change occured.
+ **/
+static void
+bus_message_state_change_cb (GstBus *bus,
+ GstMessage *message,
+ OwlVideoWidget *video_widget)
+{
+ gpointer src;
+ GstState old_state, new_state;
+
+ src = GST_MESSAGE_SRC (message);
+
+ if (src != video_widget->priv->playbin)
+ return;
+
+ gst_message_parse_state_changed (message,
+ &old_state,
+ &new_state,
+ NULL);
+
+ if (old_state == GST_STATE_READY &&
+ new_state == GST_STATE_PAUSED) {
+ GstQuery *query;
+
+ /**
+ * Determine whether we can seek.
+ **/
+ query = gst_query_new_seeking (GST_FORMAT_TIME);
+
+ if (gst_element_query (video_widget->priv->playbin, query)) {
+ gst_query_parse_seeking (query,
+ NULL,
+ &video_widget->priv->can_seek,
+ NULL,
+ NULL);
+ } else {
+ /**
+ * Could not query for ability to seek. Assume
+ * seek is supported.
+ **/
+
+ video_widget->priv->can_seek = TRUE;
+ }
+
+ gst_query_unref (query);
+
+ g_object_notify (G_OBJECT (video_widget), "can-seek");
+
+ /**
+ * Determine the duration.
+ **/
+ query = gst_query_new_duration (GST_FORMAT_TIME);
+
+ if (gst_element_query (video_widget->priv->playbin, query)) {
+ gint64 duration;
+
+ gst_query_parse_duration (query,
+ NULL,
+ &duration);
+
+ video_widget->priv->duration = duration / GST_SECOND;
+
+ g_object_notify (G_OBJECT (video_widget), "duration");
+ }
+
+ gst_query_unref (query);
+ }
+}
+
+/**
+ * Called every TICK_TIMEOUT secs to notify of a position change.
+ **/
+static gboolean
+tick_timeout (OwlVideoWidget *video_widget)
+{
+ g_object_notify (G_OBJECT (video_widget), "position");
+
+ return TRUE;
+}
+
+/**
+ * Constructs the GStreamer pipeline.
+ **/
+static void
+construct_pipeline (OwlVideoWidget *video_widget)
+{
+
+ GstElement *videosink, *audiosink;
+ GstBus *bus;
+
+ /**
+ * playbin.
+ **/
+ video_widget->priv->playbin =
+ gst_element_factory_make ("playbin2", "playbin2");
+ if (!video_widget->priv->playbin) {
+ /* Try playbin if playbin2 isn't available */
+ video_widget->priv->playbin =
+ gst_element_factory_make ("playbin", "playbin");
+ }
+
+ if (!video_widget->priv->playbin) {
+ g_warning ("No playbin found. Playback will not work.");
+
+ return;
+ }
+
+ /**
+ * A videosink.
+ **/
+ videosink = gst_element_factory_make ("gconfvideosink", "videosink");
+ if (!videosink) {
+ g_warning ("No gconfvideosink found. Trying autovideosink ...");
+
+ videosink = gst_element_factory_make ("autovideosink",
+ "videosink");
+ if (!videosink) {
+ g_warning ("No autovideosink found. "
+ "Trying ximagesink ...");
+
+ videosink = gst_element_factory_make ("ximagesink",
+ "videosink");
+ if (!videosink) {
+ g_warning ("No videosink could be found. "
+ "Video will not be available.");
+ }
+ }
+ }
+
+ /**
+ * An audiosink.
+ **/
+ audiosink = gst_element_factory_make ("gconfaudiosink", "audiosink");
+ if (!audiosink) {
+ g_warning ("No gconfaudiosink found. Trying autoaudiosink ...");
+
+ audiosink = gst_element_factory_make ("autoaudiosink",
+ "audiosink");
+ if (!audiosink) {
+ g_warning ("No autoaudiosink found. "
+ "Trying alsasink ...");
+
+ audiosink = gst_element_factory_make ("alsasink",
+ "audiosink");
+ if (!audiosink) {
+ g_warning ("No audiosink could be found. "
+ "Audio will not be available.");
+ }
+ }
+ }
+
+ /**
+ * Click sinks into playbin.
+ **/
+ g_object_set (G_OBJECT (video_widget->priv->playbin),
+ "video-sink", videosink,
+ "audio-sink", audiosink,
+ NULL);
+
+ /**
+ * Connect to signals on bus.
+ **/
+ bus = gst_pipeline_get_bus (GST_PIPELINE (video_widget->priv->playbin));
+
+ gst_bus_add_signal_watch (bus);
+
+ gst_bus_set_sync_handler (bus,
+ (GstBusSyncHandler) bus_sync_handler_cb,
+ video_widget);
+
+ g_signal_connect_object (bus,
+ "message::error",
+ G_CALLBACK (bus_message_error_cb),
+ video_widget,
+ 0);
+ g_signal_connect_object (bus,
+ "message::eos",
+ G_CALLBACK (bus_message_eos_cb),
+ video_widget,
+ 0);
+ g_signal_connect_object (bus,
+ "message::tag",
+ G_CALLBACK (bus_message_tag_cb),
+ video_widget,
+ 0);
+ g_signal_connect_object (bus,
+ "message::buffering",
+ G_CALLBACK (bus_message_buffering_cb),
+ video_widget,
+ 0);
+ g_signal_connect_object (bus,
+ "message::duration",
+ G_CALLBACK (bus_message_duration_cb),
+ video_widget,
+ 0);
+
+ g_signal_connect_object (bus,
+ "message::state-changed",
+ G_CALLBACK (bus_message_state_change_cb),
+ video_widget,
+ 0);
+
+ gst_object_unref (GST_OBJECT (bus));
+}
+
+static void
+owl_video_widget_init (OwlVideoWidget *video_widget)
+{
+ /**
+ * We do have our own GdkWindow.
+ **/
+ GTK_WIDGET_UNSET_FLAGS (video_widget, GTK_NO_WINDOW);
+ GTK_WIDGET_UNSET_FLAGS (video_widget, GTK_DOUBLE_BUFFERED);
+
+ /**
+ * Create pointer to private data.
+ **/
+ video_widget->priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (video_widget,
+ OWL_TYPE_VIDEO_WIDGET,
+ OwlVideoWidgetPrivate);
+
+ /**
+ * Initialize defaults.
+ **/
+ video_widget->priv->force_aspect_ratio = TRUE;
+
+ /**
+ * Create lock.
+ **/
+ video_widget->priv->overlay_lock = g_mutex_new ();
+
+ /**
+ * Construct GStreamer pipeline: playbin with sinks from GConf.
+ **/
+ construct_pipeline (video_widget);
+}
+
+static void
+owl_video_widget_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ OwlVideoWidget *video_widget;
+
+ video_widget = OWL_VIDEO_WIDGET (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ owl_video_widget_set_uri (video_widget,
+ g_value_get_string (value));
+ break;
+ case PROP_PLAYING:
+ owl_video_widget_set_playing (video_widget,
+ g_value_get_boolean (value));
+ break;
+ case PROP_POSITION:
+ owl_video_widget_set_position (video_widget,
+ g_value_get_int (value));
+ break;
+ case PROP_VOLUME:
+ owl_video_widget_set_volume (video_widget,
+ g_value_get_double (value));
+ break;
+ case PROP_FORCE_ASPECT_RATIO:
+ owl_video_widget_set_force_aspect_ratio
+ (video_widget,
+ g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+owl_video_widget_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ OwlVideoWidget *video_widget;
+
+ video_widget = OWL_VIDEO_WIDGET (object);
+
+ switch (property_id) {
+ case PROP_URI:
+ g_value_set_string
+ (value,
+ owl_video_widget_get_uri (video_widget));
+ break;
+ case PROP_PLAYING:
+ g_value_set_boolean
+ (value,
+ owl_video_widget_get_playing (video_widget));
+ break;
+ case PROP_POSITION:
+ g_value_set_int
+ (value,
+ owl_video_widget_get_position (video_widget));
+ break;
+ case PROP_VOLUME:
+ g_value_set_double
+ (value,
+ owl_video_widget_get_volume (video_widget));
+ break;
+ case PROP_CAN_SEEK:
+ g_value_set_boolean
+ (value,
+ owl_video_widget_get_can_seek (video_widget));
+ break;
+ case PROP_BUFFER_PERCENT:
+ g_value_set_int
+ (value,
+ owl_video_widget_get_buffer_percent (video_widget));
+ break;
+ case PROP_DURATION:
+ g_value_set_int
+ (value,
+ owl_video_widget_get_duration (video_widget));
+ break;
+ case PROP_FORCE_ASPECT_RATIO:
+ g_value_set_boolean
+ (value,
+ owl_video_widget_get_force_aspect_ratio
+ (video_widget));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+owl_video_widget_dispose (GObject *object)
+{
+ OwlVideoWidget *video_widget;
+ GObjectClass *object_class;
+
+ video_widget = OWL_VIDEO_WIDGET (object);
+
+ if (video_widget->priv->playbin) {
+ gst_element_set_state (video_widget->priv->playbin,
+ GST_STATE_NULL);
+
+ gst_object_unref (GST_OBJECT (video_widget->priv->playbin));
+ video_widget->priv->playbin = NULL;
+ }
+
+ if (video_widget->priv->tick_timeout_id > 0) {
+ g_source_remove (video_widget->priv->tick_timeout_id);
+ video_widget->priv->tick_timeout_id = 0;
+ }
+
+ destroy_dummy_window (video_widget);
+
+ object_class = G_OBJECT_CLASS (owl_video_widget_parent_class);
+ object_class->dispose (object);
+}
+
+static void
+owl_video_widget_finalize (GObject *object)
+{
+ OwlVideoWidget *video_widget;
+ GObjectClass *object_class;
+
+ video_widget = OWL_VIDEO_WIDGET (object);
+
+ g_mutex_free (video_widget->priv->overlay_lock);
+
+ g_free (video_widget->priv->uri);
+
+ object_class = G_OBJECT_CLASS (owl_video_widget_parent_class);
+ object_class->finalize (object);
+}
+
+static void
+owl_video_widget_realize (GtkWidget *widget)
+{
+ OwlVideoWidget *video_widget;
+ GdkWindow *parent_window;
+ GdkWindowAttr attributes;
+ guint attributes_mask;
+ int border_width;
+
+ video_widget = OWL_VIDEO_WIDGET (widget);
+
+ /**
+ * Mark widget as realized.
+ **/
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+ /**
+ * Lock.
+ **/
+ g_mutex_lock (video_widget->priv->overlay_lock);
+
+ /**
+ * Create our GdkWindow.
+ **/
+ border_width = GTK_CONTAINER (widget)->border_width;
+
+ attributes.x = widget->allocation.x + border_width;
+ attributes.y = widget->allocation.y + border_width;
+ attributes.width = widget->allocation.width - border_width * 2;
+ attributes.height = widget->allocation.height - border_width * 2;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= GDK_EXPOSURE_MASK;
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ parent_window = gtk_widget_get_parent_window (widget);
+ widget->window = gdk_window_new (parent_window,
+ &attributes,
+ attributes_mask);
+ gdk_window_set_user_data (widget->window, widget);
+
+ gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
+
+ /**
+ * Sync, so that the window is definitely there when the videosink
+ * starts looking at it.
+ **/
+ XSync (GDK_WINDOW_XDISPLAY (widget->window), FALSE);
+
+ /**
+ * Connect overlay, if available, to window.
+ **/
+ if (video_widget->priv->overlay) {
+ XID xid;
+
+ xid = GDK_WINDOW_XID (widget->window);
+ gst_x_overlay_set_xwindow_id (video_widget->priv->overlay, xid);
+ gst_x_overlay_expose (video_widget->priv->overlay);
+
+ /**
+ * Destroy dummy window if it was there.
+ **/
+ destroy_dummy_window (video_widget);
+ }
+
+ /**
+ * Unlock.
+ **/
+ g_mutex_unlock (video_widget->priv->overlay_lock);
+
+ /**
+ * Attach GtkStyle.
+ **/
+ widget->style = gtk_style_attach (widget->style, widget->window);
+}
+
+static void
+owl_video_widget_unrealize (GtkWidget *widget)
+{
+ OwlVideoWidget *video_widget;
+ GtkWidgetClass *widget_class;
+
+ video_widget = OWL_VIDEO_WIDGET (widget);
+
+ /**
+ * Lock.
+ **/
+ g_mutex_lock (video_widget->priv->overlay_lock);
+
+ /**
+ * Connect overlay, if available, to hidden window.
+ **/
+ if (video_widget->priv->overlay) {
+ XID xid;
+
+ xid = create_dummy_window (video_widget);
+
+ gst_x_overlay_set_xwindow_id (video_widget->priv->overlay, xid);
+ }
+
+ /**
+ * Unlock.
+ **/
+ g_mutex_unlock (video_widget->priv->overlay_lock);
+
+ /**
+ * Call parent class.
+ **/
+ widget_class = GTK_WIDGET_CLASS (owl_video_widget_parent_class);
+ widget_class->unrealize (widget);
+}
+
+static gboolean
+owl_video_widget_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ OwlVideoWidget *video_widget;
+ GtkWidgetClass *widget_class;
+
+ /* Perform extra exposure compression */
+ if (event && event->count > 0)
+ return TRUE;
+
+ video_widget = OWL_VIDEO_WIDGET (widget);
+
+ /**
+ * Only draw if we are drawable.
+ **/
+ if (!GTK_WIDGET_DRAWABLE (widget))
+ return FALSE;
+
+ gdk_draw_rectangle (widget->window, widget->style->black_gc, TRUE,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ /**
+ * Lock.
+ **/
+ g_mutex_lock (video_widget->priv->overlay_lock);
+
+ /**
+ * If we have an overlay, forward the expose to GStreamer.
+ **/
+ if (video_widget->priv->overlay)
+ gst_x_overlay_expose (video_widget->priv->overlay);
+
+ /**
+ * Unlock.
+ **/
+ g_mutex_unlock (video_widget->priv->overlay_lock);
+
+ /**
+ * Call parent class.
+ **/
+ widget_class = GTK_WIDGET_CLASS (owl_video_widget_parent_class);
+ widget_class->expose_event (widget, event);
+
+ return TRUE;
+}
+
+static void
+owl_video_widget_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ int border_width;
+ GtkWidget *child;
+
+ border_width = GTK_CONTAINER (widget)->border_width;
+
+ /**
+ * Request width from child.
+ **/
+ child = GTK_BIN (widget)->child;
+ if (child && GTK_WIDGET_VISIBLE (child))
+ gtk_widget_size_request (child, requisition);
+
+ requisition->width += border_width * 2;
+ requisition->height += border_width * 2;
+}
+
+static void
+owl_video_widget_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ OwlVideoWidget *video_widget;
+ int border_width;
+ GtkAllocation child_allocation;
+ GtkWidget *child;
+
+ video_widget = OWL_VIDEO_WIDGET (widget);
+
+ /**
+ * Cache the allocation.
+ **/
+ widget->allocation = *allocation;
+
+ /**
+ * Calculate the size for our GdkWindow and for the child.
+ **/
+ border_width = GTK_CONTAINER (widget)->border_width;
+
+ child_allocation.x = allocation->x + border_width;
+ child_allocation.y = allocation->y + border_width;
+ child_allocation.width = allocation->width - border_width * 2;
+ child_allocation.height = allocation->height - border_width * 2;
+
+ /**
+ * Resize our GdkWindow.
+ **/
+ if (GTK_WIDGET_REALIZED (widget)) {
+ gdk_window_move_resize (widget->window,
+ child_allocation.x,
+ child_allocation.y,
+ child_allocation.width,
+ child_allocation.height);
+ }
+
+ /**
+ * Forward the size allocation to our child.
+ **/
+ child = GTK_BIN (widget)->child;
+ if (child && GTK_WIDGET_VISIBLE (child)) {
+ /**
+ * The child is positioned relative to its parent.
+ **/
+ child_allocation.x = 0;
+ child_allocation.y = 0;
+
+ gtk_widget_size_allocate (child, &child_allocation);
+ }
+}
+
+static void
+owl_video_widget_class_init (OwlVideoWidgetClass *klass)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = owl_video_widget_set_property;
+ object_class->get_property = owl_video_widget_get_property;
+ object_class->dispose = owl_video_widget_dispose;
+ object_class->finalize = owl_video_widget_finalize;
+
+ widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->realize = owl_video_widget_realize;
+ widget_class->unrealize = owl_video_widget_unrealize;
+ widget_class->expose_event = owl_video_widget_expose;
+ widget_class->size_request = owl_video_widget_size_request;
+ widget_class->size_allocate = owl_video_widget_size_allocate;
+
+ g_type_class_add_private (klass, sizeof (OwlVideoWidgetPrivate));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_URI,
+ g_param_spec_string
+ ("uri",
+ "URI",
+ "The loaded URI.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_PLAYING,
+ g_param_spec_boolean
+ ("playing",
+ "Playing",
+ "TRUE if playing.",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_POSITION,
+ g_param_spec_int
+ ("position",
+ "Position",
+ "The position in the current stream in seconds.",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_VOLUME,
+ g_param_spec_double
+ ("volume",
+ "Volume",
+ "The audio volume.",
+ 0, GST_VOL_MAX, GST_VOL_DEFAULT,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_CAN_SEEK,
+ g_param_spec_boolean
+ ("can-seek",
+ "Can seek",
+ "TRUE if the current stream is seekable.",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_BUFFER_PERCENT,
+ g_param_spec_int
+ ("buffer-percent",
+ "Buffer percent",
+ "The percentage the current stream buffer is filled.",
+ 0, 100, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_DURATION,
+ g_param_spec_int
+ ("duration",
+ "Duration",
+ "The duration of the current stream in seconds.",
+ 0, G_MAXINT, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property
+ (object_class,
+ PROP_FORCE_ASPECT_RATIO,
+ g_param_spec_boolean
+ ("force-aspect-ratio",
+ "Force aspect ratio",
+ "TRUE to force the image's aspect ratio to be "
+ "honoured.",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ signals[TAG_LIST_AVAILABLE] =
+ g_signal_new ("tag-list-available",
+ OWL_TYPE_VIDEO_WIDGET,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (OwlVideoWidgetClass,
+ tag_list_available),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ signals[EOS] =
+ g_signal_new ("eos",
+ OWL_TYPE_VIDEO_WIDGET,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (OwlVideoWidgetClass,
+ eos),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[ERROR] =
+ g_signal_new ("error",
+ OWL_TYPE_VIDEO_WIDGET,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (OwlVideoWidgetClass,
+ error),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
+
+/**
+ * owl_video_widget_new
+ *
+ * Return value: A new #OwlVideoWidget.
+ **/
+GtkWidget *
+owl_video_widget_new (void)
+{
+ return g_object_new (OWL_TYPE_VIDEO_WIDGET, NULL);
+}
+
+/**
+ * owl_video_widget_set_uri
+ * @video_widget: A #OwlVideoWidget
+ * @uri: A URI
+ *
+ * Loads @uri.
+ **/
+void
+owl_video_widget_set_uri (OwlVideoWidget *video_widget,
+ const char *uri)
+{
+ GstState state, pending;
+
+ g_return_if_fail (OWL_IS_VIDEO_WIDGET (video_widget));
+
+ if (!video_widget->priv->playbin)
+ return;
+
+ g_free (video_widget->priv->uri);
+
+ if (uri) {
+ video_widget->priv->uri = g_strdup (uri);
+
+ /**
+ * Ensure the tick timeout is installed.
+ *
+ * We also have it installed in PAUSED state, because
+ * seeks etc may have a delayed effect on the position.
+ **/
+ if (video_widget->priv->tick_timeout_id == 0) {
+ video_widget->priv->tick_timeout_id =
+ g_timeout_add (TICK_TIMEOUT * 1000,
+ (GSourceFunc) tick_timeout,
+ video_widget);
+ }
+ } else {
+ video_widget->priv->uri = NULL;
+
+ /**
+ * Remove tick timeout.
+ **/
+ if (video_widget->priv->tick_timeout_id > 0) {
+ g_source_remove (video_widget->priv->tick_timeout_id);
+ video_widget->priv->tick_timeout_id = 0;
+ }
+ }
+
+ /**
+ * Reset properties.
+ **/
+ video_widget->priv->can_seek = FALSE;
+ video_widget->priv->duration = 0;
+
+ /**
+ * Store old state.
+ **/
+ gst_element_get_state (video_widget->priv->playbin,
+ &state,
+ &pending,
+ 0);
+ if (pending)
+ state = pending;
+
+ /**
+ * State to NULL.
+ **/
+ gst_element_set_state (video_widget->priv->playbin, GST_STATE_NULL);
+
+ /**
+ * Set new URI.
+ **/
+ g_object_set (video_widget->priv->playbin,
+ "uri", uri,
+ NULL);
+
+ /**
+ * Restore state.
+ **/
+ if (uri)
+ gst_element_set_state (video_widget->priv->playbin, state);
+
+ /**
+ * Emit notififications for all these to make sure UI is not showing
+ * any properties of the old URI.
+ **/
+ g_object_notify (G_OBJECT (video_widget), "uri");
+ g_object_notify (G_OBJECT (video_widget), "can-seek");
+ g_object_notify (G_OBJECT (video_widget), "duration");
+ g_object_notify (G_OBJECT (video_widget), "position");
+}
+
+/**
+ * owl_video_widget_get_uri
+ * @video_widget: A #OwlVideoWidget
+ *
+ * Return value: The loaded URI, or NULL if none set.
+ **/
+const char *
+owl_video_widget_get_uri (OwlVideoWidget *video_widget)
+{
+ g_return_val_if_fail (OWL_IS_VIDEO_WIDGET (video_widget), NULL);
+
+ return video_widget->priv->uri;
+}
+
+/**
+ * owl_video_widget_set_playing
+ * @video_widget: A #OwlVideoWidget
+ * @playing: TRUE if @video_widget should be playing, FALSE otherwise
+ *
+ * Sets the playback state of @video_widget to @playing.
+ **/
+void
+owl_video_widget_set_playing (OwlVideoWidget *video_widget,
+ gboolean playing)
+{
+ g_return_if_fail (OWL_IS_VIDEO_WIDGET (video_widget));
+
+ if (!video_widget->priv->playbin)
+ return;
+
+ /**
+ * Choose the correct state for the pipeline.
+ **/
+ if (video_widget->priv->uri) {
+ GstState state;
+
+ if (playing)
+ state = GST_STATE_PLAYING;
+ else
+ state = GST_STATE_PAUSED;
+
+ gst_element_set_state (video_widget->priv->playbin, state);
+ } else {
+ if (playing)
+ g_warning ("Tried to play, but no URI is loaded.");
+
+ /**
+ * Do nothing.
+ **/
+ }
+
+ g_object_notify (G_OBJECT (video_widget), "playing");
+
+ /**
+ * Make sure UI is in sync.
+ **/
+ g_object_notify (G_OBJECT (video_widget), "position");
+}
+
+/**
+ * owl_video_widget_get_playing
+ * @video_widget: A #OwlVideoWidget
+ *
+ * Return value: TRUE if @video_widget is playing.
+ **/
+gboolean
+owl_video_widget_get_playing (OwlVideoWidget *video_widget)
+{
+ GstState state, pending;
+
+ g_return_val_if_fail (OWL_IS_VIDEO_WIDGET (video_widget), FALSE);
+
+ if (!video_widget->priv->playbin)
+ return FALSE;
+
+ gst_element_get_state (video_widget->priv->playbin,
+ &state,
+ &pending,
+ 0);
+
+ if (pending)
+ return (pending == GST_STATE_PLAYING);
+ else
+ return (state == GST_STATE_PLAYING);
+}
+
+/**
+ * owl_video_widget_set_position
+ * @video_widget: A #OwlVideoWidget
+ * @position: The position in the current stream in seconds.
+ *
+ * Sets the position in the current stream to @position.
+ **/
+void
+owl_video_widget_set_position (OwlVideoWidget *video_widget,
+ int position)
+{
+ GstState state, pending;
+
+ g_return_if_fail (OWL_IS_VIDEO_WIDGET (video_widget));
+
+ if (!video_widget->priv->playbin)
+ return;
+
+ /**
+ * Store old state.
+ **/
+ gst_element_get_state (video_widget->priv->playbin,
+ &state,
+ &pending,
+ 0);
+ if (pending)
+ state = pending;
+
+ /**
+ * State to PAUSED.
+ **/
+ gst_element_set_state (video_widget->priv->playbin, GST_STATE_PAUSED);
+
+ /**
+ * Perform the seek.
+ **/
+ gst_element_seek (video_widget->priv->playbin,
+ 1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
+ GST_SEEK_TYPE_SET, position * GST_SECOND,
+ GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+ /**
+ * Restore state.
+ **/
+ gst_element_set_state (video_widget->priv->playbin, state);
+}
+
+/**
+ * owl_video_widget_get_position
+ * @video_widget: A #OwlVideoWidget
+ *
+ * Return value: The position in the current file in seconds.
+ **/
+int
+owl_video_widget_get_position (OwlVideoWidget *video_widget)
+{
+ GstQuery *query;
+ gint64 position;
+
+ g_return_val_if_fail (OWL_IS_VIDEO_WIDGET (video_widget), -1);
+
+ if (!video_widget->priv->playbin)
+ return -1;
+
+ query = gst_query_new_position (GST_FORMAT_TIME);
+
+ if (gst_element_query (video_widget->priv->playbin, query)) {
+ gst_query_parse_position (query,
+ NULL,
+ &position);
+ } else
+ position = 0;
+
+ gst_query_unref (query);
+
+ return (position / GST_SECOND);
+}
+
+/**
+ * owl_video_widget_set_volume
+ * @video_widget: A #OwlVideoWidget
+ * @volume: The audio volume to set, in the range 0.0 - 4.0.
+ *
+ * Sets the current audio volume to @volume.
+ **/
+void
+owl_video_widget_set_volume (OwlVideoWidget *video_widget,
+ double volume)
+{
+ g_return_if_fail (OWL_IS_VIDEO_WIDGET (video_widget));
+ g_return_if_fail (volume >= 0.0 && volume <= GST_VOL_MAX);
+
+ if (!video_widget->priv->playbin)
+ return;
+
+ g_object_set (G_OBJECT (video_widget->priv->playbin),
+ "volume", volume,
+ NULL);
+
+ g_object_notify (G_OBJECT (video_widget), "volume");
+}
+
+/**
+ * owl_video_widget_get_volume
+ * @video_widget: A #OwlVideoWidget
+ *
+ * Return value: The current audio volume, in the range 0.0 - 4.0.
+ **/
+double
+owl_video_widget_get_volume (OwlVideoWidget *video_widget)
+{
+ double volume;
+
+ g_return_val_if_fail (OWL_IS_VIDEO_WIDGET (video_widget), 0);
+
+ if (!video_widget->priv->playbin)
+ return 0.0;
+
+ g_object_get (video_widget->priv->playbin,
+ "volume", &volume,
+ NULL);
+
+ return volume;
+}
+
+/**
+ * owl_video_widget_get_can_seek
+ * @video_widget: A #OwlVideoWidget
+ *
+ * Return value: TRUE if the current stream is seekable.
+ **/
+gboolean
+owl_video_widget_get_can_seek (OwlVideoWidget *video_widget)
+{
+ g_return_val_if_fail (OWL_IS_VIDEO_WIDGET (video_widget), FALSE);
+
+ return video_widget->priv->can_seek;
+}
+
+/**
+ * owl_video_widget_get_buffer_percent
+ * @video_widget: A #OwlVideoWidget
+ *
+ * Return value: Percentage the current stream buffer is filled.
+ **/
+int
+owl_video_widget_get_buffer_percent (OwlVideoWidget *video_widget)
+{
+ g_return_val_if_fail (OWL_IS_VIDEO_WIDGET (video_widget), -1);
+
+ return video_widget->priv->buffer_percent;
+}
+
+/**
+ * owl_video_widget_get_duration
+ * @video_widget: A #OwlVideoWidget
+ *
+ * Return value: The duration of the current stream in seconds.
+ **/
+int
+owl_video_widget_get_duration (OwlVideoWidget *video_widget)
+{
+ g_return_val_if_fail (OWL_IS_VIDEO_WIDGET (video_widget), -1);
+
+ return video_widget->priv->duration;
+}
+
+/**
+ * owl_video_widget_set_force_aspect_ratio
+ * @video_widget: A #OwlVideoWidget
+ * @force_aspect_ratio: TRUE to force the image's aspect ratio to be
+ * honoured.
+ *
+ * If @force_aspect_ratio is TRUE, sets the image's aspect ratio to be
+ * honoured.
+ **/
+void
+owl_video_widget_set_force_aspect_ratio (OwlVideoWidget *video_widget,
+ gboolean force_aspect_ratio)
+{
+ g_return_if_fail (OWL_IS_VIDEO_WIDGET (video_widget));
+
+ if (video_widget->priv->force_aspect_ratio == force_aspect_ratio)
+ return;
+
+ video_widget->priv->force_aspect_ratio = force_aspect_ratio;
+
+ g_mutex_lock (video_widget->priv->overlay_lock);
+
+ if (video_widget->priv->overlay)
+ sync_force_aspect_ratio (video_widget);
+
+ g_mutex_unlock (video_widget->priv->overlay_lock);
+
+ g_object_notify (G_OBJECT (video_widget), "force-aspect-ratio");
+}
+
+/**
+ * owl_video_widget_get_force_aspect_ratio
+ * @video_widget: A #OwlVideoWidget
+ *
+ * Return value: TRUE if the image's aspect ratio is being honoured.
+ **/
+gboolean
+owl_video_widget_get_force_aspect_ratio (OwlVideoWidget *video_widget)
+{
+ g_return_val_if_fail (OWL_IS_VIDEO_WIDGET (video_widget), FALSE);
+
+ return video_widget->priv->force_aspect_ratio;
+}
diff --git a/src/plugins/gst-renderer/owl-video-widget.h b/src/plugins/gst-renderer/owl-video-widget.h
new file mode 100644
index 0000000..7bb936f
--- /dev/null
+++ b/src/plugins/gst-renderer/owl-video-widget.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2006 OpenedHand Ltd.
+ *
+ * OpenedHand Widget Library Video Widget - A GStreamer video GTK+ widget
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ */
+
+#ifndef __OWL_VIDEO_WIDGET_H__
+#define __OWL_VIDEO_WIDGET_H__
+
+#include <gtk/gtkbin.h>
+#include <gst/gsttaglist.h>
+
+G_BEGIN_DECLS
+
+#define OWL_TYPE_VIDEO_WIDGET \
+ (owl_video_widget_get_type ())
+#define OWL_VIDEO_WIDGET(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ OWL_TYPE_VIDEO_WIDGET, \
+ OwlVideoWidget))
+#define OWL_VIDEO_WIDGET_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ OWL_TYPE_VIDEO_WIDGET, \
+ OwlVideoWidgetClass))
+#define OWL_IS_VIDEO_WIDGET(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ OWL_TYPE_VIDEO_WIDGET))
+#define OWL_IS_VIDEO_WIDGET_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ OWL_TYPE_VIDEO_WIDGET))
+#define OWL_VIDEO_WIDGET_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ OWL_TYPE_VIDEO_WIDGET, \
+ OwlVideoWidgetClass))
+
+typedef struct _OwlVideoWidgetPrivate OwlVideoWidgetPrivate;
+
+typedef struct {
+ GtkBin parent;
+
+ OwlVideoWidgetPrivate *priv;
+} OwlVideoWidget;
+
+typedef struct {
+ GtkBinClass parent_class;
+
+ /* Signals */
+ void (* tag_list_available) (OwlVideoWidget *video_widget,
+ GstTagList *tag_list);
+ void (* eos) (OwlVideoWidget *video_widget);
+ void (* error) (OwlVideoWidget *video_widget,
+ GError *error);
+
+ /* Future padding */
+ void (* _owl_reserved1) (void);
+ void (* _owl_reserved2) (void);
+ void (* _owl_reserved3) (void);
+ void (* _owl_reserved4) (void);
+} OwlVideoWidgetClass;
+
+GType
+owl_video_widget_get_type (void) G_GNUC_CONST;
+
+GtkWidget *
+owl_video_widget_new (void);
+
+void
+owl_video_widget_set_uri (OwlVideoWidget *video_widget,
+ const char *uri);
+
+const char *
+owl_video_widget_get_uri (OwlVideoWidget *video_widget);
+
+void
+owl_video_widget_set_playing (OwlVideoWidget *video_widget,
+ gboolean playing);
+
+gboolean
+owl_video_widget_get_playing (OwlVideoWidget *video_widget);
+
+void
+owl_video_widget_set_position (OwlVideoWidget *video_widget,
+ int position);
+
+int
+owl_video_widget_get_position (OwlVideoWidget *video_widget);
+
+void
+owl_video_widget_set_volume (OwlVideoWidget *video_widget,
+ double volume);
+
+double
+owl_video_widget_get_volume (OwlVideoWidget *video_widget);
+
+gboolean
+owl_video_widget_get_can_seek (OwlVideoWidget *video_widget);
+
+int
+owl_video_widget_get_buffer_percent (OwlVideoWidget *video_widget);
+
+int
+owl_video_widget_get_duration (OwlVideoWidget *video_widget);
+
+void
+owl_video_widget_set_force_aspect_ratio (OwlVideoWidget *video_widget,
+ gboolean force_aspect_ratio);
+
+gboolean
+owl_video_widget_get_force_aspect_ratio (OwlVideoWidget *video_widget);
+
+G_END_DECLS
+
+#endif /* __OWL_VIDEO_WIDGET_H__ */
diff --git a/src/plugins/gst-renderer/owl-video-widget.vapi b/src/plugins/gst-renderer/owl-video-widget.vapi
new file mode 100644
index 0000000..7de87c8
--- /dev/null
+++ b/src/plugins/gst-renderer/owl-video-widget.vapi
@@ -0,0 +1,31 @@
+[CCode (cprefix = "Owl", lower_case_cprefix = "owl_")]
+namespace Owl {
+ [CCode (cheader_filename = "owl-video-widget.h")]
+ public class VideoWidget : Gtk.Bin, Atk.Implementor, Gtk.Buildable {
+ public int get_buffer_percent ();
+ public bool get_can_seek ();
+ public int get_duration ();
+ public bool get_force_aspect_ratio ();
+ public bool get_playing ();
+ public int get_position ();
+ public weak string get_uri ();
+ public double get_volume ();
+ public VideoWidget ();
+ public void set_force_aspect_ratio (bool force_aspect_ratio);
+ public void set_playing (bool playing);
+ public void set_position (int position);
+ public void set_uri (string uri);
+ public void set_volume (double volume);
+ public int buffer_percent { get; }
+ public bool can_seek { get; }
+ public int duration { get; }
+ public bool force_aspect_ratio { get; set; }
+ public bool playing { get; set; }
+ public int position { get; set; }
+ public string uri { get; set; }
+ public double volume { get; set; }
+ public virtual signal void eos ();
+ public virtual signal void error (GLib.Error error);
+ public virtual signal void tag_list_available (Gst.TagList tag_list);
+ }
+}
diff --git a/src/plugins/gst-renderer/rygel-gst-av-transport.vala b/src/plugins/gst-renderer/rygel-gst-av-transport.vala
new file mode 100644
index 0000000..da2ad42
--- /dev/null
+++ b/src/plugins/gst-renderer/rygel-gst-av-transport.vala
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2008 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ */
+
+using GUPnP;
+
+public class Rygel.GstAVTransport : Service {
+ public const string UPNP_ID = "urn:upnp-org:serviceId:AVTransport";
+ public const string UPNP_TYPE =
+ "urn:schemas-upnp-org:service:AVTransport:2";
+ public const string DESCRIPTION_PATH = "xml/AVTransport2.xml";
+
+ // The setters below update the LastChange message
+ private uint _n_tracks = 0;
+ public uint n_tracks {
+ get {
+ return _n_tracks;
+ }
+
+ set {
+ _n_tracks = value;
+
+ this.changelog.log ("NumberOfTracks", _n_tracks.to_string ());
+ }
+ }
+
+ private uint _track = 0;
+ public uint track {
+ get {
+ return _track;
+ }
+
+ set {
+ _track = value;
+
+ this.changelog.log ("CurrentTrack", _track.to_string ());
+ }
+ }
+
+ private string _metadata = "";
+ public string metadata {
+ get {
+ return _metadata;
+ }
+
+ set {
+ _metadata = value;
+
+ string escaped = Markup.escape_text (_metadata, -1);
+
+ this.changelog.log ("CurrentTrackMetadata", escaped);
+ }
+ }
+
+ private string _status = "OK";
+ public string status {
+ get {
+ return _status;
+ }
+
+ set {
+ _status = value;
+
+ this.changelog.log ("TransportStatus", _status);
+ }
+ }
+
+ private string _speed = "1";
+ public string speed {
+ get {
+ return _speed;
+ }
+
+ set {
+ _speed = value;
+
+ this.changelog.log ("TransportPlaySpeed", _speed);
+ }
+ }
+
+ private string _mode = "NORMAL";
+ public string mode {
+ get {
+ return _mode;
+ }
+
+ set {
+ _mode = value;
+
+ this.changelog.log ("CurrentPlayMode", _mode);
+ }
+ }
+
+ private GstChangeLog changelog;
+ private GstVideoWindow video_window;
+
+ public override void constructed () {
+ this.changelog = new GstChangeLog (this);
+ this.video_window = GstVideoWindow.get_default ();
+
+ query_variable["LastChange"] += query_last_change_cb;
+
+ action_invoked["SetAVTransportURI"] += set_av_transport_uri_cb;
+ action_invoked["GetMediaInfo"] += get_media_info_cb;
+ action_invoked["GetTransportInfo"] += get_transport_info_cb;
+ action_invoked["GetPositionInfo"] += get_position_info_cb;
+ action_invoked["GetDeviceCapabilities"] += get_device_capabilities_cb;
+ action_invoked["GetTransportSettings"] += get_transport_settings_cb;
+ action_invoked["Stop"] += stop_cb;
+ action_invoked["Play"] += play_cb;
+ action_invoked["Pause"] += pause_cb;
+ action_invoked["Seek"] += seek_cb;
+ action_invoked["Next"] += next_cb;
+ action_invoked["Previous"] += previous_cb;
+
+ this.video_window.notify["uri"] += this.notify_uri_cb;
+ this.video_window.notify["playback-state"] += this.notify_state_cb;
+ this.video_window.notify["duration"] += this.notify_duration_cb;
+ }
+
+ private void query_last_change_cb (GstAVTransport s,
+ string variable,
+ ref Value val) {
+ // Send current state
+ GstChangeLog log = new GstChangeLog (null);
+
+ string escaped;
+
+ log.log ("TransportState",
+ this.video_window.playback_state);
+ log.log ("TransportStatus", this.status);
+ log.log ("PlaybackStorageMedium", "NOT_IMPLEMENTED");
+ log.log ("RecordStorageMedium", "NOT_IMPLEMENTED");
+ log.log ("PossiblePlaybackStorageMedia", "NOT_IMPLEMENTED");
+ log.log ("PossibleRecordStorageMedia", "NOT_IMPLEMENTED");
+ log.log ("CurrentPlayMode", this.mode);
+ log.log ("TransportPlaySpeed", this.speed);
+ log.log ("RecordMediumWriteStatus", "NOT_IMPLEMENTED");
+ log.log ("CurrentRecordQualityMode", "NOT_IMPLEMENTED");
+ log.log ("PossibleRecordQualityMode", "NOT_IMPLEMENTED");
+ log.log ("NumberOfTracks", this.n_tracks.to_string ());
+ log.log ("CurrentTrack", this.track.to_string ());
+ log.log ("CurrentTrackDuration", this.video_window.duration);
+ log.log ("CurrentMediaDuration", this.video_window.duration);
+ escaped = Markup.escape_text (this.metadata, -1);
+ log.log ("CurrentTrackMetadata", escaped);
+ escaped = Markup.escape_text (this.video_window.uri, -1);
+ log.log ("CurrentTrackURI", escaped);
+ log.log ("AVTransportURI", escaped);
+ log.log ("NextAVTransportURI", "NOT_IMPLEMENTED");
+
+ val.init (typeof (string));
+ val.set_string (log.finish ());
+ }
+
+ // Error out if InstanceID is not 0
+ private bool check_instance_id (ServiceAction action) {
+ uint instance_id;
+
+ action.get ("InstanceID", typeof (uint), out instance_id);
+ if (instance_id != 0) {
+ action.return_error (718, "Invalid InstanceID");
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private void set_av_transport_uri_cb (GstAVTransport s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ string _uri, _metadata;
+
+ action.get ("CurrentURI", typeof (string), out _uri,
+ "CurrentURIMetaData", typeof (string), out _metadata);
+
+ this.video_window.uri = _uri;
+ this.metadata = _metadata;
+
+ action.return ();
+ }
+
+ private void get_media_info_cb (GstAVTransport s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ action.set ("NrTracks",
+ typeof (uint),
+ this.n_tracks,
+ "MediaDuration",
+ typeof (string),
+ this.video_window.duration,
+ "CurrentURI",
+ typeof (string),
+ Markup.escape_text (this.video_window.uri, -1),
+ "CurrentURIMetaData",
+ typeof (string),
+ this.metadata,
+ "NextURI",
+ typeof (string),
+ "NOT_IMPLEMENTED",
+ "NextURIMetaData",
+ typeof (string),
+ "NOT_IMPLEMENTED",
+ "PlayMedium",
+ typeof (string),
+ "NOT_IMPLEMENTED",
+ "RecordMedium",
+ typeof (string),
+ "NOT_IMPLEMENTED",
+ "WriteStatus",
+ typeof (string),
+ "NOT_IMPLEMENTED");
+
+ action.return ();
+ }
+
+ private void get_transport_info_cb (GstAVTransport s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ action.set ("CurrentTransportState",
+ typeof (string),
+ this.video_window.playback_state,
+ "CurrentTransportStatus",
+ typeof (string),
+ this.status,
+ "CurrentSpeed",
+ typeof (string),
+ this.speed);
+
+ action.return ();
+ }
+
+ private void get_position_info_cb (GstAVTransport s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ action.set ("Track",
+ typeof (uint),
+ this.track,
+ "TrackDuration",
+ typeof (string),
+ this.video_window.duration,
+ "TrackMetaData",
+ typeof (string),
+ this.metadata,
+ "TrackURI",
+ typeof (string),
+ Markup.escape_text (this.video_window.uri, -1),
+ "RelTime",
+ typeof (string),
+ this.video_window.position,
+ "AbsTime",
+ typeof (string),
+ this.video_window.position,
+ "RelCount",
+ typeof (int),
+ int.MAX,
+ "AbsCount",
+ typeof (int),
+ int.MAX);
+
+ action.return ();
+ }
+
+ private void get_device_capabilities_cb (GstAVTransport s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ action.set ("PlayMedia", typeof (string), "NOT_IMPLEMENTED",
+ "RecMedia", typeof (string), "NOT_IMPLEMENTED",
+ "RecQualityModes", typeof (string), "NOT_IMPLEMENTED");
+
+ action.return ();
+ }
+
+ private void get_transport_settings_cb (GstAVTransport s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ action.set ("PlayMode", typeof (string), this.mode,
+ "RecQualityMode", typeof (string), "NOT_IMPLEMENTED");
+
+ action.return ();
+ }
+
+ private void stop_cb (GstAVTransport s, owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ this.video_window.playback_state = "STOPPED";
+
+ action.return ();
+ }
+
+ private void play_cb (GstAVTransport s, owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ string speed;
+
+ action.get ("Speed", typeof (string), out speed);
+ if (speed != "1") {
+ action.return_error (717, "Play speed not supported");
+
+ return;
+ }
+
+ this.video_window.playback_state = "PLAYING";
+
+ action.return ();
+ }
+
+ private void pause_cb (GstAVTransport s, owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ this.video_window.playback_state = "PAUSED_PLAYBACK";
+
+ action.return ();
+ }
+
+ private void seek_cb (GstAVTransport s, owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ string unit, target;
+
+ action.get ("Unit", typeof (string), out unit,
+ "Target", typeof (string), out target);
+ switch (unit) {
+ case "ABS_TIME":
+ case "REL_TIME":
+ if (!this.video_window.seek (target)) {
+ action.return_error (710, "Seek mode not supported");
+
+ return;
+ }
+
+ action.return ();
+
+ return;
+ default:
+ action.return_error (710, "Seek mode not supported");
+
+ return;
+ }
+ }
+
+ private void next_cb (GstAVTransport s, owned ServiceAction action) {
+ action.return_error (701, "Transition not available");
+ }
+
+ private void previous_cb (GstAVTransport s, owned ServiceAction action) {
+ action.return_error (701, "Transition not available");
+ }
+
+ private void notify_state_cb (GstVideoWindow video_window,
+ ParamSpec p) {
+ this.changelog.log ("TransportState", this.video_window.playback_state);
+ }
+
+ private void notify_uri_cb (GstVideoWindow video_window,
+ ParamSpec p) {
+ var escaped = Markup.escape_text (video_window.uri, -1);
+
+ this.changelog.log ("CurrentTrackURI", escaped);
+ this.changelog.log ("AVTransportURI", escaped);
+ }
+
+ private void notify_duration_cb (GstVideoWindow window,
+ ParamSpec p) {
+ this.changelog.log ("CurrentTrackDuration", window.duration);
+ this.changelog.log ("CurrentMediaDuration", window.duration);
+ }
+}
diff --git a/src/plugins/gst-renderer/rygel-gst-changelog.vala b/src/plugins/gst-renderer/rygel-gst-changelog.vala
new file mode 100644
index 0000000..745457a
--- /dev/null
+++ b/src/plugins/gst-renderer/rygel-gst-changelog.vala
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ */
+
+using GUPnP;
+
+// Helper class for building LastChange messages
+public class Rygel.GstChangeLog : Object {
+ public Service service { get; set; }
+
+ private StringBuilder str;
+
+ private Gee.HashMap<string, string> hash;
+
+ private uint timeout_id = 0;
+
+ public GstChangeLog (Service? _service) {
+ service = _service;
+ str = new StringBuilder ();
+ hash = new Gee.HashMap<string, string> (str_hash, str_equal, str_equal);
+ }
+
+ ~GstChangeLog () {
+ if (timeout_id != 0) {
+ Source.remove (timeout_id);
+ }
+ }
+
+ private bool timeout () {
+ // Emit notification
+ service.notify ("LastChange", typeof (string), finish ());
+
+ // Reset
+ hash.clear ();
+ str.erase (0, -1);
+ timeout_id = 0;
+
+ return false;
+ }
+
+ private void ensure_timeout () {
+ // Make sure we have a notification timeout
+ if (service != null && timeout_id == 0) {
+ timeout_id = Timeout.add (200, timeout);
+ }
+ }
+
+ public void log (string var, string val) {
+ hash.set (var, "<%s val=\"%s\"/>".printf (var, val));
+
+ ensure_timeout ();
+ }
+
+ public void log_with_channel (string var, string val, string channel) {
+ hash.set (var, "<%s val=\"%s\" channel=\"%s\"/>".printf (var, val,
+ channel));
+
+ ensure_timeout ();
+ }
+
+ public string finish () {
+ str.append ("<Event xmlns=\"" +
+ "urn:schemas-upnp-org:metadata-1-0/AVT_RCS\">" +
+ "<InstanceID val=\"0\">");
+ foreach (string line in hash.get_values ()) {
+ str.append (line);
+ }
+ str.append ("</InstanceID></Event>");
+
+ return str.str;
+ }
+}
diff --git a/src/plugins/gst-renderer/rygel-gst-connection-manager.vala b/src/plugins/gst-renderer/rygel-gst-connection-manager.vala
new file mode 100644
index 0000000..64a478d
--- /dev/null
+++ b/src/plugins/gst-renderer/rygel-gst-connection-manager.vala
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2008 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ */
+
+using GUPnP;
+
+public class Rygel.GstConnectionManager : Rygel.ConnectionManager {
+ // Creates a list of supported sink protocols based on GStreamer's
+ // registry. We don't use this because of the spam it generates ..
+ /*
+ private void setup_sink_protocol_info () {
+ Gst.Registry reg = Gst.Registry.get_default ();
+
+ Gee.HashSet<string> mime_types =
+ new Gee.HashSet<string> (GLib.str_hash, GLib.str_equal);
+
+ weak List<Gst.ElementFactory> factories =
+ reg.get_feature_list (typeof (Gst.ElementFactory));
+ foreach (Gst.ElementFactory factory in factories) {
+ weak List<Gst.StaticPadTemplate> templates =
+ factory.staticpadtemplates;
+ foreach (weak Gst.StaticPadTemplate template in templates) {
+ if (template.direction != Gst.PadDirection.SINK) {
+ continue;
+ }
+
+ Gst.Caps caps = template.static_caps.get ();
+ for (int i = 0; i < caps.get_size (); i++) {
+ weak Gst.Structure str =
+ template.static_caps.get_structure (i);
+
+ mime_types.add (str.get_name ());
+ }
+ }
+ }
+
+ foreach (string type in mime_types) {
+ stdout.printf ("%s\n", type);
+ }
+ }
+ */
+
+ public override void constructed () {
+ base.constructed ();
+
+ this.connection_ids = "0";
+ this.source_protocol_info = "";
+ this.sink_protocol_info = "http-get:*:audio/mpeg:*," +
+ "http-get:*:application/ogg:*," +
+ "http-get:*:audio/x-vorbis:*," +
+ "http-get:*:audio/x-vorbis+ogg:*," +
+ "http-get:*:audio/x-ms-wma:*," +
+ "http-get:*:audio/x-ms-asf:*," +
+ "http-get:*:audio/x-flac:*," +
+ "http-get:*:audio/x-mod:*," +
+ "http-get:*:audio/x-wav:*," +
+ "http-get:*:audio/x-ac3:*," +
+ "http-get:*:audio/x-m4a:*," +
+ "http-get:*:video/x-theora:*," +
+ "http-get:*:video/x-dirac:*," +
+ "http-get:*:video/x-wmv:*," +
+ "http-get:*:video/x-wma:*," +
+ "http-get:*:video/x-msvideo:*," +
+ "http-get:*:video/x-3ivx:*," +
+ "http-get:*:video/x-3ivx:*," +
+ "http-get:*:video/x-matroska:*," +
+ "http-get:*:video/mpeg:*," +
+ "http-get:*:video/x-ms-asf:*," +
+ "http-get:*:video/x-xvid:*," +
+ "http-get:*:video/x-ms-wmv:*," +
+ "http-get:*:audio/L16;" +
+ "rate=44100;" +
+ "channels=2:*," +
+ "http-get:*:audio/L16;" +
+ "rate=44100;" +
+ "channels=1:*";
+ }
+}
diff --git a/src/plugins/gst-renderer/rygel-gst-plugin.vala b/src/plugins/gst-renderer/rygel-gst-plugin.vala
new file mode 100644
index 0000000..c528ff7
--- /dev/null
+++ b/src/plugins/gst-renderer/rygel-gst-plugin.vala
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali (Khattak) <zeeshanak gnome org>.
+ * Copyright (C) 2008 Nokia Corporation, all rights reserved.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ * <zeeshan ali nokia com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using Rygel;
+using Gee;
+using CStuff;
+
+[ModuleInit]
+public void module_init (PluginLoader loader) {
+ string MEDIA_RENDERER_DESC_PATH = BuildConfig.DATA_DIR +
+ "/xml/MediaRenderer2.xml";
+
+ var plugin = new Plugin (MEDIA_RENDERER_DESC_PATH,
+ "GstRenderer",
+ "GStreamer Renderer");
+
+ plugin.add_resource (new ResourceInfo (ConnectionManager.UPNP_ID,
+ ConnectionManager.UPNP_TYPE,
+ ConnectionManager.DESCRIPTION_PATH,
+ typeof (GstConnectionManager)));
+ plugin.add_resource (new ResourceInfo (GstAVTransport.UPNP_ID,
+ GstAVTransport.UPNP_TYPE,
+ GstAVTransport.DESCRIPTION_PATH,
+ typeof (GstAVTransport)));
+ plugin.add_resource (new ResourceInfo (GstRenderingControl.UPNP_ID,
+ GstRenderingControl.UPNP_TYPE,
+ GstRenderingControl.DESCRIPTION_PATH,
+ typeof (GstRenderingControl)));
+
+ loader.add_plugin (plugin);
+}
+
diff --git a/src/plugins/gst-renderer/rygel-gst-rendering-control.vala b/src/plugins/gst-renderer/rygel-gst-rendering-control.vala
new file mode 100644
index 0000000..182af94
--- /dev/null
+++ b/src/plugins/gst-renderer/rygel-gst-rendering-control.vala
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2008 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ */
+
+using GUPnP;
+
+public class Rygel.GstRenderingControl : Service {
+ public const string UPNP_ID = "urn:upnp-org:serviceId:RenderingControl";
+ public const string UPNP_TYPE =
+ "urn:schemas-upnp-org:service:RenderingControl:2";
+ public const string DESCRIPTION_PATH = "xml/RenderingControl2.xml";
+
+ private bool _mute = false;
+ public bool mute {
+ get {
+ return _mute;
+ }
+
+ set {
+ _mute = value;
+
+ if (this._mute) {
+ this.video_window.volume = 0;
+ } else {
+ this.video_window.volume = Volume.from_percentage (this.volume);
+ }
+
+ this.changelog.log_with_channel ("Mute",
+ this.mute ? "1" : "0",
+ "Master");
+ }
+ }
+
+ private uint _volume = 0;
+ public uint volume {
+ get {
+ return _volume;
+ }
+
+ set {
+ _volume = value;
+
+ if (!this.mute) {
+ this.video_window.volume = Volume.from_percentage (this.volume);
+ }
+
+ this.changelog.log_with_channel ("Volume",
+ this.volume.to_string (),
+ "Master");
+ }
+ }
+
+ private string preset_name_list = "";
+
+ private GstChangeLog changelog;
+ private GstVideoWindow video_window;
+
+ public override void constructed () {
+ this.changelog = new GstChangeLog (this);
+ this.video_window = GstVideoWindow.get_default ();
+
+ query_variable["LastChange"] += query_last_change_cb;
+
+ action_invoked["ListPresets"] += list_presets_cb;
+ action_invoked["SelectPreset"] += select_preset_cb;
+ action_invoked["GetMute"] += get_mute_cb;
+ action_invoked["SetMute"] += set_mute_cb;
+ action_invoked["GetVolume"] += get_volume_cb;
+ action_invoked["SetVolume"] += set_volume_cb;
+
+ this._volume = Volume.to_percentage (this.video_window.volume);
+ }
+
+ private void query_last_change_cb (GstRenderingControl s,
+ string var,
+ ref GLib.Value val) {
+ // Send current state
+ var log = new GstChangeLog (null);
+
+ log.log_with_channel ("Mute", mute ? "1" : "0", "Master");
+ log.log_with_channel ("Volume", this.volume.to_string (), "Master");
+
+ val.init (typeof (string));
+ val.set_string (log.finish ());
+ }
+
+ // Error out if InstanceID is not 0
+ private bool check_instance_id (ServiceAction action) {
+ uint instance_id;
+
+ action.get ("InstanceID", typeof (uint), out instance_id);
+ if (instance_id != 0) {
+ action.return_error (702, "Invalid InstanceID");
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private void list_presets_cb (GstRenderingControl s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ action.set ("CurrentPresetNameList",
+ typeof (string),
+ this.preset_name_list);
+
+ action.return ();
+ }
+
+ private void select_preset_cb (GstRenderingControl s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ string preset_name;
+
+ action.get ("PresetName", typeof (string), out preset_name);
+ if (preset_name != "") {
+ action.return_error (701, "Invalid Name");
+
+ return;
+ }
+
+ action.return ();
+ }
+
+ // Error out if 'Channel' is not 'Master'
+ private bool check_channel (ServiceAction action) {
+ string channel;
+
+ action.get ("Channel", typeof (string), out channel);
+ if (channel != "Master") {
+ action.return_error (501, "Action Failed");
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private void get_mute_cb (GstRenderingControl s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ if (!check_channel (action)) {
+ return;
+ }
+
+ action.set ("CurrentMute", typeof (bool), this.mute);
+
+ action.return ();
+ }
+
+ private void set_mute_cb (GstRenderingControl s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ if (!check_channel (action)) {
+ return;
+ }
+
+ bool mute;
+
+ action.get ("DesiredMute", typeof (bool), out mute);
+
+ this.mute = mute;
+
+ action.return ();
+ }
+
+ private void get_volume_cb (GstRenderingControl s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ if (!check_channel (action)) {
+ return;
+ }
+
+ action.set ("CurrentVolume", typeof (uint), this.volume);
+
+ action.return ();
+ }
+
+ private void set_volume_cb (GstRenderingControl s,
+ owned ServiceAction action) {
+ if (!check_instance_id (action)) {
+ return;
+ }
+
+ if (!check_channel (action)) {
+ return;
+ }
+
+ uint volume;
+
+ action.get ("DesiredVolume", typeof (uint), out volume);
+ if (volume > 100) {
+ action.return_error (501, "Action Failed");
+
+ return;
+ }
+
+ this.volume = volume;
+
+ action.return ();
+ }
+}
+
+// Helper class for converting between double and percentage representations
+// of volume.
+private class Volume {
+ public static double from_percentage (uint percentage) {
+ return ((double) percentage / 100.0) * 4.0;
+ }
+
+ public static uint to_percentage (double volume) {
+ return (uint) ((volume / 4.0) * 100.0);
+ }
+}
+
diff --git a/src/plugins/gst-renderer/rygel-gst-video-window.vala b/src/plugins/gst-renderer/rygel-gst-video-window.vala
new file mode 100644
index 0000000..70ec6be
--- /dev/null
+++ b/src/plugins/gst-renderer/rygel-gst-video-window.vala
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2008 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ */
+
+using Gtk;
+using Gst;
+using Owl;
+
+public class Rygel.GstVideoWindow : Window {
+ private static GstVideoWindow video_window;
+
+ private VideoWidget video_widget;
+
+ private string _playback_state = "STOPPED";
+ public string playback_state {
+ get {
+ return this._playback_state;
+ }
+
+ set {
+ this._playback_state = value;
+
+ switch (_playback_state) {
+ case "STOPPED":
+ this.video_widget.playing = false;
+
+ if (this.video_widget.can_seek) {
+ this.video_widget.position = 0;
+ }
+
+ break;
+ case "PAUSED_PLAYBACK":
+ this.video_widget.playing = false;
+ break;
+ case "PLAYING":
+ this.video_widget.playing = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ public string uri {
+ get {
+ return this.video_widget.uri;
+ }
+
+ set {
+ this.video_widget.uri = value;
+ }
+ }
+
+ public double volume {
+ get {
+ return this.video_widget.volume;
+ }
+
+ set {
+ this.video_widget.volume = value;
+ }
+ }
+
+ public string duration { get; private set; }
+ public string playback_position { get; private set; }
+
+ construct {
+ this.type = WindowType.TOPLEVEL;
+ }
+
+ private GstVideoWindow () {
+ this.fullscreen_state = true;
+
+ this.video_widget.eos += this.eos_cb;
+ this.video_widget.notify["duration"] += this.notify_duration_cb;
+ this.video_widget.notify["position"] += this.notify_position_cb;
+
+ // Show a video widget
+ this.video_widget = new VideoWidget ();
+ this.video_widget.show ();
+
+ this.add (video_widget);
+ this.show_all ();
+
+ this.key_press_event += this.key_press_callback;
+ }
+
+ public static GstVideoWindow get_default () {
+ if (video_window == null) {
+ video_window = new GstVideoWindow ();
+ }
+
+ return video_window;
+ }
+
+ public bool fullscreen_state {
+ get {
+ if (this.window != null) {
+ return (this.window.get_state () &
+ Gdk.WindowState.FULLSCREEN) != 0;
+ }
+
+ return false;
+ }
+
+ set {
+ if (value)
+ this.fullscreen ();
+ else {
+ this.unfullscreen ();
+ }
+ }
+ }
+
+ private bool key_press_callback (GstVideoWindow window,
+ Gdk.EventKey event) {
+ switch (event.keyval) {
+ case 0xffc8: /* Gdk.KeySyms.F11 */
+ this.fullscreen_state = ! fullscreen_state;
+ break;
+ case 0xff1b: /* Gdk.KeySyms.Escape */
+ this.fullscreen_state = false;
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ private void eos_cb (VideoWidget video_widget) {
+ this.playback_state = "STOPPED";
+ }
+
+ private void notify_duration_cb (VideoWidget video_widget,
+ ParamSpec p) {
+ this.duration = Time.to_string (video_widget.duration);
+ }
+
+ private void notify_position_cb (VideoWidget video_widget,
+ ParamSpec p) {
+ this.playback_position = Time.to_string (video_widget.position);
+ }
+
+ public bool seek (string time) {
+ if (this.video_widget.can_seek) {
+ this.video_widget.position = Time.from_string (time);
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+// Helper class for converting between second and string representations
+// of time.
+private class Time {
+ public static int from_string (string str) {
+ int hours, minutes, seconds;
+
+ str.scanf ("%d:%2d:%2d%*s", out hours, out minutes, out seconds);
+
+ return hours * 3600 + minutes * 60 + seconds;
+ }
+
+ public static string to_string (int time) {
+ int hours, minutes, seconds;
+
+ hours = time / 3600;
+ seconds = time % 3600;
+ minutes = seconds / 60;
+ seconds = seconds % 60;
+
+ return "%d:%.2d:%.2d".printf (hours, minutes, seconds);
+ }
+}
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]