Sunday, December 29, 2013

Happy streaming in 2014!

My son Andrew


Thanks to Adrian Drzewicki (webina.rs project) for the amazing t-shirt.

One common exec issue

I'm often asked to help with exec directive not starting child ffmpeg (or any other process). One common error is the following

  • ffmpeg is NOT in /bin or /usr/bin
  • full path is not specified in exec, only binary name

In this case exec will not start child process because nginx wipes its environment before starting workers. So child processes have empty PATH variable and cannot find the executable. In such cases the binary is usually looked up in default locations like /bin or /usr/bin.

To solve the issue please add env PATH directive to nginx.conf. It will preserve PATH environment variable.

...
# keep $PATH
env PATH;
...

rtmp {
  server {
    listen 1935;
    application myapp {
        live on;
        exec ffmpeg -i rtmp://localhost/myapp/$name
                    -c copy -f flv rtmp://localhost/app2/$name;
    }
    application app2 {
      live on;
    } 
  }
}

Saturday, December 21, 2013

Arutcam, MPEG-DASH and stat

In nginx-rtmp-module version 1.0.9 I've implemented parsing H264 SPS block. Now it's possible to see video dimensions even if no RTMP metadata came from publisher. It's important for arutcam which does not generate such metadata. In earlier versions this prevented MPEG-DASH engine from generating valid initialization fragment.

Stat page is updated as well with smart codec names and profiles.

Tuesday, November 26, 2013

Nginx-rtmp-module 1.0.7 with exec pull

I've merged updated exec engine with exec_pull enabled into master and tagged v1.0.7. Updated directives page now contains entries for new exec_pull and exec_options directives.

If you update from old version please make sure you reconfigure nginx. The new version has one file deleted, so you'll get an error otherwise.

Friday, November 22, 2013

MPEG-DASH live streaming in nginx-rtmp-module

I'm pleased to announce MPEG-DASH support in nginx-rtmp-module. Browser-side support in implemented in dash.js library.

Setting up nginx-rtmp-module


Check out dash branch of nginx-rtmp-module. Dash configuration is similar to HLS
location /dash {
  root /tmp;
}

...

application myapp {
  live on;
  dash on;
  dash_path /tmp/dash;
}
Dash module will create m4v, m4a, mpd files in its directory.

Setting up dash.js

  • Check out live branch of my dash.js fork
  • Set up nginx location serving dash.js contents and prepare streaming html. For this purpose modify (or copy) baseline.html in dash.js root and put your mpd (dash playlist) url in it instead of the default one
  • Open that html in browser and watch the stream published to nginx-rtmp-module


Issues

  • I only tested DASH streaming in Chrome, it seems other browsers are poorly supported now. However I need feedback about other browsers.
  • Video should be h264 encoded with Baseline profile. Other profiles will not work! Audio is AAC.
  • Vanilla dash.js does not play live streams well. I've submitted pull request to the project.

I want to thank Stephen Basile for his great work on dash module! He implemented all basic MPEG-DASH stuff including protocol manifest and mp4 writer.

Friday, November 8, 2013

Whois links on stat page

Today I have updated stat.xsl to generate RIPE Whois service links for each IP address on stat page. Until now client addresses where plain text. Now you can easily see where your clients come from.

Thursday, November 7, 2013

Disabling idle streams in nginx-rtmp-module

By default nginx-rtmp allows subscribers to connect to any (even nonexistent) stream within application. You can connect to a stream with no publisher in it. When publisher disconnects all subscribers remain connected. That can be controlled with on_play and on_update HTTP notifications. The most recent version of nginx-rtmp has one more way to limit subscribers in idle streams.

The new directive is idle_streams. Default value on is consistent with the current behavior. If turned off subscribers will not be able to connect to idle (not published to) streams and will be disconnected when stream publisher disconnects.

application live {
    live on;
    idle_streams off;
}

Sunday, November 3, 2013

Client timestamp display and verification in nginx-rtmp-module 1.0.6

In nginx-rtmp-module 1.0.6 current stream timestamp is returned in stat xml for each client entry. The value is the last timestamp of rtmp audio/video packet sent to that client. These are usually the same for live stream subscribers except for dead clients (the new feature makes it possible to find such clients). However with VOD streams the feature makes even more sense showing stream position for each client.

The timestamp value is sent with on_update notification as well. Now you can use this value on server side for stream status update & validation. You can limit playback duration for certain users returning HTTP 5xx error from on_update when timestamp exceeds predefined limit. Don't mess the new timestamp argument with the old time argument which is client connection time.

Nginx and module version in stat

New nginx-rtmp-module returns nginx and nginx-rtmp-module versions in nginx_version and nginx_rtmp_version nodes of stat xml document. Stylesheet stat.xsl is updated as well.

Tuesday, October 8, 2013

Exec pull feature in nginx-rtmp-module

I'm proud to announce the most wanted feature by module users - exec_pull. The old exec (now renamed to exec_push) runs external process when a client starts publishing. The new exec_pull starts external process when first subscriber connects to the stream and kills it when the last one disconnects. It's extremely useful if you don't want the stream to be always pulled and transcoded (exec_static) but only do that when there's at least one client.

Imagine you have many video sources and want to be able to watch those in browser. The sources have different formats and codecs so transcoding and reformatting is required. That's done by ffmpeg. With the current master you can use exec_static to run ffmpeg's all the time even when no one is watching. The best solution is setting up your system to keep ffmpeg running when there's at least one client. The new exec_pull directive does that.

The directive supports only the following substitutions
  • name - stream name
  • app - application name
All other variables supported by exec/exec_push are not supported in exec_pull because they are session-dependent.

The feature has the following limitations
  • It works properly only in single-worker mode. Nginx worker cannot make child ffmpeg connect to the same worker. If it connects to a different worker streaming will not be stable. To overcome the limitation please set up two nginx instances with the first one having only a single worker with exec_pull and pull the stream from it by the second nginx.
  • There's no way to pull/exec_pull the stream when HLS client connects.

The following example shows how to pull data from remote mpeg-ts stream with transcoding.
application myapp {
    live on;
    exec_pull ffmpeg -i http://example.com/video.ts -c:v copy 
                     -c:a libfaac -ar 44100 -ac 1 
                     -f flv rtmp://localhost/$app/$name;
}

The code is in exec-pull branch of nginx-rtmp-module.

Monday, October 7, 2013

Event HLS

HLS specification supports several playlist types specified with X-PLAYLIST-TYPE directive. When directive is absent live streaming is usually assumed which means client plays the stream from the current live position (several fragments to the end). There's another playlist type EVENT which tells the client always play the stream from the beginning. This type of playlist is useful for recording and streaming live events.

Since 1.0.5 version nginx-rtmp-module support HLS EVENT playlist type. The directive is

hls_type live|event

application mystream {
    live on;
    hls on;
    hls_path /tmp/hls;
    hls_playlist_length 1h;
    hls_type event;
}

Note however playlist length is not dynamic. Please make sure you have enough playlist length set with hls_playlist_length directive for the whole event.

Thursday, September 19, 2013

Metadata copy mode in nginx-rtmp-module version 1.0.4

The new version 1.0.4 of nginx-rtmp-module has new metadata sending mode. Previous versions could only send reconstructed metadata with predefined fields (height, width etc) or completely turn metadata off. Now it's possible to send exact copy of publisher metadata block. The block now can contain any data fields. All the fields will be passed to subscribers.

application /myapp {
    live on;
    meta copy;
}

The meta directive keeps old default value on which sends reconstructed metadata. For copy mode please specify meta copy explicitly.

Thursday, September 5, 2013

Support for _definst_ in nginx-rtmp-module

With current master it's a little easier to stream from Wowza/FMS. Now nginx-rtmp handles _definst_ in application names. The following lines are equivalent

ffplay rtmp://localhost/myapp/_definst_/mystream
ffplay rtmp://localhost/myapp/mystream

Thanks Sebastien Estienne for the patch.

Wednesday, August 28, 2013

HLS full urls in nginx-rtmp-module

In the latest master of nginx-rtmp-module I have added a new directive hls_base_url which makes it possible to have full urls in HLS playlists. With this feature enabled you can download playlist and play it locally since it references remote files. The feature is supported in master(variant) and slave playlists.


location /hls {
    root /tmp;
}
...
application mystream {
    live on;
    hls on;
    hls_path /tmp/hls;
    hls_base_url http://localhost:8080/hls/;
}

Here's the contents of HLS playlist for mystream stream

$ cat /tmp/hls/mystream.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:15
#EXT-X-DISCONTINUITY
#EXTINF:9.333,
http://localhost:8080/hls/mystream-0.ts
#EXTINF:7.167,
http://localhost:8080/hls/mystream-1.ts
#EXTINF:5.417,
http://localhost:8080/hls/mystream-2.ts
#EXTINF:5.500,
http://localhost:8080/hls/mystream-3.ts
#EXTINF:15.166,
http://localhost:8080/hls/mystream-4.ts
#EXTINF:9.584,
http://localhost:8080/hls/mystream-5.ts
#EXTINF:9.333,
http://localhost:8080/hls/mystream-6.ts

Fixed arutcam video streaming on Android 4.2

In version 1.9 of arutcam video streaming has been fixed. I tested the app on Sony Xperia with Android 4.2.2 and it could not stream video, only audio. Now it's fixed, please update the application.

Many small bugs are fixed in arutcam as well.

Tuesday, August 20, 2013

Big-endian architecture support in nginx-rtmp-module

In branch big-endian of nginx-rtmp-module there's experimental support for big-endian architectures like MIPS and big-endian ARM. If you have such devices (eg. routers) please try installing nginx-rtmp on them and report the result. I've tested live streaming which is OK. In theory everything should work fine, but I still need more responses before merging the branch

Monday, August 19, 2013

Camera auto-focus in arutcam 1.3

In version 1.3 of arutcam auto-focus support has been added. Video quality is now much better. This version will be available at Google Play in several hours. Please make sure you install the new version.

UPDATE
I have added camera selection (front/back) as well. The version number is 1.4, it will appear at Google Play soon.

UPDATE2
Several people reported camera lock problems. Please post details or send me email about that problem.

Sunday, August 18, 2013

Nginx-1.5.3 ipv6 compilation fix

I'm finally back from vacation. Sorry for not answering some emails.

I've fixed nginx-1.5.3 compilation with ipv6 enabled. It's enabled by default when compiling for ARM. Thanks for reporting the issue. The tag is v1.0.3.

Thursday, August 1, 2013

ArutCam - Android application for live streaming to nginx-rtmp-module

I'm proud to announce the first release of ArutCam - free Android application for live streaming to nginx-rtmp-module. You need at least Android 4.0 to install it.
The first thing to do after installing is setting RTMP URL to pubish stream to. Go to Settings/RTMP and enter the URL.
Use Publish button to start streaming.

In settings you can change video and audio parameters as well.
Download and install ArutCam on Google Play

Friday, July 26, 2013

Updated test flash RTMP publisher and player

I've updated the flash applets in test/rtmp-publisher directory. They are now a little smarter and simpler.

Monday, July 22, 2013

HLS variant playlist

In v1.0.2 of nginx-rtmp-module I've added support for HLS variant playlists. Now it's easy to create multi-bitrate HLS streams. The new directive is hls_variant.
hls_variant SUFFIX [PARAM]*;
where SUFFIX is used to match incoming stream name, PARAMs are values added to each variant playlist entry describing the entry. Examples are BANDWIDTH=xxxx or CODECS=yyyy.
http {
    listen 80;

    location /hls {
        types {
            application/vnd.apple.mpegurl m3u8;
            video/mp2t ts;
        }
        alias /tmp;
    }
}

rtmp {
    server {
        listen 1935;
        
        application src {
            live on;

            exec ffmpeg -i rtmp://localhost/src/$name
              -c:a libfdk_aac -b:a 32k  -c:v libx264 -b:v 128K -f flv rtmp://localhost/hls/$name_low
              -c:a libfdk_aac -b:a 64k  -c:v libx264 -b:v 256k -f flv rtmp://localhost/hls/$name_mid
              -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 512K -f flv rtmp://localhost/hls/$name_hi;
        }

        application hls {
            live on;

            hls on;
            hls_path /tmp/hls;
            hls_nested on;

            hls_variant _low BANDWIDTH=160000;
            hls_variant _mid BANDWIDTH=320000;
            hls_variant _hi  BANDWIDTH=640000;
        }
    }
}
If you stream incoming video with the following command
ffmpeg -i /var/videos/sintel.mp4 -c:a copy -c:v copy -f flv rtmp://localhost/src/sintel;
then the multi-bitrate HLS stream will be available at the following URL
ffplay http://localhost/hls/sintel.m3u8
This playlist references 3 playlist with low, medium and high qualities.

Wednesday, July 3, 2013

GStreamer and Raspberry Pi

GStreamer

For a long time I've been using only ffmpeg as streaming/encoding/decoding tool. But there's another well-known project GStreamer which can do a lot of things, even more than ffmpeg. Recently I've encountered a problem which GStreamer solves but ffmpeg does not.

Here's an example of GStreamer call capturing video and audio from webcam and publishing RTMP stream to server. The whole long argument is called GStreamer pipe. It consists of elements separated with "!". Elements receive input and produce output. For details please apply to GStreamer web site.

gst-launch-1.0 v4l2src  ! "video/x-raw,width=640,height=480,framerate=15/1" !
h264enc bitrate=1000 ! video/x-h264,profile=high ! h264parse ! queue !
flvmux name=mux alsasrc device=hw:1 ! audioresample ! audio/x-raw,rate=48000 !
queue ! voaacenc bitrate=32000 ! queue ! mux. mux. !
rtmpsink location=\"rtmp://example.com/myapp/mystream live=1\"

GStreamer rtmpsink plugin writes long timestamps in type-3 packets in a different way than ffmpeg rtmp engine. So the following directive is needed in nginx.conf when publishing with GStreamer.

publish_time_fix off;

Encoding & decoding on Raspberry Pi

Most people already known what Raspberry Pi is. It is a small and cheap credit-card sized ARM computer. One can easily install Linux on it. It recognizes USB devices well (webcams, 3G/4G modems etc) so streaming from Raspberry Pi is an interesting thing to try.

Raspberry Pi processor is too weak to encode h264. Successfully it has hardware h264 encoder & decoder. The only way to use it is through OpenMAX interface. Ffmpeg has no support for OpenMAX so we can only use GStreamer which has OpenMAX support in gst-omx project. Here's an example GStreamer pipeline streaming RTMP from Raspberry Pi to server.

gst-launch-1.0 v4l2src  ! "video/x-raw,width=640,height=480,framerate=15/1" !
omxh264enc target-bitrate=1000000 control-rate=variable !
video/x-h264,profile=high ! h264parse ! queue !
flvmux name=mux alsasrc device=hw:1 ! audioresample ! audio/x-raw,rate=48000 !
queue ! voaacenc bitrate=32000 ! queue ! mux. mux. !
rtmpsink location=\"rtmp://example.com/myapp/mystream live=1\"

Note omxh264enc element which is hardware h264 encoder. Software h264 encoder is called h264enc.

Please make sure you use the recent version of gst-omx (>= July 1). I've submitted a patch which fixes setting stream bitrate for Raspberry Pi OpenMAX. Without it you can only stream at a low bitrate about 100kpbs.

You need GStreamer version 1.0 for gst-omx to compile and work on Raspberry Pi.

Monday, June 24, 2013

Synchronization fix

Lately I've fixed an Audio-Video synchronization bug in nginx-rtmp. If publisher streams frames with non-strictly monotonic timestamps (that happens sometimes) then output timestamps become out of sync - Audio and Video do not match. Now it's fixed, please update from master.

Tuesday, June 18, 2013

Multi-worker statistics and control with nginx "per-worker-listener" patch

Today I've written a small nginx patch providing a feature that solves many problems in RTMP-HTTP integration namely statistics and control. Statistics and control is known to work bad in multi-worker mode since HTTP request goes to a random worker. So the client receives random worker statistics or performs a control action with a worker that didn't receive previous actions from user.

The patch is named per-worker-listener. It can be downloaded here. When applied to nginx it opens ports for connecting to the worker you want, not just a random worker. Each worker has its own port number so you can choose the port to connect. The new syntax extends listen directive with a new option per_worker. The port specified in listen directive becomes base port number for worker listeners.

listen 80; # usual listen directive
listen 9000 per_worker; # per-worker listener

With the above configuration 1st worker will listen to 9000 port, 2nd to 9001 port, 3rd to 9002 port etc. To make that work please turn off accept_mutex. Listeners with no per_worker work as usual.

events {
worker_connections 1024;
accept_mutex off;
}


The patch is especially useful with stat and control handlers of nginx-rtmp-module.

server {
listen 9000 per_worker;
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root /tmp;
}
location /control {
rtmp_control all;
}
}


Now see 3rd worker stats at the location http://localhost:9002/stat.

Thursday, June 13, 2013

Windows support in 1.0.1

I've updated the nginx-rtmp code to compile and run on MS Windows. The limitations are

  • execs are not supported

  • static pulls are not supported

  • auto_push is not supported


Moreover the code is now much more portable so it can probably compile on uncommon platforms not supported earlier.

UPD: Nginx for Windows compilation guide
Add RTMP module with the usual option --add=module=PATH/TO/nginx-rtmp-module

UPD2: You can use Microsoft Visual C++ 2010 Express to compile nginx. It's free. Download it from Microsoft web site.

Sunday, June 9, 2013

Secure links in nginx-rtmp

Many people ask me for secure link support in nginx-rtmp similar to nginx http secure links. The truth is no special support is needed to have that in nginx-rtmp. You can set secure links on on_connect, on_play or on_publish. I'll show how to do that for on_play. on_publish is very much the same, on_connect has no name argument, only app.



http {
    server {
        listen 8080;
        server_name localhost;

        location /on_play {

            # set connection secure link
            secure_link $arg_st,$arg_e;
            secure_link_md5 mysecretkey$arg_app/$arg_name$arg_e;

            # bad hash
            if ($secure_link = "") {
                return 501;
            }

            # link expired
            if ($secure_link = "0") {
                return 502;
            }

            return 200;
        }
    }
}
rtmp {
    server {
        listen 1935;
        notify_method get;

        # protected application
        application myapp {
            live on;
            on_play http://localhost:8080/on_play;
        }
    }
}


With the above configuration you cannot play any stream from myapp application without providing the right secure link.


> ffplay 'rtmp://localhost/myapp/mystream'
ffplay version 1.0.6 Copyright (c) 2003-2013 the FFmpeg developers
...
rtmp://localhost/myapp/mystream: Unknown error occurred


In error.log we have this message


notify: HTTP retcode: 5xx


Now let's construct valid secure RTMP link. Get current timestamp first and add 1 hour expire time to it.


> date +%s
1370777449
> echo $((1370777449+3600))
1370781049

Then construct the hash (watch config above for key)


> echo -n "mysecretkeymyapp/mystream1370781049" | openssl dgst -md5 -binary | 
         openssl enc -base64 | tr '+/' '-_' | tr -d '='
Mbjev5ld4mmCN00mwIqD7w


Now we have the hash. It's easy to construct the valid secure RTMP url.

> ffplay 'rtmp://localhost/myapp/mystream?e=1370781049&st=Mbjev5ld4mmCN00mwIqD7w'

Tuesday, June 4, 2013

Fixed build in nginx-1.5.1

According to nginx 1.5.1 changelist the bug with ssl requiring nginx-http_ssl_module when adding nginx-rtmp-module is now fixed. So nginx with nginx-rtmp-module should now build as before without any additional options. The bug has been introduced in 1.3.14. Thanks to Maxim Dounin for that fix.

Monday, May 27, 2013

VOD HLS in Nginx

Recently I have finished one project for Nginx Inc. The project is VOD HLS module making HLS stream from mp4 file. The module is fast and completely asynchronous. I think it is the fastest solution available. The module is easy to configure as well.

location / {
hls;
root /var/mp4;
}


Nginx VOD HLS module is proprietary. Please apply to Nginx Inc for more details.

Friday, May 17, 2013

VOD stat in 0.9.20

I've merged VOD statistics into master. Now you can see it on /stat page.

Tuesday, May 14, 2013

Optional HLS cleanup

Today I've added a new directive hls_cleanup toggling HLS fragment and playlist cleanup from nginx cache manager process. By default cleanup is on. Now you can turn it off and have plain MPEG-TS chunked recorder.


application myapp {
live on;
hls on;
hls_path /tmp/hsls;
hls_cleanup off;
}

Monday, May 13, 2013

Simple pull/push DNS load-balancing in 0.9.19

Simple DNS load-balancing is implemented in 0.9.19. When remote host name is resolved to multiple addresses then stream is pulled or pushed sequentially from all those addresses.


application myapp {
live on;
pull rtmp://video.example.com/someapp;
}


Suppose video.example is resolved to 192.168.0.1 and 192.168.0.2. When the first server is unavailable the next connection attempt will be made to the second server.

Dynamic pull and push in 0.9.19

Since 0.9.19 version of nginx-rtmp-module you can dynamically pull or push streams with on_play and on_publish. 3xx HTTP result code used to redirect current stream. Now if the new stream name is started with rtmp:// then it's supposed to be RTMP URL and relay (pull/push) is created for the current client.


http {
...
location /local_redirect {
rewrite ^.*$ newname? permanent;
}
location /remote_redirect {
# no domain name here, only ip
rewrite ^.*$ rtmp://192.168.1.123/someapp/somename? permanent;
}
...
}

rtmp {
...
application myapp1 {
live on;
# stream will be redirected to 'newname'
on_play http://localhost:8080/local_redirect;
}
application myapp2 {
live on;
# stream will be pulled from remote location
# requires nginx >= 1.3.10
on_play http://localhost:8080/remote_redirect;
}
...
}


In the example above myapp1 uses old redirect behavior, myapp2 shows the new feature.

In myapp2 application source stream name is not changed and pull is created for the source stream name. Later clients connecting to this stream will see the video pulled by the first client. That's identical to usual pull behavior. You can change it however by specifying notify_relay_redirect on. This will redirect pulled stream to a new (long url-like) local name. So the streams will be grouped on rtmp url.

on_publish does the same for push operation.

The feature has 2 limitations

  • RTMP URL should not contain domains names, only IP addresses

  • nginx version>=1.3.10 is required

Sunday, May 12, 2013

Time argument in on_update

New time argument has been added to on_update handler. It's the number of second since play/publish call. With this you can limit client playback or publish time.

Thursday, April 25, 2013

Updated HLS in 0.9.18

New HLS in 0.9.18 includes the following updates



  • Directory-per-stream mode: hls_nested on


  • Automatic asynchronous fragment erasing. Fragments and playlists are erased automatically after timeout has expired from nginx cache manager process


  • Fixed iPhone playback


  • Introduced HLS discontinuities on stream restart


  • Three fragment naming modes: hls_fragment_naming sequential|timestamp|system


  • Two fragment slicing modes: hls_fragment_slicing plain|aligned. Aligned mode creates ts fragments aligned to incoming timestamp boundaries.


  • Max fragment size limitation to escape giant fragments


  • Smaller fragment size


  • Fractional durations described in HLSv3 specs for better seeking


  • Fragment duration and discontinuity recovery on stream restart


  • Always new fragment files, no fragment reuse


Wednesday, April 24, 2013

Setting file name pattern in recorder

In the latest master it's possible to set strftime-compliant pattern when specifying record_suffix value.


recorder rec1 {
record all;
record_interval 5s;
record_suffix -%d-%b-%y-%T.flv;
record_path /tmp/rec;
}


This config will produce files of the form mystream-24-Apr-13-18:23:38.flv. Full list of supported strftime format options you can find on strftime man page.

Shell-style exec redirects in 0.9.17

The new feature is shell-style redirects in exec-family directives. Now you can save ffmpeg (or any other executed child) output in file and pass input to it (makes no sense with ffmpeg however).


application myapp {
live on;
exec ffmpeg -i rtmp://localhost/myapp/$name -c copy
-f flv rtmp://example.com/myapp/$name 2>>/var/log/ffmpeg-$name.log;
}


Now you have a single log file like /var/log/ffmpeg-mystream.log for each stream where you can find all ffmpeg output including errors. You can redirect stdout (1>/var/log/ffmpeg-$name.out) and any other stream (1>&2) as well. Both truncate > and append >> modes are supported as well as usual variable substitutions .

Tuesday, April 23, 2013

Updates and fixes in 0.9.16

Updates in version 0.9.16 of nginx-rtmp-module



  • New stat.xsl with smart byte counter and bandwidth numbers. Thanks to misiek08 for the contribution.



  • Fixed auto-pushing to cache manager and cache loader processes. Earlier versions used to write millions of errors when one of these processes is created by nginx. Now it's fixed.



  • Fixed exe_static and static pull auto-start. No need to wait until first network connection for them to start.



  • Fixed relays on FreeBSD. It's been reported that on some FreeBSD systems pull/push sometimes fail to connect to remote side. Now it's fixed.




There are two upcoming updates as well.



  • New version of VOD statistics in branch vod-stat2. Many people asked for this. I'm waiting for response on this feature before I merge it into master. It's now ready to be merged. I hope I'll have enough feedback to merge it until the branch gets too old.



  • Significantly improved version of HLS in hls-restart-on-audio branch. Many HLS updates have been implemented in this branch. iPhone playback has been improved. Fragment creation and erasing has been improved as well. I'll post more details about this later. You can try this branch and report the issues. I'm planning to merge it soon.


Friday, April 19, 2013

Done-family notifications

A little bug has been fixed today. When on_play/on_publish return error on_play_done/on_publish_done/on_done are not called anymore.

Sunday, April 14, 2013

RTMP notify redirects in 0.9.14

Recently I have committed a new feature in nginx-rtmp. The feature is called notify-redirects. HTTP notifications have been available in ngnx-rtmp for a long time. Unsuccessful (non-2xx) HTTP result code used to terminate RTMP connection.

Since version 0.9.14 notification HTTP result code 3xx redirects RTMP connection to another application/stream. Application redirect is done in case of on_connect, stream redirect is done in case of on_play and on_publish. Application/stream name should be returned in Location field of HTTP response header.

In this example fallback is returned as new application name instead of the one provided by RTMP client.

curl -v http://localhost:8080/on_connect?...
...
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.2.4
< Date: Sun, 14 Apr 2013 04:41:18 GMT
< Content-Type: text/html
< Content-Length: 184
< Connection: keep-alive
< Location: fallback
...


Redirects can be easily implemented in nginx. The following example implements on_connect handler checking for RTMP flashVer equal to my_secret_flashver and redirecting client to fallback application in case of failure.

http {
server {
listen 8080;
server_name localhost;
location /on_connect {
if ($arg_flashver != "my_secret_flashver") {
rewrite ^.*$ fallback? permanent;
}
return 200;
}
}
}


Rtmp config for this case.

rtmp {
server {
listen 1935;
notify_method get;
on_connect http://localhost:8080/on_connect;
application myapp {
# Live stream for good clients
live on;
}
application fallback {
# VOD video for bad clients
live on;
play /var/fallback;
}
}
}


The same way you can redirect streams with on_play and on_publish handlers.

PS: Several important issues have been fixed in HLS engine as well. Please make sure you use the latest code. Moreover new updated HLS engine is under development.

Friday, March 22, 2013

Native HLS in master

I have merged native HLS into master and tagged v0.9.13. Now you don't need ffmpeg for HLS to work. Just compile nginx with nginx-rtmp-module. No separate hls module exist anymore. All HLS code is included in main rtmp module.

Wednesday, March 20, 2013

Switching bitrates in RTMP

Today I have fixed an issue with RTMP bitrate switch for VOD streams. With JWPlayer version 6 it's possible to choose RTMP bitrate while playing stream in a way similar to youtube. I'll provide a small tutorial here how to set up this on your system.

smil

In latest master I have updated JWPlayer to version 6. This version supports the following config format.



<script type="text/javascript" src="/jwplayer/jwplayer.js"></script></pre>
<div id="container">Loading the player ...</div>
<script type="text/javascript">
jwplayer("container").setup({
sources:[
{
file:"/sintel.smil"
}
],
ga: {},
autostart: true,
width: 640,
height: 480,
primary: 'flash'
});
</script>


This configuration refers smil file setting different bitrates/qualities for the same video. Here's an example of sintel.smil

<smil>
<head>
<meta base="rtmp://localhost/vod/" />
</head>
<body>
<switch>
<video src="mp4:sintel-120p.mp4" height="120" />
<video src="mp4:sintel-240p.mp4" height="240" />
<video src="mp4:sintel-480p.mp4" height="480" />
</switch>
</body>
</smil>


Mp4 files for different bitrates can be created from the source sintel.mp4 with the following ffmpeg calls

ffmpeg -i sintel.mp4 -c:v libx264 -c:a copy -s 640x480 sintel-480p.mp4
ffmpeg -i sintel.mp4 -c:v libx264 -c:a copy -s 320x240 sintel-240p.mp4
ffmpeg -i sintel.mp4 -c:v libx264 -c:a copy -s 160x120 sintel-120p.mp4


The error fixed in 0.9.12 is nginx crash when switching stream quality in case VOD file is accessed remotely with http.

Thanks to fjyaniez for the report and config examples.

Wednesday, March 6, 2013

Tuesday, March 5, 2013

Native HLS in nginx-rtmp

Until now HLS implementation in nginx-rtmp made use of ffmpeg libs to create HLS fragments. That lead to such major problems as complicated compilation process, ffmpeg/avconv confusion, libavformat API limitations, uncontrollable memory allocations in ffmpeg libs etc. Besides ffmpeg may not be available on some architectures (embedded etc). That was all too serious to keep using those libs.

Today I'm introducing native HLS support. It seems to be significantly faster that before, and does not require any third-party libs. The new code in is native-hls branch. There's no separate HLS module anymore. HLS is included in main rtmp module as one of the features.

In addition to native mpeg-ts generation new HLS engine is more consistent with stream restarts.

Sunday, March 3, 2013

Appending recorded files in 0.9.11

Until v0.9.11 of nginx-rtmp recorder could only create multiple unique files per stream (record_unique option) or re-write the file with each stream restart. Now a new mode is supported - append mode. The new directive is record_append. With this setting on the flv file is appended on each stream start.


record all;
record_path /tmp/rec;
record_append on;


Note that stream timestamps at the border are written close to each other without any gap so that there's no suspension when watching the recorded stream.

Connection limiter in 0.9.9

Since v0.9.9 it's now possible to limit the number of simultaneous RTMP connections to nginx-rtmp. The new directive is max_connections. By default there's no limit on connection number.

max_connections 200;

Connection number is stored in shared memory so it affects all workers. Auto-push connections as well as relay connections (push/pull) are also limited by this engine.

Friday, February 22, 2013

Multiple locations for VOD in 0.9.8

Last week I've added support for multiple play locations in VOD engine. Until 0.9.8 play directive could only receive a single directory or http location to play from. Now it receives multiple locations. Playback is started from the first location where file is found.


application vod {
# search in /var/videos; in case of failure search in /www
play /var/videos /www;
}

application vod {
# search in local directories; if not found go to remote server
play /tmp/cache /var/videos http://example.com/videos;
}


Another new feature in 0.9.8 is play_local_path setting. This directive sets directory where remote files are copied after they are completely downloaded from http location. The setting is unset by default. This directory should be on the same device as play_temp_path setting which sets temporary location where remote files are downloaded. Default is /tmp.

With the new features it's easy to set up local cache for remote files.


application cached_vod {
# remote files are copied to /tmp/videos after download
# and played later from local directory until deleted manually

play_local_path /tmp/videos;
play /tmp/videos http://video.example.com/videos;
}

Wednesday, February 13, 2013

Picture in picture

Following the previous post about logo this post describes in short how to make picture-in-picture (PIP) with ffmpeg and set up nginx-rtmp to work with that.

PIP can be done with the same overlay ffmpeg filter as before. This is nginx.conf example merging two incoming streams and producing picture-in-picture result.


application myapp {
live on;
}

application big {
live on;
exec ffmpeg -i rtmp://localhost/big/$name -i rtmp://localhost/small/$name
-filter_complex "[0][1]overlay=10:10" -f flv rtmp://localhost/myapp/$name;
}

application small {
live on;
}


The incoming streams are published in big and small application. The result is published to myapp.

Publish Sintel to big.

ffmpeg -re -i /var/video/sintel.mp4 -c:v flv -s 800x600 -c:a libfaac -ar 22050 -ac 1
-f flv rtmp://localhost/big/mystream


Then publish Elephants dream to small.

ffmpeg -re -i /var/video/ed.avi -c:v flv -s 400x200 -c:a libfaac -ar 22050 -ac 1
-f flv rtmp://localhost/small/mystream


Here's the result.
pip

Note the result will not be available if one of incoming streams in unavailable.

Placing logo on video

It's really great to be able to add logo on your video. Here's a simple guide how to do that with ffmpeg and set up nginx-rtmp to add logo automatically.

Here's a simple mychan.png logo with transparent background made in GIMP.
mychan

What we want is to add logo on video that goes through nginx-rtmp. That can be done with the following ffmpeg overlay filter.

movie=/var/pictures/mychan.png[logo];[0][logo]overlay=0:70


Let's create a new application addlogo and add ffmpeg exec line to it. The result is published to myapp.

application myapp {
live on;
}

application addlogo{
live on;
exec ffmpeg -i rtmp://localhost/addlogo/$name
-vf "movie=/var/pictures/mychan.png[logo];[0][logo]overlay=0:70"
-c:v flv -f flv rtmp://localhost/myapp/$name;
}


Now start streaming sintel.

ffmpeg -re -i /var/video/sintel.mp4 -c:v flv -s 800x600 -c:a libfaac -ar 44100 -ac 1
-f flv rtmp://localhost/addlogo/mystream


Here's the result.
logo_on_stream

Sunday, February 10, 2013

Proper video-only playback and new defaults in 0.9.7

There have been problems with playing video-only (no audio) streams. With wait_key setting disabled several flash players used to wait forever when playing such stream. With the setting enabled it's much better but still it hangs sometimes. Now an empty audio frame is sent with the first video frame and that completely fixes the problem. That behavior is enabled with wait_video on directive. It's optional since it's not needed for audio-only streams.

All the above is true for flash clients only. Ffmpeg-family tools treat such streams in a completely different way. They wait for both (audio and video) streams to appear in RTMP stream until probesize bytes pass through. That setting can (and should) be reduced in real life.

Moreover since 0.9.7 version of nginx-rtmp-module new default values are set for several options:

  • wait_key is now ON by default. That's the most reasonable setting for most cases.

  • play_restart is now OFF by default. Several flash players stop streaming on NetStream.Play.Stop and do not resume it later on NetStream.Play.Start which is not what expected.

Monday, February 4, 2013

On_connect and on_disconnect in 0.9.6

Two new HTTP notifications were added in version 0.9.6 of nginx-rtmp-module: on_connect and on_disconnect.

The first one is called when client issues connect command. It receives all arguments coming with that command, namely app, flashver, swfurl, tcurl, pageurl, addr. In addition to that on_connect receives connection arguments which have never been available in previous versions if nginx-rtmp. Any HTTP result code different from 2xx is considered an error and client connection is closed. This notification can be used for user authorization.

Here's an example of passing connection argument with JWPlayer.


jwplayer("container").setup({
modes: [
{ type: "flash",
src: "/jwplayer/player.swf",
config: {
bufferlength: 1,
file: "mystream",
streamer: "rtmp://localhost/myapp?arg1=v1&arg2=v2",
provider: "rtmp",
autostart: "1"
}
}
]
});




Several Flash players can only pass connection arguments to server and never pass play/publish arguments. OSMF passes the same arguments to all calls.

The second notification on_disconnect is called when the connection is closed. It receives exactly the same set of arguments as on_connect. Note however on_dicsonnect is called on every disconnect event whether connect command was issued or not. The result code of this callback is not analyzed.

Both new callbacks should be added to server{} scope of nginx.conf and never to application{} scope. That's because client is still not connected to any application at the moment of on_connect and might have no application at the moment of on_disconnect. HTTP method is tuned with the previously introduced notify_method directive.


rtmp {
server {
listen 1935;
notify_method get;

on_connect http://localhost:8080/on_connect;
on_disconnect http://localhost:8080/on_disconnect;

application myapp {
live on;
}
}
}

Sunday, February 3, 2013

New static features in 0.9.5

Two new great features were merged into nginx-rtmp master on the weekend. The first is formerly announced static pull and the second is static exec.

Static pulls are pulls started at nginx startup. This makes it possible to generate HLS files without any dependence on rtmp clients currently playing the stream.


pull rtmp://remote.example.com/app/stream name=localstream static;


Static exec is a similar feature for running external programs at nginx startup. With this feature you can start streaming from an external source with ffmpeg.

exec_static ffmpeg -re -i /var/movies/movie.avi -c:v libx264
-c:a libmp3lame -ar 22050 -ac 1 -f flv rtmp://localhost/myapp/mystream;


The greatest thing that comes with static execs is the ability to receive data from webcam without running external scripts. Now it can all be done in nginx.conf.

exec_static ffmpeg -f video4linux2 -i /dev/video0 -c:v libx264
-an -f flv rtmp://localhost/myapp/mystream


The same applies to receiving stream from any remote source in any formats and codecs.

exec_static ffmpeg -i http://video.example.com/livechannel.ts -c copy
-f flv rtmp://localhost/myapp/mystream


While static exec feature works perfectly on Linux it has certain limitations on FreeBSD. Because of missing prctl syscall or any substitution there's no simple way to make child process terminated automatically when parent gets killed. When a single nginx worker in multi-worker configuration is killed on FreeBSD it's still possible that a new exec child will be started even if the previously started child is still alive. If you use that on FreeBSD make sure you never kill a single nginx worker and always kill all.

Thursday, January 31, 2013

No HLS cache

I've added the following line to HLS playlist

#EXT-X-ALLOW-CACHE:NO

This disables caching HLS files on client side.

Monday, January 28, 2013

Fixed nginx_status

Today I have fixed a small issue concerning wrong active connection number on nginx_status page. Before the fix that number used to grow with every rtmp connection.

Sunday, January 27, 2013

HLS continuous streaming

HLS is a sequence of MPEG-TS fragments and M3U8 playlist. Each fragment has its sequence number. Upon stream restart these sequence numbers used to start from beginning. Some players don't like that behavior and stop the stream. Besides some HLS files can remain unerased.

A new directive hls_continuous makes HLS generation more consistent. When stream is started nginx-rtmp parses the playlist and finds the last sequence number. Streaming is started from that sequence number.


hls on;
hls_path /tmp/hls;
hls_continuous on;


Ffplay/ffmpeg is completely happy with that. However VLC sometimes stops playing at fragment boundary.

The feature is off by default.

Saturday, January 26, 2013

Mp4 track selector

In version 0.9.2 of nginx-rtmp I have added track selector for mp4 playback. If your mp4 has multiple audio (or video) tracks then you can provide track index to play. Argument names are aindex and vindex.

Examples:

rtmp://localhost/vod/file.mp4?aindex=1

rtmp://localhost/vod/file.mp4?vindex=1

# ignore video track
rtmp://localhost/vod/file.mp4?vindex=-1

rtmp://localhost/vod/file.mp4?aindex=0&vindex=2

Wednesday, January 23, 2013

HLS audio synchronization

Sometimes when playing HLS on iPhone you can hear crackles. The reason for them is MPEG-TS timestamps have much higher resolution than RTMP timestamps. Nginx-rtmp has a built-in feature fixing that. The directive name is hls_sync. I have not announced this feature before. Now I have added it to directives page. Moreover the feature is on by default. So if you have crackles in HLS just update from master.

Wednesday, January 16, 2013

Locking files in recorder

Sometimes you need to find out which recorded files are ready for processing and which are still being recorded. A new feature record_lock makes that possible.


recorder myrec {
record all;
record_interval 30s;
record_path /tmp/rec;
record_unique on;
record_lock on;
}


File being recorded is exclusively locked with fcntl lock engine. You can easily check file lock status from elsewhere. On FreeBSD you can use flock tool to check that. On Linux flock and fcntl are unrelated so you're left with writing a simple script. Here's an example of isunlocked.py.


#!/usr/bin/python

import fcntl, sys

sys.stderr.close()
fcntl.lockf(open(sys.argv[1], "a"), fcntl.LOCK_EX|fcntl.LOCK_NB)


Use this script to check if file is currently being recorded.


for file in /tmp/rec/*; do
printf "$file : ";
if ~/isunlocked.py $file; then echo "ready"; else echo "locked"; fi;
done

Tuesday, January 15, 2013

HLS on Android

For Android devices to play HLS stream properly you have to assign application/vnd.apple.mpegurl content type to m3u8 playlist.


location /hls {
types {
application/vnd.apple.mpegurl m3u8;
}
alias /tmp/hls;
}


I want to thank module users in nginx-rtmp-ru google group for this solution.

Access log in nginx-rtmp 0.9

The latest feature implemented in nginx-rtmp is access logging. It looks similar to nginx HTTP access logging. Similar to HTTP case the logger is on by default. If not changed explicitly RTMP access logger shares access log file with nginx HTTP engine.


rtmp {
access_log logs/rtmp_access.log;
server {
...
}
}


Logging can be turned off:

access_log off;


RTMP access logger supports formatting with a number of predefined variables. The complete list of supported variables can be found on directives page in log_format directive description.


log_format new '$remote_addr $bytes_sent';
access_log logs/rtmp_access.log new;


Default format combined is supported as well. Here's the definition of this format:

$remote_addr [$time_local] $command "$app" "$name" "$args" -
$bytes_received $bytes_sent "$pageurl" "$flashver" ($session_readable_time)

Static pull feature in nginx-rtmp

Currently implemented pull/push relays are dynamic. They are created dynamically when there's at least one RTMP client playing/publishing the stream. That's useful for saving network bandwidth and handling undefined set of streams. However this implementation suffers from several problems

  • HLS is not generated until there's at least one RTMP client playing the pulled stream

  • In multi-worker mode each worker pulls its own stream from remote server

  • In multi-worker mode recording pulled stream and generating HLS from such stream leads to conflict between workers writing the same files


I've been asked many times to solve that, especially HLS/pull. That's hardly possible with dynamic relays. However not many people really need that kind of relays. In many cases It'd be enough to have static relays activated at nginx start with no dependence on RTMP clients. With static relays all above mentioned problems can easily be solved.

Static pulls are implemented in static-relay branch. To create one you need to add static keyword to pull directive argument list.

pull rtmp://remote.example.com/myapp/rstream pageUrl=ABC name=mystream static;

Such stream is started at nginx start and restarted on stream error. There's new pull_reconnect directive setting restart timeout. Unlike earlier you cannot omit stream name when creating static pull.

This feature will be later merged into master. I'm waiting for your feedback until then.