Flash Media Server/FFmpeg Annoyance #1: RTMP/Stream URIs

By Brian Lesser
August 11, 2011 | Comments: 2

Every project has its annoying moments that often are quickly forgotten. So before I forget, I thought I'd make some notes in case this one is useful for someone else.

Our FFmpeg-based video compositing engine connects to FMS in order to read and write streams. But there's a problem. The rtmp_open method (in FFmpeg/libavformat/rtmpproto.c) couldn't connect to our application. Here's why.

RTMP URIs

Our FMS application is named virtualStudio and may have many instances - one for each studio. Each Flash client must connect to a specific studio. Each application instance of the virtual studio application is available at a different RTMP address. For example studios 12 and 24 would be at:

rtmp://x.ryerson.ca/virtualStudio/studio/12
rtmp://x.ryerson.ca/virtualStudio/studio/24

Each studio manages its own streams. Streams within an application instance have their own URIs. Since we are using the Real-Time Component Framework, our stream URIs look something like this:

rtcfResources/source/public/1342

In Flash that's not a problem. First, our Flash client connects to a studio:

netConnection.connect("rtmp://x.ryerson.ca/virtualStudio/studio/24", credentials);

Second, after it connects, it publishes a stream:

netStream.publish("rtcfResources/source/public/1342");

Tools like FFmpeg are designed to get a stream (via other protocols) using a single URI - not two. So we have to put the two URIs together so FFmpeg will connect to FMS and then play the stream:

rtmp://x.ryerson.ca/virtualStudio/studio/24/rtcfResources/source/public/1342

The problem for FFmpeg (and for many Flash-based video players) is that it doesn't know what part of the URI is the application instance it should connect to and what part is the URI of the stream. In other words where does the RTMP URI end and the stream URI begin?

It turns out FFmpeg's rtmp_open function does something like this:

  1. assume the first part of the path after the host (and port) is the application to connect to unless
  2. a colon is found in the path, in which case use the first and second part of the path if there is one.

In the example I've been using virtualStudio is the first part of the path so FFmpeg will try to connect to:

rtmp://x.ryerson.ca/virtualStudio

That won't work for our application. We need to connect to:

rtmp://x.ryerson.ca/virtualStudio/studio/24

URIs with File Types?

In some cases you have to specify a file type in stream URIs. For example to play an mp4 in Flash:

netStream.play("mp4:rtcfResources/source/public/1342.mp4");

We initially thought specifying the file type might help. For example putting the RTMP and stream URIs together again:

rtmp://x.ryerson.ca/virtualStudio/studio/24/mp4:rtcfResources/source/public/1342.mp4

But that doesn't work either as FFmpeg tries to connect to:

rtmp://x.ryerson.ca/virtualStudio/studio

If you're curious why FFmpeg behaves this way, here's a snippet of code from FFmpeg's rtmp_open method in FFmpeg/libavformat/rtmpproto.c that extracts the path from the URI to connect to FMS:


//Checks for the first '/' after appName
char *p = strchr(path + 1, '/');
if (!p) {
//if it doesn't exist set rt->app (application path) to null
fname = path + 1;
rt->app[0] = '\0';
} else {
char *c = strchr(p + 1, ':');
fname = strchr(p + 1, '/');
//if next '/' doesn't exist or there exists a ':' before the next '/' then the leftover is streamName
if (!fname || c < fname) {
fname = p + 1;
av_strlcpy(rt->app, path + 1, p - path);
} else {
//everything after next '/' is streamName
fname++;
av_strlcpy(rt->app, path + 1, fname - path - 1);
}
}

To help with reading the code, p is a character pointer to the first slash within the path (the one between virtualStudio and studio), fname is a pointer to the second slash and c is pointer to the colon.

It would probably be better if the code broke the URI in two just before any filetype token. But then again, this kind of combined URI with a filetype token in the middle is probably not a valid URI anyway so its hard to say what FFmpeg should do.

A Quick Hack

Here's our quick hack to fix the problem. We set a variable that says where to split the URI and added this code to rtmp_open:


//Loop the number of times as specified by the variable rtmp_app_depth
for (int i=1; i < rtmp_app_depth; i++) {
char *temp = strchr(p + 1, '/');
if (!temp) {
break;
}
p = temp;
}
fname = p;
p++; fname++;
av_strlcpy(rt->app, path + 1, p - path);

So far it seems to work.

Not an FFmpeg Problem

This problem has been around ever since Macromedia (now Adobe) introduced their FLVPlayback component. It might have been better if it had two properties you could set: one for the application instance and one for the stream URI. But they used one source property that required combining the RTMP URI and stream URI. (That's likely because they wanted to support progressive download via HTTP using the same property.) Some information about stream names is available from Adobe here:

http://goo.gl/xqtu9

One way video players deal with this problem is to try to connect to every possibly application instance name before the stream name or file type. For example a player might attempt to connect to all of these addresses and then use whatever worked:

rtmp://x.ryerson.ca/virtualStudio
rtmp://x.ryerson.ca/virtualStudio/studio
rtmp://x.ryerson.ca/virtualStudio/studio/24

That seems a little wasteful. The only way out of this problem seems to be adopting a different URI scheme for combined RTMP/Stream addresses. It might be as simple as adopting a semicolon as a separator. For example:

rtmp://x.ryerson.ca/virtualStudio/studio/24;mp4:rtcfResources/source/public/1342.mp4

Maybe one day everyone can adopt something like that. It's clear and unambiguous though I have no ideas what headaches it might cause for FFmpeg or video players and the "mp4:" may mean it isn't a valid URI. Maybe use a query string to define the stream path instead?

In the mean time its not hard to hack an open source project like FFmpeg in order to connect to an application instance.

Hmm, maybe we'll work on having rtmp_open work with an optional semicolon...

Some References

Virtual Studio project: http://ru.ryerson.ca/virtualStudio/
Illustration of how the compositing engine works: http://goo.gl/k1RCI
Introduction to the Real Time Component Framework: http://goo.gl/u52mt
URI Schemes: http://en.wikipedia.org/wiki/URI_scheme

Credits

Thanks to James Currie and Igor Ljaskevic who are doing the real work on the video compositing engine and everyone who has contributed to FFmpeg.


You might also be interested in:

2 Comments

Hey Brian,

It's funny I just finished writing a NetConnection manager class for a popular open source audio player package that leverages Flash, Jplayer: https://github.com/happyworm/jPlayer - it never had support for RTMP before, so in my fork I've added it in:https://github.com/rmhall/jPlayer - I have run into the same problem with appname names and instance names in FMS many times, and no one appears to have standardized on a better way, other than to try multiple connections to work things out (waste of time/bandwidth in my opinion). In my NetConnection manager, I'm using ^ to indicate the breakpoint after an instance name, similar to how you are using a ;

Good to know others have run into this and thinking the same way, FFMPEG should adopt usage of a special sentinel character. I went with ^ but ; works nicely too. If there was a standard it would be super handy.

Rob

Thanks Robert,
I find a separator appealing because it is so simple, but, as Ranbir Singh just pointed out on the FlashMedia mailing list, librtmp uses a query string:

http://rtmpdump.mplayerhq.hu/librtmp.3.html

For example playPath.

I hope Adobe can come up with a valid URI scheme to unambiguously resolve this.

News Topics

Recommended for You

Got a Question?