PHPShout : a shoutcast streamer in PHP: Part 4

Warning: This blogpost has been posted over two years ago. That is a long time in development-world! The story here may not be relevant, complete or secure. Code might not be complete or obsoleted, and even my current vision might have (completely) changed on the subject. So please do read further, but use it with caution.
Posted on 24 Mar 2012
Tagged with: [ C ]  [ extension ]  [ icecast ]  [ PHP ]  [ streaming

In the last post, we created a template extension for our shout class. Next up, we need to do the actual implementation.

Task 6: creating setters

Let’s take a look at the source from this commit: https://github.com/jaytaph/phpshout/blob/99fed246064b5046ec18b848bf779359353268a0/shout.c

Creating the setters is just as easy as creating the getters. The same applies with the setters: we can use a generic function for setting data (we need a few, depending on the type we need to set), but it all work the same way.

Check out line 161 to 212 on the generic setters. Notice that on line 197 we are getting a long integer from PHP, but we need to store a short int. So we mask only the short in part (0xFFFF). This means we can still add a value that is higher than 65535, but it will gets converted to a short. It also would be possible to check the value, and if the value is higher than 65535, it will throw an error.

Like 215 and 216 are special cases, since they will return the error number and error string of the shout library. There are not setters for this. The rest of the functions all have a getters and setters.

Task 7: Other functionality

We’re getting REALLY close in finishing up our extension. All that is left to do is write the functionality for all the non-getter/setter functions. The first one is already there on line 263. This function returns a bool(true) when your  shout object is connected to a stream, or false otherwise. You will see we are doing a mapping on line 271 so it will return either true or false.

Now, let’s take a look at all the functions added. This is done in commit: https://github.com/jaytaph/phpshout/blob/35ac2b32009936e53c3b336efed36a604d37d9f3/shout.c

First off all, I’ve changed the layout a bit for the getters and setters on line 220 to 373. The open(), close() and sync(), send() and delay() methods  should look really familar by now. The same goes for the get_audio_info() and set_audio_info() methods on line 472 and line 489. The only thing you will see is that the set_audio_info() accepts 2 strings (a key and a value) from PHP. We talk about the set_metadata() function later, but the get_queue_length() function is also a simple one.

On line 613, you will see our structure filling up quite nicely with public methods  now :-)

Now, the only thing left to do now is talk about the set_metadata() function. This function is a bit different than the rest, since this function actually accepts an array. Internally in the libshout library, the metadata acts like an array. So from a PHP point of view, it makes sense to use arrays for this. So what we do on line 515 is accept an array. Then on line 520, we initialize a shout_metadata structure that will hold the data we want to set.

The for-loop on line 523 to 525 can been seen as a loop over all the elements from the array we just got. Then on line 533 we call  SEPARATE_ZVAL with the value for the current element. This separation actually creates a copy (but not really) of the current value so we can change it. If we leave this out, all changes to this value will also reflect back in the original PHP array, which we don’t want.

The convert_to_string makes sure that the value is always a string, which comes in handy since we need strings to store in our metadata structure.

Next on line 537, we try and get the key for the current value we are iterating. This key can either be a string or a number. If it’s a number, it means that the “str_key” value is not filled, and we will make sure that this number is converted into a string nevertheless. We do that on lines 538 to 541.

When all is done, we have a string with the key, and a string with the value (both not binary safe, but we still don’t care about that :)), and add it to our metadata structure through shout_metadata_add. The Z_STRVAL_PP is a macro that returns the string value for that current element.

When we are done with iterating, we can set the metadata on line 549, and afterwards we can free the metadata structure on line 550. We return the status code of the setting of the metadata on line 552.

And we’re done (for now)

That’s it really. All the functionality from libshout is implemented! I’ve added an example on how to use the extension on github as well https://github.com/jaytaph/phpshout/blob/35ac2b32009936e53c3b336efed36a604d37d9f3/example/example.php, but make sure you use the latest version from https://github.com/jaytaph/phpshout/.

To test if it works, I’ve done the following. On my machine, I’ve setup an icecast server (I’m running OSX, so i’ve used a trail version of nicecast, but there are others). I’ve setup the server and changed this info in the example.php configuration (primarily the ip, port and password). Then, I added a file.mp3 to the directory so there is something to stream and started the example. If everything goes ok, you see a . appearing for every 4096kb of data that is stream to the server.

Next, I connected a few listeners (they connect to http://192.168.1.10:8000/listen.m3u, in my case) and dance away to my new PHP streamer!!  Oh the fun!

Conclusion

Cool things are cool, and writing something from scratch is one of them. Off course, there are hundreds better ways to stream data, but now it’s possible to do this from PHP as well. Lets stream your MP3 data, change the set_metadata according to the ID3 tags inside, show an upcoming playlist, a previous playlist, or even let people up/down vote for their favourite tunes and create a php jukebox. Who needs spotify or last.fm anymore? :p