After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 649931 - [apexsink] Allow to play to new, generation 2 AirTunes (AirPlay) hardware
[apexsink] Allow to play to new, generation 2 AirTunes (AirPlay) hardware
Status: RESOLVED FIXED
Product: GStreamer
Classification: Platform
Component: gst-plugins-bad
git master
Other Linux
: Normal enhancement
: 0.10.23
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2011-05-11 03:38 UTC by W. Michael Petullo
Modified: 2011-05-19 06:08 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Patch against gst-plugins-bad Git master, as of approx. May 5th 2011 at 22:00 CST (17.09 KB, patch)
2011-05-11 03:38 UTC, W. Michael Petullo
needs-work Details | Review
Patch against gst-plugins-bad Git master, as of approx. May 5th 2011 at 22:00 CST (18.26 KB, patch)
2011-05-16 01:57 UTC, W. Michael Petullo
needs-work Details | Review
Patch against gst-plugins-bad Git master, as of approx. May 5th 2011 at 22:00 CST (18.68 KB, patch)
2011-05-18 19:05 UTC, W. Michael Petullo
needs-work Details | Review
Patch against gst-plugins-bad Git master, as of approx. May 18th 2011 at 15:50 CST (20.34 KB, patch)
2011-05-18 20:57 UTC, W. Michael Petullo
committed Details | Review

Description W. Michael Petullo 2011-05-11 03:38:28 UTC
Created attachment 187604 [details] [review]
Patch against gst-plugins-bad Git master, as of approx. May 5th 2011 at 22:00 CST

It seems that new Apple devices expect a slightly different protocol when receiving an AirTunes stream. I have written a patch that allow apexsink to stream to these devices. To support this, the plugin now has two additional properties: generation and transport-protocol.

With this patch, I am able to stream to AirServer, a software implementation of the newer protocol. There are some issues remaining, probably having to do with the use of usleep (notice the comment in the patch):

1. For some reason, no audio plays unless I specify "--gst-debug=apexsink:5" on the gst-launch command line. Very odd. Perhaps printing to the console slows down the timing of things.

2. I hear a skip every 6-40 seconds.
Comment 1 Sebastian Dröge (slomo) 2011-05-11 07:53:18 UTC
Review of attachment 187604 [details] [review]:

So the new generation uses UDP? You should make sure that you don't send too large packets from the sink then, TCP will handle that correctly by using fragmentation but for UDP you have to do it yourself

::: ext/apexsink/gstapexsink.c
@@ +119,3 @@
+  static GEnumValue generation[] = {
+    {GST_APEX_GENERATION_ONE, "GST_APEX_GENERATION_ONE", "First generation (e.g., original AirPort Express)"},
+    {GST_APEX_GENERATION_TWO, "GST_APEX_GENERATION_TWO", "Second generation (e.g., Apple TV v2)"},

Please use something else for the name (the first string), e.g. generation-one

@@ +136,3 @@
+  static GEnumValue transport_protocol[] = {
+    {GST_APEX_TCP, "GST_APEX_TCP", "TCP"},
+    {GST_APEX_UDP, "GST_APEX_UDP", "UDP"},

Please use something else for the name (the first string), e.g. udp and tcp

@@ +407,3 @@
+        GST_INFO_OBJECT (sink, "ApEx transport protocol set to \"%d\"", sink->transport_protocol);
+      } else {
+        G_OBJECT_WARN_INVALID_PSPEC (object, "transport protocol", prop_id, pspec);

Instead of this warning you should do nothing and use a GST_WARNING_OBJECT() that says that the property can only be changed in NULL state.

@@ +575,3 @@
+   */
+  usleep ((gulong) ((length * 1000000.) / (GST_APEX_RAOP_BITRATE *
+                     GST_APEX_RAOP_BYTES_PER_SAMPLE)));

Instead of sleeping it's probably a good idea to use the GstClock API here, which allows interruptions and everything.
Comment 2 Bastien Nocera 2011-05-12 15:05:52 UTC
BTW, I realise that this is mainly for testing, but the airport express support that should actually be used is the one in PulseAudio.

http://git.zx2c4.com/pulseaudio-raop2/ has a base for RAOP2 support in PulseAudio.
Comment 3 W. Michael Petullo 2011-05-12 15:20:21 UTC
(In reply to comment #2)
> BTW, I realise that this is mainly for testing, but the airport express support
> that should actually be used is the one in PulseAudio.

I agree, for many desktop applications. However, there are other uses where GStreamer may be more appropriate or convenient.
Comment 4 W. Michael Petullo 2011-05-16 01:55:32 UTC
 > So the new generation uses UDP? You should make sure that you don't send too
> large packets from the sink then, TCP will handle that correctly by using
> fragmentation but for UDP you have to do it yourself

The new generation uses either TCP or UDP. The protocol supported is advertised using mDNS/SD. Either way, the packet size is fixed at 12 (RTSP header) + 3 (ALAC header) + 1408 (data).
 
> ::: ext/apexsink/gstapexsink.c
> @@ +119,3 @@
> +  static GEnumValue generation[] = {
> +    {GST_APEX_GENERATION_ONE, "GST_APEX_GENERATION_ONE", "First generation
> (e.g., original AirPort Express)"},
> +    {GST_APEX_GENERATION_TWO, "GST_APEX_GENERATION_TWO", "Second generation
> (e.g., Apple TV v2)"},
> 
> Please use something else for the name (the first string), e.g. generation-one

Done in second patch.

> @@ +136,3 @@
> +  static GEnumValue transport_protocol[] = {
> +    {GST_APEX_TCP, "GST_APEX_TCP", "TCP"},
> +    {GST_APEX_UDP, "GST_APEX_UDP", "UDP"},
> 
> Please use something else for the name (the first string), e.g. udp and tcp

Done in second patch.

> @@ +407,3 @@
> +        GST_INFO_OBJECT (sink, "ApEx transport protocol set to \"%d\"",
> sink->transport_protocol);
> +      } else {
> +        G_OBJECT_WARN_INVALID_PSPEC (object, "transport protocol", prop_id,
> pspec);
> 
> Instead of this warning you should do nothing and use a GST_WARNING_OBJECT()
> that says that the property can only be changed in NULL state.

Done in second patch.

> @@ +575,3 @@
> +   */
> +  usleep ((gulong) ((length * 1000000.) / (GST_APEX_RAOP_BITRATE *
> +                     GST_APEX_RAOP_BYTES_PER_SAMPLE)));
> 
> Instead of sleeping it's probably a good idea to use the GstClock API here,
> which allows interruptions and everything.

Done in second patch.

I am still working on the timing issues noted in the original report.
Comment 5 W. Michael Petullo 2011-05-16 01:57:59 UTC
Created attachment 187886 [details] [review]
Patch against gst-plugins-bad Git master, as of approx. May 5th 2011 at 22:00 CST
Comment 6 Sebastian Dröge (slomo) 2011-05-16 07:03:00 UTC
Review of attachment 187886 [details] [review]:

::: ext/apexsink/gstapexsink.c
@@ +579,3 @@
+                                            (GstClockTime) (gst_clock_get_time(apexsink->clock) + ((length * 1000000000.)
+					     / (GST_APEX_RAOP_BITRATE * GST_APEX_RAOP_BYTES_PER_SAMPLE))));
+    gst_clock_id_wait (clock_id, NULL);

Looks good but to make this interruptible you have to store the clock_id somewhere and cancel it in the state change function when going from PAUSED to READY (before chaining up to the parent class).

Everything else looks good now and I'll commit it after this change
Comment 7 W. Michael Petullo 2011-05-18 19:05:25 UTC
Created attachment 188065 [details] [review]
Patch against gst-plugins-bad Git master, as of approx. May 5th 2011 at 22:00 CST

This version of the patch should address comment #6. It also fixes the issue where audio would not play unless used with --gst-debug=apexsink:5.
Comment 8 Sebastian Dröge (slomo) 2011-05-18 20:04:56 UTC
Comment on attachment 188065 [details] [review]
Patch against gst-plugins-bad Git master, as of approx. May 5th 2011 at 22:00 CST

No it doesn't, you need to store the clockid somewhere in the apexsink instance and cancel it when changing the state from PAUSED to READY. I don't see any changes in that part of the code
Comment 9 W. Michael Petullo 2011-05-18 20:57:46 UTC
Created attachment 188075 [details] [review]
Patch against gst-plugins-bad Git master, as of approx. May 18th 2011 at 15:50 CST

Unschedule clock ID on state change from PAUSED to READY.
Comment 10 Sebastian Dröge (slomo) 2011-05-19 06:08:19 UTC
commit a08290f9314fc01c6d8f0ad7165d2ba360cb0409
Author: W. Michael Petullo <mike@flyn.org>
Date:   Thu May 19 08:05:14 2011 +0200

    apexsink: Add support for generation 2 AirTunes hardware
    
    Fixes bug #649931.