OpenHome Forum
Songcast Receiver in Java - Printable Version

+- OpenHome Forum (http://forum.openhome.org)
+-- Forum: OpenHome (/forumdisplay.php?fid=1)
+--- Forum: Media (/forumdisplay.php?fid=3)
+--- Thread: Songcast Receiver in Java (/showthread.php?tid=1218)



Songcast Receiver in Java - PeteManchester - 27-02-2014 10:41 AM

Hi,

I've been experimenting with the Songcast protocol on my Java MediaPlayer, at the moment I have some very rough code that will query the ohz uri to get the ohm uri and from the ohm uri I then join and start to receive the audio stream/packets.

'4F 68 6D 20 01 03 07 24 32 02 01 B9 00 00 ....'

I have some questions about the Songcast audio stream:

Is the Songcast audio stream always in the same format, or could the format change?

What is the Songcast audio format, is it propriety, or is it in a 'standard' format.

But the main question is not really an OpenHome question, the documentation is very good (http://openhome.org/wiki/Av:Developer:Songcast:Ohm), what I am stuck on now is how to play back the audio stream.

In my MediaPlayer I use either MPD (or mplayer) to play back the Playlist tracks and Radio steams, so I was thinking that maybe I could parse my Songcast audio stream to a format that MPD or mplayer would understand, or the other option would be to try and playback through the hardware directly, but this seems a bit limited with Java..

Has anyone done anything similar,or have any ideas how I could playback the Songcast stream.

Thanks,

Pete.


RE: Songcast Receiver in Java - simonc - 28-02-2014 04:07 PM

(27-02-2014 10:41 AM)PeteManchester Wrote:  Hi,

I've been experimenting with the Songcast protocol on my Java MediaPlayer, at the moment I have some very rough code that will query the ohz uri to get the ohm uri and from the ohm uri I then join and start to receive the audio stream/packets.

'4F 68 6D 20 01 03 07 24 32 02 01 B9 00 00 ....'

I have some questions about the Songcast audio stream:

Is the Songcast audio stream always in the same format, or could the format change?

Any Songcast stream will follow the protocol spec you link below. The only circumstances in which the format would change would be if the version number (byte #5 of each packet) changes.

(27-02-2014 10:41 AM)PeteManchester Wrote:  What is the Songcast audio format, is it propriety, or is it in a 'standard' format.

Probably somewhere in between... The format is open; we've published the protocol spec and a sample implementation. Anyone is free to use it; there is no requirement to pay royalties or have your implementation approved/certified. This will be enough for some people to view it as a standard. The one thing that may cause others to disagree is that it hasn't been formally certified by e.g. IETF. There are no plans to pursue this more formal type of standardisation.

(27-02-2014 10:41 AM)PeteManchester Wrote:  But the main question is not really an OpenHome question, the documentation is very good (http://openhome.org/wiki/Av:Developer:Songcast:Ohm), what I am stuck on now is how to play back the audio stream.

In my MediaPlayer I use either MPD (or mplayer) to play back the Playlist tracks and Radio steams, so I was thinking that maybe I could parse my Songcast audio stream to a format that MPD or mplayer would understand, or the other option would be to try and playback through the hardware directly, but this seems a bit limited with Java..

Has anyone done anything similar,or have any ideas how I could playback the Songcast stream.

The audio is transmitted as big endian PCM. The ohSongcast repo includes code that parses audio packets; you could easily use this to extract the PCM data. If you don't have access to a codec that'll just pass through PCM (or possibly convert endianess), you could create a stream in WAV file format - all that'd be required here would be to write out a simple header then read PCM data from each packet, passing it on as if it were the next chunk of WAV data.


RE: Songcast Receiver in Java - simoncn - 04-03-2014 04:40 PM

(28-02-2014 04:07 PM)simonc Wrote:  If you don't have access to a codec that'll just pass through PCM (or possibly convert endianess), you could create a stream in WAV file format - all that'd be required here would be to write out a simple header then read PCM data from each packet, passing it on as if it were the next chunk of WAV data.

For a WAV stream, It would be necessary to convert each sample from big-endian to little-endian format.


RE: Songcast Receiver in Java - simonc - 04-03-2014 04:52 PM

(04-03-2014 04:40 PM)simoncn Wrote:  
(28-02-2014 04:07 PM)simonc Wrote:  If you don't have access to a codec that'll just pass through PCM (or possibly convert endianess), you could create a stream in WAV file format - all that'd be required here would be to write out a simple header then read PCM data from each packet, passing it on as if it were the next chunk of WAV data.

For a WAV stream, It would be necessary to convert each sample from big-endian to little-endian format.

I think it might be possible to note that audio data is big-endian by starting the stream with the bytes {'R', 'I', 'F', 'X'} (as opposed to the more usual {'R', 'I', 'F', 'F'}). Converting to little endian is probably better though as it'll be more widely supported (and also doesn't rely on my interpretation of vaguely worded docs.)


RE: Songcast Receiver in Java - simoncn - 04-03-2014 05:21 PM

(04-03-2014 04:52 PM)simonc Wrote:  I think it might be possible to note that audio data is big-endian by starting the stream with the bytes {'R', 'I', 'F', 'X'} (as opposed to the more usual {'R', 'I', 'F', 'F'}). Converting to little endian is probably better though as it'll be more widely supported (and also doesn't rely on my interpretation of vaguely worded docs.)

I think that would be sensible. According to this page, RIFX isn't widely supported. For example, MinimServer doesn't read RIFX files, and no-one has ever asked me to support this.


RE: Songcast Receiver in Java - PeteManchester - 04-03-2014 05:48 PM

Thanks for the detailed answer...

As usual the answer turned out to be quite simple, I used the javax.sound library to play the PCM.

It seems to be working quite well..

Thanks again,

Pete.


RE: Songcast Receiver in Java - PeteManchester - 14-03-2014 05:19 PM

Hi Sorry, another question.

The Songcast Receiver works very well when using either the OpenHome or Linn Soncast Sender for windows, but we have been testing with a Linn DS as the Songcast Sender and are having some issues.

The issues are mostly around 24bit tracks,

In the OHU audio message we are told the track is 24bit

Code:
2014-03-14 16:52:24,183 [OHMMessageQueue] INFO  [org.rpi.songcast.ohm.OHMEventAudio] Songcast Stream: Codec: ALAC SampleRate: 96000 BitRate: 4608000 BitDepth: 24
2014-03-14 16:52:24,228 [OHMMessageQueue] INFO  [org.rpi.songcast.core.SongcastPlayerJSLatency] Creating Audio Format: SampleRate:96000.0 BitRate:4608000 BitDepth:24 Channels:2 Codec:ALAC Signed:true BigEndian:true
2014-03-14 16:52:24,247 [OHMMessageQueue] DEBUG [org.rpi.songcast.core.SongcastPlayerJSLatency] FrameSize: 6
2014-03-14 16:52:24,253 [OHMMessageQueue] DEBUG [org.rpi.songcast.core.SongcastPlayerJSLatency] Adjusted Buffer Size: 31998
2014-03-14 16:52:24,268 [OHMMessageQueue] DEBUG [org.rpi.songcast.core.SongcastPlayerJSLatency] PCM_SIGNED 96000.0 Hz, 24 bit, stereo, 6 bytes/frame, big-endian

But in the meta data we are told the track is 16bit

Code:
2014-03-14 16:53:40,274 [OHMMessageQueue] DEBUG
[org.rpi.songcast.ohm.OHMEventTrack] MetaData:
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/">
    <item id="0$:Quality$1$albums$1$0" parentID="0$:Quality$1$albums$1"
        restricted="1">
        <dc:title xmlns:dc="http://purl.org/dc/elements/1.1/">Pazzo Il Mondo !?</dc:title>
        <upnp:genre xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">Musical</upnp:genre>
        <dc:date xmlns:dc="http://purl.org/dc/elements/1.1/">2008-01-01</dc:date>
        <upnp:album xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">55\21</upnp:album>
        <upnp:artist xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">Musica Nuda</upnp:artist>
        <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Musica Nuda</dc:creator>
        <upnp:originalTrackNumber xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">1
        </upnp:originalTrackNumber>
        <upnp:albumArtURI xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">http://10.0.1.146:9790/minimserver/*/Music/ALAC/Musica*20Nuda/Musica*20Nuda*20-*2055*2021*20(2008)/01*20Musica*20Nuda*20-*20Pazzo*20Il*20Mondo*20!*ef*80*a5.m4a/$!picture-8877-95049.jpg
        </upnp:albumArtURI>
        <res duration="0:02:32.000" size="14772199" bitsPerSample="16"
            sampleFrequency="44100" nrAudioChannels="2"
            protocolInfo="http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=0170000000000000000000​0000000000">http://10.0.1.146:9790/minimserver/*/Music/ALAC/Musica*20Nuda/Musica*20Nuda*20-*2055*2021*20(2008)/01*20Musica*20Nuda*20-*20Pazzo*20Il*20Mondo*20!*ef*80*a5.m4a
        </res>
        <upnp:class xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">object.item.audioItem.musicTrack
        </upnp:class>
    </item>
</DIDL-Lite>

When we try to play the track using javax.sound we set the format according to the OHU message (24bit) but we get an error when trying to write out audio byte array.

Code:
2014-03-14 16:53:40,593 [SongcastPlayerJavaSoundLatency] ERROR [org.rpi.songcast.core.SongcastPlayerJSLatency] Error Writing Data
java.lang.IllegalArgumentException: illegal request to write non-integral number of frames (880 bytes, frameSize = 6 bytes)
    at com.sun.media.sound.DirectAudioDevice$DirectDL.write(DirectAudioDevice.java:731)​
    at org.rpi.songcast.core.SongcastPlayerJSLatency.addData(SongcastPlayerJSLatency.ja​va:99)
    at org.rpi.songcast.core.SongcastPlayerJSLatency.run(SongcastPlayerJSLatency.java:1​69)
    at java.lang.Thread.run(Thread.java:724)

Is there some kind of upscaling heppening on the Linn DS, that would make sense, that the meta data has information about the original track and the songcast has information about the upscaled stream from songcast.

On non 24bit tracks everything seems fine, even though the codec is described as non PCM (mpd, alac).

Thanks,

Pete.


RE: Songcast Receiver in Java - simoncn - 14-03-2014 05:56 PM

(14-03-2014 05:19 PM)PeteManchester Wrote:  When we try to play the track using javax.sound we set the format according to the OHU message (24bit) but we get an error when trying to write out audio byte array.

Code:
2014-03-14 16:53:40,593 [SongcastPlayerJavaSoundLatency] ERROR [org.rpi.songcast.core.SongcastPlayerJSLatency] Error Writing Data
java.lang.IllegalArgumentException: illegal request to write non-integral number of frames (880 bytes, frameSize = 6 bytes)
    at com.sun.media.sound.DirectAudioDevice$DirectDL.write(DirectAudioDevice.java:731)​
    at org.rpi.songcast.core.SongcastPlayerJSLatency.addData(SongcastPlayerJSLatency.ja​va:99)
    at org.rpi.songcast.core.SongcastPlayerJSLatency.run(SongcastPlayerJSLatency.java:1​69)
    at java.lang.Thread.run(Thread.java:724)

Is there some kind of upscaling heppening on the Linn DS, that would make sense, that the meta data has information about the original track and the songcast has information about the upscaled stream from songcast.

On non 24bit tracks everything seems fine, even though the codec is described as non PCM (mpd, alac).

Thanks,

Pete.

For 24-bit audio, a stereo sample is 6 bytes long. You are trying to write 880 bytes, which doesn't correspond to an integral number of 6-byte samples.


RE: Songcast Receiver in Java - simonc - 14-03-2014 05:59 PM

Other than eventing it to subscribers and sending it via songcast, the DS does not use the metadata that a control point provides. You can trust the bit depth in the songcast frames and ignore the one in metadata.

Audio data in songcast frames will be packed big endian pcm. I think it should always contain audio for an exact number of samples. In normal use, you'd expect each frame to contain 5ms of audio. For 24-bit 96kHz stereo this'd equate to 2880 bytes of audio per frame. Can you recheck the code that is reporting 880 bytes please?