Giter Club home page Giter Club logo

nginx-rtmp-module's People

Contributors

arut avatar benmcmeen avatar bryan-stripe avatar carbon198 avatar chriswiggins avatar cryol avatar diegostamigni avatar fserreau avatar gamec avatar hannseman avatar heftig avatar itpp16 avatar itsgg avatar jbochi avatar jeffreywescott avatar karlisk avatar mainyaa avatar mattpepin avatar misiek08 avatar nl0 avatar petergeneric avatar premultiply avatar renevolution avatar rocfang avatar saintdev avatar sergey-dryabzhinsky avatar steelywing avatar stvad avatar trurlmcbyte avatar ut0mt8 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nginx-rtmp-module's Issues

Compiling nginx (release-1.25.1) with this module (v1.2.0) produce no HLS nor DASH streams

I needed to have variant streams for both HLS & DASH but since the original module from nginx doesn't support dash_variant I've tried to compile nginx from sources with this module

But no streams come out, even if I only try to use HLS, let alone both.

my configuration command line is:

./auto/configure --user=root --group=root \
                          --prefix=/usr/local/nginx --sbin-path=/usr/local/sbin/nginx \
                          --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf \
                          --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log \
                          --pid-path=/run/nginx.pid --lock-path=/var/lock/nginx.lock --http-client-body-temp-path=/tmp/nginx-client-body \
                          --http-fastcgi-temp-path=/usr/local/nginx/fastcgi_temp --http-proxy-temp-path=/usr/local/nginx/proxy_temp \
                          --http-scgi-temp-path=/usr/local/nginx/scgi_temp --http-uwsgi-temp-path=/usr/local/nginx/uwsgi_temp \
                          --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module \
                          --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module \
                          --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_slice_module \
                          --with-http_ssl_module --with-http_sub_module --with-http_stub_status_module --with-http_v2_module \
                          --with-http_secure_link_module --with-stream  --with-stream_realip_module \
                          --add-module=/home/iosif/tmp/2bdeleted/nginx-rtmp-module/nginx-rtmp-module 

Because I had a problem in compilation with gcc 7.5.0 (-Werror=implicit-fallthrough) after configuration of nginx I've modified the created Makefile instead of

CFLAGS =  -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g  -I/home/iosif/tmp/2bdeleted/nginx-rtmp-module/nginx-rtmp-module

to

CFLAGS =  -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Wextra -Wimplicit-fallthrough -g  -I/home/iosif/tmp/2bdeleted/nginx-rtmp-module/nginx-rtmp-module

my nginx.conf file is:

worker_processes auto;
rtmp_auto_push on;
rtmp_auto_push_reconnect 1s;

events {
    worker_connections 1024;
}

rtmp {
    server {
        listen 1935;
        chunk_size 4096;

        # Transcoding (ffmpeg needed)
        application live {
            live on;
            interleave off;
            record off;

            allow publish all;
            allow play all;

            exec_push ffmpeg -i rtmp://127.0.0.1:1935/live/$name
                  -c:v libx264 -b:v 128k -tune zerolatency -preset superfast -crf 23 -f flv rtmp://127.0.0.1:1935/hls/$name_low
                  -c:v libx264 -b:v 512k -tune zerolatency -preset superfast -crf 23 -f flv rtmp://127.0.0.1:1935/hls/$name_hi
                  -c:v libx264 -b:v 1920k -tune zerolatency -preset superfast -crf 23 -f flv rtmp://127.0.0.1:1935/hls/$name_hi_3
                  -c copy -f flv rtmp://127.0.0.1:1935/hls/$name_src

                  -c:v libx264 -b:v 1024K -bufsize 1024k -f flv rtmp://127.0.0.1:1935/dash/$name_hi
                  -c:v libx264 -b:v 832K -bufsize 832k -f flv rtmp://127.0.0.1:1935/dash/$name_med
                  -c:v libx264 -b:v 256K -bufsize 256k -f flv rtmp://127.0.0.1:1935/dash/$name_low;
        }

        application hls {
            live on;

            hls on;
            hls_allow_client_cache enabled;
            hls_path /streamServer/hls/;
            hls_playlist_length 20s;
            hls_fragment 3s;

            hls_variant _low BANDWIDTH=160000;
            hls_variant _hi  BANDWIDTH=448000
            hls_variant _hi_3  BANDWIDTH=2048000;
            hls_variant _src BANDWIDTH=4096000; # Source bitrate, source resolution

            hls_variant _default BANDWIDTH=500000;
       }

       application dash {
           live on;

           dash on;
           dash_path /streamServer/dash/;
           dash_fragment 3s;
           dash_playlist_length 20s;
           dash_cleanup on;
           dash_variant _low bandwidth="256000";
           dash_variant _med bandwidth="832000";
           dash_variant _hi bandwidth="1024000" max;
       }
    }
}

http {
    default_type application/octet-stream;
    sendfile        on;
    keepalive_timeout 65;
    server_tokens off;

    gzip on;
    gzip_vary on;
    gzip_disable "msie6";
    types_hash_max_size 2048;
    client_max_body_size 1m;

    server {
        listen      80;
        server_name 127.0.0.1;

        location /hls {
            # Disable cache
            #add_header Cache-Control no-cache;
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Expose-Headers' 'Content-Length';

            # allow CORS preflight requests
            if ($request_method = 'OPTIONS') {
                    add_header 'Access-Control-Allow-Origin' '*';
                    add_header 'Access-Control-Max-Age' 1728000;
                    add_header 'Content-Type' 'text/plain charset=UTF-8';
                    add_header 'Content-Length' 0;
                    return 204;
            }

            # Serve a manifest file that lists the available quality levels
            if ($request_uri ~* "(\d+p)\.m3u8$") {
                set $quality $1;
                rewrite ^(.*)$ /manifest/$quality/index.m3u8 break;
            }

            #server hls fragments
            types{
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }

            #expires -1;

            root /streamServer/;
       }

       location /dash {
          # Disable cache
          #add_header Cache-Control no-cache;
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Expose-Headers' 'Content-Length';

          # allow CORS preflight requests
          if ($request_method = 'OPTIONS') {
                  add_header 'Access-Control-Allow-Origin' '*';
                  add_header 'Access-Control-Max-Age' 1728000;
                  add_header 'Content-Type' 'text/plain charset=UTF-8';
                  add_header 'Content-Length' 0;
                  return 204;
          }

          types{
               application/dash+xml mpd;
               video/mp4 mp4;
          }

          #expires -1;

          root /streamServer/;
       }

       # This URL provides RTMP statistics in XML
       location /stat {
               rtmp_stat all;
               rtmp_stat_stylesheet stat.xsl; # Use stat.xsl stylesheet
       }
       location /stat.xsl {
               # XML stylesheet to view RTMP stats.
               root /usr/local/nginx/html;
       }
   }
}

I'm streaming from a USB camera connected on my PC using OBS and my system is Ubuntu. If I modify my nginx.conf to only have one DASH stream, keep the rest (regarding HLS) and use the original code of the module then everything works, I get one DASH stream & multiple HLS streams

Any idea?

nginx-rtmp-module without dash_variant

I'm using nginx-rtmp-module from https://github.com/ut0mt8/nginx-rtmp-module/ with the dash_variant ability.

My question is:
Can I use this fork of nginx-rtmp-module but without dash_variant configuration (as it should work with the original nginx-rtmp-module)?

I tried to remove the dash_variant property but the the directory /tmp/dash/{name} was empty (the directory was created but the mpd and the segments did not created).

Stream is constantly freezing

Good time of the day!

First of all, thank you for maintaining this project!
Now to the problem, it is actually a set of problems, which i have no idea where are they coming from.

Just a bit information about server:

  1. Intel Core i9-9900k
  2. 32GB 4600Mhz RAM
  3. NVMe SSD
  4. Ubuntu 18.10
  5. Nginx 1.15.9
  6. Module - latest

And here are the problems:

  1. Shaka Player (or any other player to be honest) constantly fetching the mpd file (sometimes twice or ever three times in a row without fetching segments)
  2. The duration of segments is always different no matter what i am doing (tried CBR, VBR, tried to lower the bitrate)
  3. Stream is starting just fine, but exactly 9 seconds later it just hangs, then 3 seconds later it works again, and after that, the next time it freezes, it will never resume (any player, you name it, videojs, shaka, dash, etc)

I've already tried many different solutions, but none of them are working. (I've started with the original module, and then found yours which even supports Dash QUALITY SWITCHER!)

These are my configuration files for the server itself and the www part of it.

rtmp-server-default.conf

server {
	listen 1935;
	ping 30s;
	notify_method get;
	notify_update_timeout 10s;
	chunk_size 4096;
	publish_time_fix off;

	application live {
		live on;

		# No RTMP playback
		deny play all;

		push rtmp://127.0.0.1:1935/dash-live;
		push_reconnect 1s;

		# Events
		on_publish http://app.local/api/stream/start;
		on_publish_done http://app.local/api/stream/stop;
		on_update http://app.local/api/stream/update;
	}

	application hls-live {
		live on;

		# No RTMP playback
		deny play all;

		# Only allow publishing from localhost
		allow publish 127.0.0.1;
		deny publish all;

		# Package this stream as HLS
		hls on;
		hls_path /tmp/hls/stream;

		# Put streams in their own subdirectory under `hls_path`
		hls_nested on;
		hls_fragment_naming system;
        }

	application dash-live {
		deny play all;
		allow publish 127.0.0.1;
		deny publish all;

		live on;
		dash on;
		dash_nested on; 
		dash_repetition on;
		dash_fragment 2;
		dash_playlist_length 60;
		dash_cleanup on;
		dash_clock_compensation http_head;
		dash_clock_helper_uri http://live.local/time;
		dash_path /tmp/dash/stream;
	}
}

So what i am doing here, first of all, i accept connections only to the live endpoint of the application, and there are few methods to check if user is allowed to stream, as well as update event and 'stream ended' event. The first idea was that these methods (especially update, because interval of 10 seconds is almost 9 seconds when stream freezes for the first time) is causing this issue, however, these are 'benchmark' results to see if there is any possible issue with them

[2019-03-20 15:31:26] local.INFO: array (
  'class' => 'App\\Classes\\Stream\\Manager',
  'method' => 'start',
  'execution_time' => 0.0056459903717041016,
)  
[2019-03-20 15:31:36] local.INFO: array (
  'class' => 'App\\Classes\\Stream\\Manager',
  'method' => 'update',
  'execution_time' => 0.00015020370483398438,
)  
[2019-03-20 15:31:46] local.INFO: array (
  'class' => 'App\\Classes\\Stream\\Manager',
  'method' => 'update',
  'execution_time' => 0.003345012664794922,
)  
[2019-03-20 15:31:47] local.INFO: array (
  'class' => 'App\\Classes\\Stream\\Manager',
  'method' => 'stop',
  'execution_time' => 0.000164031982421875,
)

Also, worth noting, that user just uses rtmp://live.local/live as the endpoint and provides the stream key, after that, the start method which is called on_publish returns 301 status code with location to the new stream name (to hide the stream key)

And here is the live.local.conf file

server {
	listen 80;
	listen [::]:80;

	root /var/www/data/#username#/www/streaming.live;

	index index.html index.htm index.nginx-debian.html;

	server_name live.local www.live.local;

	location /stat {
		rtmp_stat all;
		rtmp_stat_stylesheet original_stat.xsl;
	}

	location /control {
		rtmp_control all;
	}
	
	location /time {
		return 200;
	}

	location /stream {
		root /tmp/dash;
		add_header Access-Control-Allow-Origin * always;
		add_header Cache-Control no-cache always;
	}
}

Also, these are some of the global Nginx configuration variables:

user www-data;
worker_processes 1;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
load_module /usr/lib/nginx/modules/ngx_rtmp_module.so;

events {
    multi_accept        on;
    worker_connections  1024;
}

http {

	##
	# Basic Settings
	##

	charset			utf-8;
	sendfile		on;
	tcp_nopush		on;
	tcp_nodelay		on;
	server_tokens		off;
	log_not_found		off;
	types_hash_max_size	2048;
	client_max_body_size	2000M;
	keepalive_timeout	65;

	///////////////////////
}

rtmp {
	include /etc/nginx/rtmp-enabled/*;
}

Doesn't matter what i try, it still freezes exactly 9 seconds into the stream (according to the counter on the player which is Shaka)

Support with WebM

Hey,

How can I use different codec like vp9 for transcoding because when I use the following command:

ffmpeg -i rtmp://localhost:1935/$app/$name -c:v libvpx-vp9 -f webm rtmp://localhost/dash/$name_dash

The ffmpeg starts running in the backgroud but the /dash directory remains empty.

[DASH] Shaka fetching root manifest over and over again

Good time of the day,

Encountered an issue with Shaka Player and this module:

  1. Module generates quality for low/med/hi
  2. All directories created as they should and files are there
  3. Shaka always requesting root manifest

Nginx Config

user nginx;
worker_processes auto;
# error_log /var/log/nginx/debug.log debug;

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;

        location /live {
            alias /tmp/nginx/dash;
            add_header 'Access-Control-Allow-Origin' '*';
		    add_header Cache-Control no-cache always;
        
        }

        location /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet static/stat.xsl;
        }

        location /static {
            alias /var/www/static;
        }

        location /time {
            add_header 'Access-Control-Allow-Origin' '*';
            return 200;
        }

    }
}

rtmp {
    server {
        listen 1935;
        chunk_size 4000;
        ping 30s;
	    notify_method get;
	    notify_update_timeout 10s;
        publish_time_fix on;

        application live {
            live on;

            on_publish http://app.local/api/stream/start;
            on_publish_done http://app.local/api/stream/stop;
            on_update http://app.local/api/stream/update;

            exec ffmpeg -i rtmp://localhost:1935/live/$name
                -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 2500k -f flv -g 30 -r 30 -s 1280x720 -preset superfast -profile:v baseline -vsync cfr rtmp://localhost:1935/dash/$name_hi
                -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 1000k -f flv -g 30 -r 30 -s 854x480 -preset superfast -profile:v baseline -vsync cfr rtmp://localhost:1935/dash/$name_med
                -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 750k -f flv -g 30 -r 30 -s 640x360 -preset superfast -profile:v baseline -vsync cfr rtmp://localhost:1935/dash/$name_low;
        }

        application dash {
            live on;
		    dash on;

            dash_repetition on;
            dash_fragment 2;
            dash_playlist_length 30;
            dash_cleanup on;
            dash_nested on;
            dash_clock_compensation http_head;
            dash_clock_helper_uri http://live.local/time;
            dash_path /tmp/nginx/dash;

            dash_variant _low bandwidth="878000" width="640" height="360";
            dash_variant _med bandwidth="1128000" width="854" height="480";
            dash_variant _hi bandwidth="2628000" width="1280" height="720" max;
        }

    }
}

And this is the generated manifest

<?xml version="1.0"?>
<MPD
    type="dynamic"
    xmlns="urn:mpeg:dash:schema:mpd:2011"
    xmlns:cenc="urn:mpeg:cenc:2013"
    xmlns:mspr="urn:microsoft:playready"
    availabilityStartTime="2019-03-31T23:13:32Z"
    publishTime="2019-03-31T23:13:32Z"
    minimumUpdatePeriod="PT2.046S"
    minBufferTime="PT2.000S"
    timeShiftBufferDepth="P0Y00M00DT0H00M08.046S"
    suggestedPresentationDelay="PT5.092S"
    profiles="urn:hbbtv:dash:profile:isoff-live:2012,urn:mpeg:dash:profile:isoff-live:2011"
    xmlns:xsi="http://www.w3.org/2011/XMLSchema-instance"
    xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd">
  <Period start="PT0S" id="dash">
    <AdaptationSet
        id="1"
        startWithSAP="1"
        segmentAlignment="true"
        maxWidth="1280"
        maxHeight="720"
        maxFrameRate="30"
        par="16:9">
      <Representation
          id="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_low_H264"
          mimeType="video/mp4"
          codecs="avc1.42c01f"
          sar="1:1"
          bandwidth="878000"
          width="640"
          height="360"
          >
        <SegmentTemplate
            presentationTimeOffset="0"
            timescale="1000"
            media="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_low/$Time$.m4v"
            initialization="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_low/init.m4v">
          <SegmentTimeline>
             <S t="0" d="2046"/>
             <S t="2046" d="2000" r="2"/>
          </SegmentTimeline>
        </SegmentTemplate>
      </Representation>
      <Representation
          id="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_med_H264"
          mimeType="video/mp4"
          codecs="avc1.42c01f"
          sar="1:1"
          bandwidth="1128000"
          width="854"
          height="480"
          >
        <SegmentTemplate
            presentationTimeOffset="0"
            timescale="1000"
            media="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_med/$Time$.m4v"
            initialization="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_med/init.m4v">
          <SegmentTimeline>
             <S t="0" d="2046"/>
             <S t="2046" d="2000" r="2"/>
          </SegmentTimeline>
        </SegmentTemplate>
      </Representation>
      <Representation
          id="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_hi_H264"
          mimeType="video/mp4"
          codecs="avc1.42c01f"
          sar="1:1"
          bandwidth="2628000"
          width="1280"
          height="720"
          >
        <SegmentTemplate
            presentationTimeOffset="0"
            timescale="1000"
            media="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_hi/$Time$.m4v"
            initialization="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_hi/init.m4v">
          <SegmentTimeline>
             <S t="0" d="2046"/>
             <S t="2046" d="2000" r="2"/>
          </SegmentTimeline>
        </SegmentTemplate>
      </Representation>
    </AdaptationSet>
    <AdaptationSet
        id="2"
        startWithSAP="1"
        segmentAlignment="true">
      <AudioChannelConfiguration
          schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011"
          value="1"/>
      <Representation
          id="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_hi_AAC"
          mimeType="audio/mp4"
          codecs="mp4a.40.2"
          audioSamplingRate="44100"
          bandwidth="125000">
        <SegmentTemplate
            presentationTimeOffset="0"
            timescale="1000"
            media="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_hi/$Time$.m4a"
            initialization="852efbfca498c8fddc214c2ae2ab0991973d97339df92fab80ab7d570e449a6ec1ec80502d8d9cc97a56d04b2b3e942e94ad07857a48d055ce011f71060c03fd_hi/init.m4a">
          <SegmentTimeline>
             <S t="0" d="2046"/>
             <S t="2046" d="2000" r="2"/>
          </SegmentTimeline>
        </SegmentTemplate>
      </Representation>
    </AdaptationSet>
  </Period>
  <UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-head:2014"
       value="http://live.local/time" />
</MPD>

Actually, same goes for VLC, it just cannot play the playlist

Not working

I tried your fork of Nginx rtmp module.
Nginx version - 1.10.3

I used the example configuration in the read me with dash_path /tmp/dash. I created an RTMP stream from OBS, with the stream the url rtmp://:1935/ingest/test. When I took a look at /tmp/dash, it was empty.

I later compiled and built the rtmp module with --with-debug flag. I performed the above steps later. There were no logs from nginx rtmp

Time Base

I've been testing this and I have a 30000/1001 scan rate stream where the rate is being written with a 1000 frame base:

frameRate="29969/1000"
timescale="1000"

I'm trying to figure out why player.time() in dash.js does not match the actual time of the video. Doing a player.seek( 0 ) seems to have dash.js loose it's marbles as to where it is at, and I was wondering if the timescale at 1000 rather than 1001 might contribute to that.

Question SCTE35

You implemented cuepoints and or SCTE35 signals in this repo?

Dash manifest with repetition :

static u_char *
ngx_rtmp_dash_write_segment(u_char *p, u_char *last, ngx_uint_t t, ngx_uint_t d, ngx_uint_t r)
{
#define NGX_RTMP_DASH_MANIFEST_TIME                                            \
    "             <S t=\"%uD\" d=\"%uD\"/>\n"

#define NGX_RTMP_DASH_MANIFEST_TIME_WITH_REPETITION                            \
    "             <S t=\"%uD\" d=\"%uD\" r=\"%uD\"/>\n"

    if (r == 0) {
        p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME,
                         t, d);
    } else {
        p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME_WITH_REPETITION,
                         t, d, r);
    }

    return p;
}

static u_char *
ngx_rtmp_dash_write_segment_timeline(ngx_rtmp_session_t *s, ngx_rtmp_dash_ctx_t *ctx, u_char *p, u_char *last)
{

    ngx_uint_t              i, t, d, r;
    ngx_rtmp_dash_frag_t    *f;

    for (i = 0; i < ctx->nfrags; i++) {
        f = ngx_rtmp_dash_get_frag(s, i);

        if (i == 0) {
            t = f->timestamp;
            d = f->duration;
            r = 0;
        } else {
            if (f->duration == d) {
                r++;
            } else {
                p = ngx_rtmp_dash_write_segment(p, last, t, d, r);
                t = f->timestamp;
                d = f->duration;
                r = 0;
            }
        }

        if (i == ctx->nfrags - 1) {
            p = ngx_rtmp_dash_write_segment(p, last, t, d, r);
        }
    }

    return p;
}


[RTMP] "too big message" (on big-endian platforms)

Big-endian platforms (usually running OpenWrt) have a known issue in RTMP code. ARUT has started to deal with it in branch "big-endian" but never finished it. His changes never got into master. There are at least 3 open issues in his repo (sergey-dryabzhinsky#324 arut#576 arut#739) that describe the problem. I can confirm that package nginx-all-module in Openwrt 19.07.2 uses your source code (ut0mt8/nginx-rtmp-module) and still has the problem on MIPS. Log from nginx:

2020/05/12 21:20:52 [info] 14610#0: *9 client connected '192.168.11.41'
2020/05/12 21:20:52 [info] 14610#0: *9 too big message: 67108864 > 20971520 , client: 192.168.11.41, server: 0.0.0.0:1935
2020/05/12 21:20:52 [info] 14610#0: *9 disconnect, client: 192.168.11.41, server: 0.0.0.0:1935
2020/05/12 21:20:52 [info] 14610#0: *9 deleteStream, client: 192.168.11.41, server: 0.0.0.0:1935

I create this Issue to document current status. Suggested course of action is to review rtmp code and make it endian-agnostic. Unfortunately I have no time to do that now. It's a pity ARUT hasn't started using NTOH/HTON from the beginning.

Is the DASH implementation more robust (eg. is it tested on big-endian machines)?

scte35 inband supports.

I tried to use scte35 inband feature this fork. But it seems this doesnt work by default. Can you share me some stream that having scte35 event inside, or how can I make it works.
Thanks in advance
Tam

Something is broken and after 2-4 seconds, ffmpeg launch "broken pipe"

I gave a try to your fork, cause I'm very interested in ABR on DASH (dash_variant) that you implement in your fork. However, where Arut source and Sergey fork works fine, your fork fails, and that made me crazy cause I didn't think the problem was in your module. Please, take a look. Meanwhile, I continue using Sergey's fork.

Thank you

av_interleaved_write_frame(): Broken pipe
Error writing trailer

This is the complete output from ffmpeg

ffmpeg -re -f lavfi -i 'testsrc=size=1280x720:rate=24'  -f lavfi -i "sine=frequency=1000:sample_rate=44100,volume=0:enable='gt(t-trunc(t),0.25)'" -filter_complex "format=yuv420p, drawtext=fontcolor=white: fontsize=60: fontfile=/usr/share/fonts/truetype/LiberationMono-Bold.ttf: box=1:[email protected]: shadowcolor=black: shadowx=1: shadowy=1: x=(w-text_w-10):y=(h-text_h-5): timecode='00\:00\:00\:00': timecode_rate=24" -r 24 -c:v libx264 -profile:v baseline -level 3.0 -x264opts 'keyint=48:min-keyint=24:no-scenecut' -g 48 -keyint_min 24 -sc_threshold 0 -preset veryfast  -c:a aac -b:a 128k -ar 44100 -tune zerolatency -movflags +faststart -f flv "rtmp://ip_of_nginx_server/live/directo" 
ffmpeg version 3.2.4 Copyright (c) 2000-2017 the FFmpeg developers
  built with gcc 6 (SUSE Linux)
  configuration: --prefix=/usr --libdir=/usr/lib64 --shlibdir=/usr/lib64 --incdir=/usr/include/ffmpeg --extra-cflags='-fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -g' --optflags='-fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -g' --disable-htmlpages --enable-pic --disable-stripping --enable-shared --disable-static --enable-gpl --disable-openssl --enable-avresample --enable-libcdio --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libcelt --enable-libcdio --enable-libdc1394 --enable-libfreetype --enable-libgsm --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libschroedinger --enable-libspeex --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libwebp --enable-netcdf --enable-vaapi --enable-vdpau --enable-libfdk_aac --enable-nonfree --enable-libmp3lame --enable-libtwolame --enable-libx264 --enable-libx265 --enable-libxvid --enable-x11grab
  libavutil      55. 34.101 / 55. 34.101
  libavcodec     57. 64.101 / 57. 64.101
  libavformat    57. 56.101 / 57. 56.101
  libavdevice    57.  1.100 / 57.  1.100
  libavfilter     6. 65.100 /  6. 65.100
  libavresample   3.  1.  0 /  3.  1.  0
  libswscale      4.  2.100 /  4.  2.100
  libswresample   2.  3.100 /  2.  3.100
  libpostproc    54.  1.100 / 54.  1.100
Input #0, lavfi, from 'testsrc=size=1280x720:rate=24':
  Duration: N/A, start: 0.000000, bitrate: N/A
    Stream #0:0: Video: rawvideo (RGB[24] / 0x18424752), rgb24, 1280x720 [SAR 1:1 DAR 16:9], 24 tbr, 24 tbn, 24 tbc
Input #1, lavfi, from 'sine=frequency=1000:sample_rate=44100,volume=0:enable='gt(t-trunc(t),0.25)'':
  Duration: N/A, start: 0.000000, bitrate: 1411 kb/s
    Stream #1:0: Audio: pcm_f32le, 44100 Hz, mono, flt, 1411 kb/s
[libx264 @ 0x847320] using SAR=1/1
[libx264 @ 0x847320] frame MB size (80x45) > level limit (1620)
[libx264 @ 0x847320] MB rate (86400) > level limit (40500)                                                                  
[libx264 @ 0x847320] using cpu capabilities: MMX2 SSE2Fast SSSE3 Cache64 SlowShuffle
[libx264 @ 0x847320] profile Constrained Baseline, level 3.0
[libx264 @ 0x847320] 264 - core 148 - H.264/MPEG-4 AVC codec - Copyleft 2003-2016 - http://www.videolan.org/x264.html - options: cabac=0 ref=1 deblock=1:0:0 analyse=0x1:0x111 me=hex subme=2 psy=1 psy_rd=1.00:0.00 mixed_ref=0 me_range=16 chroma_me=1 trellis=0 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=0 threads=2 lookahead_threads=2 sliced_threads=1 slices=2 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=0 weightp=0 keyint=48 keyint_min=24 scenecut=0 intra_refresh=0 rc=crf mbtree=0 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, flv, to 'rtmp://10.162.0.30/live/directo':
  Metadata:
    encoder         : Lavf57.56.101
    Stream #0:0: Video: h264 (libx264) ([7][0][0][0] / 0x0007), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], q=-1--1, 24 fps, 1k tbn, 24 tbc
    Metadata:
      encoder         : Lavc57.64.101 libx264
    Side data:
      cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: -1
    Stream #0:1: Audio: aac (LC) ([10][0][0][0] / 0x000A), 44100 Hz, mono, fltp, 128 kb/s
    Metadata:
      encoder         : Lavc57.64.101 aac
Stream mapping:
  Stream #0:0 (rawvideo) -> format (graph 0)
  drawtext (graph 0) -> Stream #0:0 (libx264)
  Stream #1:0 -> #0:1 (pcm_f32le (native) -> aac (native))
Press [q] to stop, [?] for help
av_interleaved_write_frame(): Broken pipeB time=00:00:03.58 bitrate= 453.1kbits/s speed=   1x
Error writing trailer of rtmp://10.162.0.30/live/directo: Broken pipeframe=   98 fps= 24 q=21.0 Lsize=     237kB time=00:00:04.06 bitrate= 477.3kbits/s speed=   1x
video:213kB audio:19kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 2.292459%
[libx264 @ 0x847320] frame I:3     Avg QP: 5.33  size: 18520
[libx264 @ 0x847320] frame P:95    Avg QP: 8.33  size:  1701
[libx264 @ 0x847320] mb I  I16..4: 91.0%  0.0%  9.0%
[libx264 @ 0x847320] mb P  I16..4:  7.5%  0.0%  0.1%  P16..4:  3.4%  1.2%  0.1%  0.0%  0.0%    skip:87.7%
[libx264 @ 0x847320] coded y,uvDC,uvAC intra: 2.8% 21.0% 13.7% inter: 0.3% 1.8% 0.2%
[libx264 @ 0x847320] i16 v,h,dc,p: 90%  6%  1%  3%
[libx264 @ 0x847320] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 48% 26% 18%  2%  1%  2%  1%  2%  1%
[libx264 @ 0x847320] i8c dc,h,v,p: 23%  7% 59% 11%
[libx264 @ 0x847320] kb/s:425.52
[aac @ 0x84abe0] Qavg: 55608.855
Conversion failed!

[DASH] Is it possible to pass "source" quality

So, the title states it, is it possible to parse source quality without encoding?

Obviously, i have no idea on which bitrates and framerates use might use, so i cannot simply define the bitrate in the dash_variant, yet i can simply "pass" the stream through without encoding.

dash master manifest is not created

I already managed to create multiple bitrate from rtmp source using ffmpeg command, but while hls is okay, everything works fine, our dash doesn't have .mpd manifest file which contains all bitrate, only have nested folder for each bitrate. what do we need to make main manifest file generated?

worker_processes  auto;
events {
    # Allows up to 1024 connections, can be adjusted
    worker_connections  1024;
}

# RTMP configuration
rtmp {
    server {
        listen 1935; # Listen on standard RTMP port
        chunk_size 4000; 
        
        # This application is to accept incoming stream
        application live {
            live on; # Allows live input
            
            # Once receive stream, transcode for adaptive streaming
            # This single ffmpeg command takes the input and transforms
            # the source into 4 different streams with different bitrate
            # and quality. P.S. The scaling done here respects the aspect
            # ratio of the input.
            exec ffmpeg -i rtmp://localhost/$app/$name -async 1 -vsync -1
                        -c:v libx264 -c:a copy -b:v 256k -b:a 32k -vf "scale=480:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/show/$name_low
                        -c:v libx264 -c:a copy -b:v 768k -b:a 96k -vf "scale=720:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/show/$name_mid
                        -c:v libx264 -c:a copy -b:v 1024k -b:a 128k -vf "scale=960:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/show/$name_high
                        -c:v libx264 -c:a copy -b:v 1920k -b:a 128k -vf "scale=1280:trunc(ow/a/2)*2" -tune zerolatency -preset veryfast -crf 23 -f flv rtmp://localhost/show/$name_hd720;
        }
        
        # This application is for splitting the stream into HLS fragments
        application show {
            live on; # Allows live input from above
            hls on; # Enable HTTP Live Streaming
	    
	    dash on;
	    #dash_nested on;
	    dash_repetition on;
	    dash_path /home/jenkins/tmp/dash/;
            
            # Pointing this to an SSD is better as this involves lots of IO
            hls_path /home/jenkins/tmp/hls/;
            
            # Instruct clients to adjust resolution according to bandwidth
            hls_variant _low BANDWIDTH=288000; # Low bitrate, sub-SD resolution
            hls_variant _mid BANDWIDTH=448000; # Medium bitrate, SD resolution
            hls_variant _high BANDWIDTH=1152000; # High bitrate, higher-than-SD resolution
            hls_variant _hd720 BANDWIDTH=2048000 max; # High bitrate, HD 720p resolution

            dash_variant _low BANDWIDTH=288000; # Low bitrate, sub-SD resolution
            dash_variant _mid BANDWIDTH=448000; # Medium bitrate, SD resolution
            dash_variant _high BANDWIDTH=1152000; # High bitrate, higher-than-SD resolution
            dash_variant _hd720 BANDWIDTH=2048000 max; # High bitrate, HD 720p resolution
        }
    }
}

http {
    # See http://licson.net/post/optimizing-nginx-for-large-file-delivery/ for more detail
    # This optimizes the server for HLS fragment delivery
    sendfile off;
    tcp_nopush on;
    directio 512;
    
    # HTTP server required to serve the player and HLS fragments
    server {
        listen 80;

	location /dash {
            types {
                application/dash+xml mpd;
            }
            
            root /home/jenkins/tmp/;
            add_header Cache-Control no-cache; # Prevent caching of HLS fragments
            add_header Access-Control-Allow-Origin *; # Allow web player to access our playlist
        }
        
        location /hls {
            types {
                application/vnd.apple.mpegurl m3u8;
            }
            
            root /home/jenkins/tmp/;
            add_header Cache-Control no-cache; # Prevent caching of HLS fragments
            add_header Access-Control-Allow-Origin *; # Allow web player to access our playlist
        }
    }
}

our result look like this

jenkins@srs-live-16:~/tmp$ ls dash/ | grep mpd
teststream_hd720.mpd
teststream_high.mpd
teststream_low.mpd
teststream_mid.mpd

 ls hls/
teststream.m3u8        
teststream_hd720-1.ts  
teststream_high-0.ts  
teststream_high.m3u8  
teststream_low-1.ts  
teststream_mid-0.ts  
teststream_mid.m3u8
teststream_hd720-0.ts  
teststream_hd720.m3u8  
teststream_high-1.ts  
teststream_low-0.ts   
teststream_low.m3u8  
teststream_mid-1.ts

DASH streams not working properly (dash.js/shaka player/rx player)

Hello,

Thanks for your fork, it seems it's been a lot of work to fix the DASH issues upstream so I'm thankful for your work.

I tried your latest dev branch against nginx/1.14.1 and there are all kind of erratic behaviors with different players.

Shaka player doesn't start unless I revert the commit that changes availabilityStartTime because Shaka player seems to expect an UTC time. If not, it'll try to seek hours into the future and never start. That said, it will have short stalls on some browsers because it find some millisecond gaps in the stream (according to the log).

dash.js is weird because it sometimes will load properly, but other times will load with very big delay times (over 100 seconds). Sometimes it loads with 150s or even 200s of delay. Of course because the playlist is only 120s, the fragments don't exist so it doesn't play.

Have you experienced any of this issues? Is it a problem with my configuration?

I can provide logs. Here's one snapshot of the mpd:
http://tv.bienvenidoainternet.org/snapshot.mpd

Thanks.

time_iso8601 variable not available

Do you have any suggestion for my issue?

Expected Behavior

time_iso8601 available in nginx rtmp module

Actual Behavior

Variable time_iso8601 not available in rtmp module

sudo /usr/local/nginx/sbin/nginx -t
nginx: [emerg] unknown variable "time_iso8601" in /usr/local/nginx/conf/nginx.conf:53
nginx: configuration file /usr/local/nginx/conf/nginx.conf test failed

Steps to Reproduce the Problem

  1. I use time_iso8601 as part of logging properties
  2. time_iso8601 used in log_format directive
rtmp {
   log_format rtmpnginxjson escape=json '{'
	'"@timestamp":"$time_iso8601",'
	'"component":"my_rtmp_app",'
	'"type":"nginxrtmpaccess",'
	'"hostname":"localhost",'
	'"ip":"0.0.0.0",'
	'"client_ip":"$remote_addr",'
	'"connection_number":"$connection",'
	'"application_name":"$app",'
	'"stream_name":"$name",'
	'"command":"$command",'
	'"bytes_sent":"$bytes_sent",'
	'"bytes_received":"$bytes_received",'
	'"session_time":"$session_time",'
	'"session_readable_time":"$session_readable_time",'
	'"msec":"$msec"'
	'}';
    server {
    access_log /usr/local/nginx/logs/rtmp-access.log rtmpnginxjson;
    listen 1935;
    chunk_size 4096;

    application ingest {
            allow play 127.0.0.1;
      live on;
      meta on;

            exec /usr/local/bin/ffmpeg -i "rtmp://localhost:1935/$app/$name timeout=5" -xerror
            -pix_fmt yuv420p -c:v libx264 -tune zerolatency -x264opts keyint=60:min-keyint=60:no-scenecut:bframes=3:b-adapt=2:ref=3:crf=27:vbv-maxrate=413:vbv-bufsize=375 -s 416x240 -profile:v high -preset faster -b:a 96k -c:a libfdk_aac -sws_flags bilinear -f flv rtmp://localhost:1936/hls/${app}_${name}_lowest
      ;
      
          }
  }
}
  1. Test nginx configuration

Specifications

  • Nginx version: 1.8.1
  • Nginx rtmp module: f0ea623
  • Compilation options: ./configure --prefix=/usr/local/nginx --with-file-aio --with-http_ssl_module --add-module=/tmp/nginx-rtmp-module

Audio only representation

I'm testing your module, but it seems it's not capable of handling audio only streaming with multiple representations. If i try to fool it and write some dummy width & height parameters, it only writes a single audio representation out (the one flagged with "max").

My config:

events {
    worker_connections  1024;
}

error_log  logs/error.log  info;
user root;

# RTMP configuration
rtmp {
    server {
        listen 1935;

        application live {
            live on;

            exec /usr/local/bin/ffmpeg -i rtmp://localhost/$app/$name -async 1
                        -vn -ar 32000 -c:a libfdk_aac -profile:a aac_he_v2 -b:a 22k -f flv rtmp://localhost/show/$name_low
                        -vn -ar 32000 -c:a libfdk_aac -profile:a aac_he -b:a 56k -f flv rtmp://localhost/show/$name_mobile
                        -vn -ar 44100 -c:a libfdk_aac -cutoff 16000 -b:a 128k -f flv rtmp://localhost/show/$name_standard
                        -vn -ar 44100 -c:a libfdk_aac -cutoff 20000 -b:a 256k -f flv rtmp://localhost/show/$name_hq
                        -f flv rtmp://localhost/show/$name_src 2>>/tmp/log;
        }

        application show {
            live on;
            dash on;
            dash_nested on;

            dash_cleanup on;
            dash_fragment 2;
            dash_playlist_length 120;
            dash_variant _low bandwidth="22000" width="1" height="1";
            dash_variant _mobile bandwidth="56000" width="2" height="2";
            dash_variant _standard bandwidth="128000" width="3" height="3";
            dash_variant _hq bandwidth="256000" width="4" height="4" max;

            dash_path /tmp/nginx/dash/;
        }
    }
}

http {
    server {
        listen 8080;

        location /dash {
            alias /tmp/nginx/dash;
            add_header Cache-Control no-cache;
            add_header Access-Control-Allow-Origin *;
        }
    }
}

The resulting variant mpd file looks like this:

<?xml version="1.0"?>
<MPD
    type="dynamic"
    xmlns="urn:mpeg:dash:schema:mpd:2011"
    availabilityStartTime="2017-07-29T13:08:58Z"
    publishTime="2017-07-29T13:08:58Z"
    minimumUpdatePeriod="PT2.021S"
    minBufferTime="PT2.000S"
    timeShiftBufferDepth="P0Y00M00DT0H02M01.208S"
    suggestedPresentationDelay="PT5.042S"
    profiles="urn:hbbtv:dash:profile:isoff-live:2012,urn:mpeg:dash:profile:isoff-live:2011"
    xmlns:xsi="http://www.w3.org/2011/XMLSchema-instance"
    xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd">
  <Period start="PT0S" id="dash">
    <AdaptationSet
        id="2"
        startWithSAP="1"
        segmentAlignment="true">
      <AudioChannelConfiguration
          schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011"
          value="1"/>
      <Representation
          id="demo_hq_AAC"
          mimeType="audio/mp4"
          codecs="mp4a.40.2"
          audioSamplingRate="44100"
          bandwidth="250000">
        <SegmentTemplate
            presentationTimeOffset="0"
            timescale="1000"
            media="demo_hq/$Time$.m4a"
            initialization="demo_hq/init.m4a">
          <SegmentTimeline>
             <S t="240396" d="2020"/>
             <S t="242416" d="2020"/>
             <S t="244436" d="2020"/>
             <S t="246456" d="2020"/>
             <S t="248476" d="2020"/>
             <S t="250496" d="2021"/>
             <S t="252517" d="2020"/>
             <S t="254537" d="2020"/>
             <S t="256557" d="2020"/>
             <S t="258577" d="2020"/>
             <S t="260597" d="2020"/>
             <S t="262617" d="2020"/>
             <S t="264637" d="2021"/>
             <S t="266658" d="2020"/>
             <S t="268678" d="2020"/>
             <S t="270698" d="2020"/>
             <S t="272718" d="2020"/>
             <S t="274738" d="2020"/>
             <S t="276758" d="2020"/>
             <S t="278778" d="2020"/>
             <S t="280798" d="2021"/>
             <S t="282819" d="2020"/>
             <S t="284839" d="2020"/>
             <S t="286859" d="2020"/>
             <S t="288879" d="2020"/>
             <S t="290899" d="2020"/>
             <S t="292919" d="2020"/>
             <S t="294939" d="2021"/>
             <S t="296960" d="2020"/>
             <S t="298980" d="2020"/>
             <S t="301000" d="2020"/>
             <S t="303020" d="2020"/>
             <S t="305040" d="2020"/>
             <S t="307060" d="2020"/>
             <S t="309080" d="2021"/>
             <S t="311101" d="2020"/>
             <S t="313121" d="2020"/>
             <S t="315141" d="2020"/>
             <S t="317161" d="2020"/>
             <S t="319181" d="2020"/>
             <S t="321201" d="2020"/>
             <S t="323221" d="2020"/>
             <S t="325241" d="2021"/>
             <S t="327262" d="2020"/>
             <S t="329282" d="2020"/>
             <S t="331302" d="2020"/>
             <S t="333322" d="2020"/>
             <S t="335342" d="2020"/>
             <S t="337362" d="2020"/>
             <S t="339382" d="2021"/>
             <S t="341403" d="2020"/>
             <S t="343423" d="2020"/>
             <S t="345443" d="2020"/>
             <S t="347463" d="2020"/>
             <S t="349483" d="2020"/>
             <S t="351503" d="2020"/>
             <S t="353523" d="2021"/>
             <S t="355544" d="2020"/>
             <S t="357564" d="2020"/>
             <S t="359584" d="2020"/>
          </SegmentTimeline>
        </SegmentTemplate>
      </Representation>
    </AdaptationSet>
  </Period>
</MPD>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.