FLVTool2 broken

To make FLV's coming from flash streaming servers (FCS/FMS/Red5) properly work as a progressive download, it's often required to use a tool to inject 'missing' meta-data.

Up until now I used FLVTool2 for this, but it seems to load the entire FLV into memory (badd!), and now I'm getting nasty errors as well..

  1. ERROR: EOFError
  2. ERROR: /usr/local/lib/site_ruby/1.8/flv/amf_string_buffer.rb:37:in `read'
  3. ERROR: /usr/local/lib/site_ruby/1.8/flv/amf_string_buffer.rb:243:in `read__STRING'
  4. ERROR: /usr/local/lib/site_ruby/1.8/flv/audio_tag.rb:56:in `read_header'
  5. ERROR: /usr/local/lib/site_ruby/1.8/flv/audio_tag.rb:47:in `after_initialize'
  6. ERROR: /usr/local/lib/site_ruby/1.8/flv/tag.rb:56:in `initialize'
  7. ERROR: /usr/local/lib/site_ruby/1.8/flv/stream.rb:447:in `new'
  8. ERROR: /usr/local/lib/site_ruby/1.8/flv/stream.rb:447:in `read_tags'
  9. ERROR: /usr/local/lib/site_ruby/1.8/flv/stream.rb:58:in `initialize'
  10. ERROR: /usr/local/lib/site_ruby/1.8/flvtool2/base.rb:272:in `new'
  11. ERROR: /usr/local/lib/site_ruby/1.8/flvtool2/base.rb:272:in `open_stream'
  12. ERROR: /usr/local/lib/site_ruby/1.8/flvtool2/base.rb:238:in `process_files'
  13. ERROR: /usr/local/lib/site_ruby/1.8/flvtool2/base.rb:225:in `each'
  14. ERROR: /usr/local/lib/site_ruby/1.8/flvtool2/base.rb:225:in `process_files'
  15. ERROR: /usr/local/lib/site_ruby/1.8/flvtool2/base.rb:44:in `execute!'
  16. ERROR: /usr/local/lib/site_ruby/1.8/flvtool2.rb:168:in `execute!'
  17. ERROR: /usr/local/lib/site_ruby/1.8/flvtool2.rb:228
  18. ERROR: /usr/bin/flvtool2:2:in `require'
  19. ERROR: /usr/bin/flvtool2:2

I wanted to try out FLVTool++ anyway, as it seems like a much more scalable solution.. This is how its installed on debian:

Grab the source:

  1. wget "http://rcdn.org:8080/images/4/44/Flvtool%2B%2B.tar.bz2"

Unpack

  1. tar xfvj Flvtool++.tar.bz2

Make sure you have a compiler installed (as root).

  1. apt-get install build-essential

Compiling

  1. make

AArrghh! It doesn't compile :(. This is the error it gave me:

  1. g++ -O2 -c -Wall -D_FILE_OFFSET_BITS=64 flvtool.cpp -o flvtool.o
  2. AMF.h:35: error: invalid pure specifier (only `= 0' is allowed) before ';' token
  3. flvtool.cpp: In function 'int main(int, char**)':
  4. flvtool.cpp:192: warning: too many arguments for format
  5. make: *** [flvtool.o] Error 1

Ok, lets dig into this code a bit.. the errors seems to give a few useful hints..

First thing I did was go to line 35 in AMF.h and change:

  1. virtual std::ostream& operator << (std::ostream& os) const = NULL;

into:

  1. virtual std::ostream& operator << (std::ostream& os) const = 0;

Next, open flvtool.cpp (line 192) and change:

  1. fprintf(stderr, "usage:\n", argv[0]);

Into:

  1. fprintf(stderr, "usage:\n");

Now, for some reason you also need to manually create the bin/ directory in your source directory, after that you can start compiling again.

  1. mkdir bin
  2. make

Success! Your bin/ directory will now contain the flvtool++ binary

  1. ./bin/flvtool++
  2. #outputs:
  3.  
  4. flvtool++ version 1640 2007/05/24 14:39:18
  5. usage:
  6.   flvtool++ <input file> <output file> (<start time> <end time>)
  7.   flvtool++ -i <input file>

Firefox gets httpOnly cookies

httpOnly cookies allow you to hide your (session-)cookies from javascript. In the case of an XSS hole in your application, it will make a hackers life much harder to steal someones session.

Internet Explorer support httpOnly cookies for a long time, but since version 2.0.0.5 Firefox also supports this feature. Apparently Mozilla hasn't openly promoted this new feature yet, because its still possible to fetch the cookies with XMLHttpRequest. PHP has support for httpOnly cookies and sessions since 5.2.

PHP-RPC update 3

I figured, the best way to come up with a usable PHP-RPC spec, is to put it in practice, and see where the problems appear. This is to prevent putting 'academical correctness' before usefulness.

So, I created a server implementation, and so far it works well. It's under a bsd license, so feel free to give it a shot. Right now I put everything in the 'Sabre' package, hopefully at one point this will be Zend, PEAR2 or Solar, but I'll look at that when I can put the 1.0 stamp on it. This implementation is only tested with PHP 5.2 and 5.1.

download link

The server class:

  1. <?php
  2.  
  3.   // creating the server object
  4.   require_once 'Sabre/PHPRPC/Server.php';
  5.   $server = new Sabre_PHPRPC_Server();
  6.  
  7.   // handling method calls
  8.   // Method contains a method name.. this could for example be 'blog.getPosts'
  9.   // argumentNotation is 1 if its a simple array, it is 2 if the parameters are specified as a struct
  10.   function invokeMethod($method,array $arguments,$argumentNotation) {
  11.  
  12.      return 'Hello World! You called the following method: ' . $method;
  13.  
  14.   }
  15.  
  16.   $server->setInvokeCallback('invokeMethod');
  17.  
  18.   // after this point, everything goes automatic.
  19.   $server->exec();
  20.  
  21. ?>

A sample client:

  1. <?php
  2.  
  3.   $url = 'http://www.example.org/phprpc.php';
  4.  
  5.   $data = file_get_contents($url . '?method=system.testingMethod');
  6.   $data = unserialize($data);
  7.  
  8.   echo $data['result']; // will output "Hello World! You called the following method: system.testingMethod
  9.  
  10. ?>

Here's the updated proposal. Changes have been highlighted.

The proposal (0.2)

Goals

  • Client should be very easy to implement. Server is allowed to be a bit more complex.
  • No duplication of the HTTP protocol. For example, HTTP already provides encryption, redirecting and authentication.
  • PHP 4/5/6 compatiblity.
  • Client and server implementations should be built from the idea 'be strict in what you produce, be liberal in what you accept'

The request

Requests are made using either GET or POST. Both should be accepted. GET is more appropriate for fetching information, whereas POST is used for posting new data. POST has the advantage that it doesn't have any limits in the size of the request and an encoding can be supplied. GET has the advantage that information can be fetched using a one-liner.

When there is no encoding specified, UTF-8 is assumed. Data supplied using POST should be encoded as application/x-www-form-urlencoded (this is how a browser submits data by default).

The method thats called should always be supplied as the 'method' variable. The method can contain periods (.) to seperate namespaces like XML-RPC. Arguments can be specified in two ways, and the API documentation should specify what the appropriate way is. The first way is using named arguments, a GET example would be:

  1. http://www.example.org/services/phprpc?method=getUsers&maxItems=20

The method here is getUsers, the named argument is maxItems and its value is 20.

The second way is using a list of arguments, which might be more appropriate in some cases where you want to directly map services and methods from a class on the server to the api. This is also how XML-RPC works.

  1. http://www.example.org/services/phprpc?method=getUsers&arguments[0]=20&arguments[1]=1

The first argument is 20, the second is 1.

Smart clients should autodetect if the user is trying to use named arguments or a sequence by checking out the type of the keys in the array.

Smart servers should use reflection to automatically map named arguments to the actual arguments in a list.

Clients SHOULD supply the version of PHP they are running. This can be either a complete version number, or just the major version (e.g.: 4, 5, 6). Clients should supply this as the phpVersion parameter. If the versionnumber is not supplied, the current stable PHP version is assumed, which is at the time of writing 5.

Clients MAY also supply the version of the PHP-RPC protocol as the 'version' parameter. Currently this is 0.2.

Clients MAY supply a returnClasses parameter. The value for returnClasses is either 0 or 1 and this can tell the server if the client is aware of typed objects that might be sent from the server.

The server

The server MUST allow requests both GET and POST requests. The server MUST treat any incoming text without encoding as UTF-8.

The server SHOULD allow both named arguments and indexed arguments for methods where this is possible.

If the client sent phpVersion the server MUST convert the returned serialized string so it can be read by the server. If the phpVersion is 4 or 5 the server MUST convert all unicode-strings (type U) to binary strings (type s). If the phpVersion is 4 the server MUST convert all private and protected properties to public properties.

Servers SHOULD also convert all typed objects to either STDClass'es or arrays when the client supplied returnClasses is set to 0, if this is appropriate.

I'm fairly sure I will remove the following paragraph. If PHP gets an HTTP 500 on a file_get_contents, it will throw an error, which removes the possibility to easily grab the error message.

When the method-call was successful the server should send HTTP code 200. When an error occurred the server should send an appropriate HTTP error code. (for example 400 for missing arguments, 500 for unexpected exceptions, 401 if the user should authenticate itself first and 403 is the method was not allowed to be called).

The return data is always in PHP's serialize data format. The Content-Type header should always be 'application/x-php-serialized'

The server will always return an error with the following properties:

result
The actual return data.. (or an array with information about an exception, in which case it should have at least the 'message' property.)
status
HTTP status code for the method call. (200 = success, 500 = internal server error, 400 = bad request, etc etc.) Custom error codes have to start at 600.
version
optional: PHP-RPC protocol version. Currently this is 0.2
server
optional: Name of the server. Can be any string.

FiTC 2007 presentation video

FITC presentation videos are online, including mine. Its the first time I actually watch it. It's a bit creepy to see myself, but I guess it went fine. I couldn't get a mic hooked up at the time, so the audio quality is a bit crappy.

I also notice that I made a bunch of mistakes related to embedding swf's, which I only found out later.

A funny fact: The girl sitting right in the front (and leaves after a while) is now my girlfriend. So we actually have video footage of when we first met :).

PHP namespaces

For those of you who are not following the PHP internals mailing list, the namespaces subject has been brought up again, with a simple implementation from Dmitry Stogov.

Its hard to say if it actually gets integrated in the source, as its a touchy subject and many implementations have came before this; but I love the way its implemented.

Quotes from Dmitry:

Namespaces are defined the following way:

  1. <?php
  2. namespace Zend::DB;
  3.  
  4. class Connection {
  5. }
  6.  
  7. function connect() {
  8. }
  9. ?>

Namespace definition does the following:
All class and function names inside are automatically prefixed with namespace name. Inside namespace, local name always takes precedence over global name. It is possible to use the same namespace in several PHP files.
The namespace declaration statement must be the very first statement in file.

Every class and function from namespace can be referred to by the full name
- e.g. Zend::DB::Connection or Zend::DB::connect - at any time.

  1. <?php
  2. require 'Zend/Db/Connection.php';
  3. $x = new Zend::DB::Connection;
  4. Zend::DB::connect();
  5. ?>

Namespace or class name can be imported:

  1. <?php
  2. require 'Zend/Db/Connection.php';
  3. import Zend::DB;
  4. import Zend::DB::Connection as DbConnection;
  5. $x = new Zend::DB::Connection();
  6. $y = new DB::connection();
  7. $z = new DbConnection();
  8. DB::connect();
  9. ?>
 1

About

My name is Evert, and I've been writing semi-regularly on this blog since 2006.

I'm currently available for contract work.

more info.

Subscribe

Dropbox

Dropbox is a simple cross-platform online backup and sync application. The first 2GB of space is free, and both you and me get an extra 250MB extra space if you sign up through this link.