mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-11-03 03:03:56 +08:00
update 2026-10-29 11:59:06
This commit is contained in:
1 parent
bc85557d34
commit
e2f86e09f4
5033 files changed
-2043160
No files matched your search
@@ -1,22 +0,0 @@
|
||||
Copyright (C) 2017 Roman Arutyunyan
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -1,256 +0,0 @@
|
||||
*************************
|
||||
NGINX MPEG-TS Live Module
|
||||
*************************
|
||||
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
- receives MPEG-TS over HTTP
|
||||
- produces and manages live HLS_
|
||||
- produces and manages live MPEG-DASH_
|
||||
|
||||
|
||||
Compatibility
|
||||
=============
|
||||
|
||||
- `nginx <http://nginx.org>`_ version >= 1.11.5
|
||||
|
||||
|
||||
Build
|
||||
=====
|
||||
|
||||
Building nginx with the module:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# static module
|
||||
$ ./configure --add-module=/path/to/nginx-ts-module
|
||||
|
||||
# dynamic module
|
||||
$ ./configure --add-dynamic-module=/path/to/nginx-ts-module
|
||||
|
||||
|
||||
Directives
|
||||
==========
|
||||
|
||||
ts
|
||||
--
|
||||
|
||||
========== ========
|
||||
*Syntax:* ``ts``
|
||||
*Context:* location
|
||||
========== ========
|
||||
|
||||
Sets up a live MPEG-TS handler for the location.
|
||||
This directive is **required** for HLS or MPEG-DASH generation.
|
||||
|
||||
The last URI component is used as a stream name.
|
||||
For example, if the URI is ``/foo/bar/baz``, the stream name is ``baz``.
|
||||
|
||||
A simple way to stream MPEG-TS over HTTP is by running ``ffmpeg``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ffmpeg ... -f mpegts http://127.0.0.1:8000/foo
|
||||
|
||||
|
||||
By default, HTTP request body size is limited in nginx.
|
||||
To enable live streaming without size limitation, use the directive
|
||||
``client_max_body_size 0``.
|
||||
|
||||
|
||||
ts_hls
|
||||
------
|
||||
|
||||
========== ========
|
||||
*Syntax:* ``ts_hls path=PATH [segment=MIN[:MAX]] [segments=NUMBER] [max_size=SIZE] [noclean]``
|
||||
*Context:* location
|
||||
========== ========
|
||||
|
||||
Enables generating live HLS in the location.
|
||||
The ``PATH`` parameter specifies a directory where HLS playlist and segment
|
||||
files will be created.
|
||||
The directory is created if missing.
|
||||
For every publshed stream a subdirectory with the stream name is created under
|
||||
the ``PATH`` directory.
|
||||
The HLS playlist file created in the stream subdirectory is named
|
||||
``index.m3u8``.
|
||||
A path handler is installed to watch files in the directory.
|
||||
The old files in the directory are automatically deleted once they get old
|
||||
enough and are not supposed to be accessed by clients anymore.
|
||||
It is not allowed to reuse the path in other ``ts_hls`` or ``ts_dash``
|
||||
directives.
|
||||
|
||||
The ``segment`` parameter specifies minimum and maximum segment durations.
|
||||
If the stream has video, segments are started at video key frames.
|
||||
If a key frame does not appear within ``MAX`` duration, the segment is
|
||||
truncated.
|
||||
The default value for minimum segment duration is 5 seconds.
|
||||
If unspecified, maximum segment duration is set to be twice as much as the
|
||||
minimum.
|
||||
|
||||
The ``segments`` parameter specifies the maximum number of segments in a
|
||||
playlist.
|
||||
As new segments are added to the playlist, the oldest segments are removed from
|
||||
it.
|
||||
|
||||
The ``max_size`` parameter specifies the maximum size of a segment.
|
||||
A segment is truncated once it reaches this size.
|
||||
|
||||
The ``noclean`` parameter indicates that the old files (segments and the
|
||||
playlist) should not be automatically deleted from disk.
|
||||
|
||||
Example::
|
||||
|
||||
location / {
|
||||
ts;
|
||||
ts_hls path=/var/hls segment=10s;
|
||||
}
|
||||
|
||||
|
||||
ts_dash
|
||||
-------
|
||||
|
||||
========== ========
|
||||
*Syntax:* ``ts_dash path=PATH [segment=MIN[:MAX]] [segments=NUMBER] [max_size=SIZE] [noclean]``
|
||||
*Context:* location
|
||||
========== ========
|
||||
|
||||
Enables generating live MPEG-DASH in the location.
|
||||
The ``PATH`` parameter specifies a directory where MPEG-DASH manifest and
|
||||
segment files will be created.
|
||||
The directory is created if missing.
|
||||
For every publshed stream a subdirectory with the stream name is created under
|
||||
the ``PATH`` directory.
|
||||
The MPEG-DASH manifest file created in the stream subdirectory is named
|
||||
``index.mpd``.
|
||||
A path handler is installed to watch files in the directory.
|
||||
The old files in the directory are automatically deleted once they get old
|
||||
enough and are not supposed to be accessed by clients anymore.
|
||||
It is not allowed to reuse the path in other ``ts_hls`` or ``ts_dash``
|
||||
directives.
|
||||
|
||||
The ``segment`` parameter specifies minimum and maximum segment durations.
|
||||
If the stream has video, segments are started at video key frames.
|
||||
If a key frame does not appear within ``MAX`` duration, the segment is
|
||||
truncated.
|
||||
The default value for minimum segment duration is 5 seconds.
|
||||
If unspecified, maximum segment duration is set to be twice as much as the
|
||||
minimum.
|
||||
|
||||
When setting an explicit value for the ``MAX`` parameter, the following
|
||||
note should be taken into account.
|
||||
If the next segment is shorter than the previous one by a factor more that
|
||||
two, dash.js_ can end up in a busy cycle requesting the second segment over
|
||||
and over again.
|
||||
|
||||
The ``segments`` parameter specifies the maximum number of segments in a
|
||||
manifest.
|
||||
As new segments are added to the manifest, the oldest segments are removed from
|
||||
it.
|
||||
|
||||
The ``max_size`` parameter specifies the maximum size of a segment.
|
||||
A segment is truncated once it reaches this size.
|
||||
|
||||
The ``noclean`` parameter indicates that the old files (segments and the
|
||||
manifest) should not be automatically deleted from disk.
|
||||
|
||||
Example::
|
||||
|
||||
location / {
|
||||
ts;
|
||||
ts_dash path=/var/hls segment=10s;
|
||||
}
|
||||
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
nginx.conf::
|
||||
|
||||
# nginx.conf
|
||||
|
||||
events {
|
||||
}
|
||||
|
||||
http {
|
||||
server {
|
||||
listen 8000;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
}
|
||||
|
||||
location /publish/ {
|
||||
ts;
|
||||
ts_hls path=/var/media/hls segment=10s;
|
||||
ts_dash path=/var/media/dash segment=10s;
|
||||
|
||||
client_max_body_size 0;
|
||||
}
|
||||
|
||||
location /play/ {
|
||||
types {
|
||||
application/x-mpegURL m3u8;
|
||||
application/dash+xml mpd;
|
||||
video/MP2T ts;
|
||||
video/mp4 mp4;
|
||||
}
|
||||
alias /var/media/;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HLS in HTML:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<body>
|
||||
<video width="640" height="480" controls autoplay
|
||||
src="http://127.0.0.1:8000/play/hls/sintel/index.m3u8">
|
||||
</video>
|
||||
</body>
|
||||
|
||||
MPEG-DASH in HTML using the dash.js_ player:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<script src="http://cdn.dashjs.org/latest/dash.all.min.js"></script>
|
||||
|
||||
<body>
|
||||
<video data-dashjs-player
|
||||
width="640" height="480" controls autoplay
|
||||
src="http://127.0.0.1:8000/play/dash/sintel/index.mpd">
|
||||
</video>
|
||||
</body>
|
||||
|
||||
Broadcasting a single-bitrate mp4 file:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ffmpeg -re -i ~/Movies/sintel.mp4 -bsf:v h264_mp4toannexb
|
||||
-c copy -f mpegts http://127.0.0.1:8000/publish/sintel
|
||||
|
||||
Broadcasting an mp4 file in multiple bitrates.
|
||||
For proper HLS generation streams should be grouped into MPEG-TS programs with
|
||||
the ``-program`` option of ``ffmpeg``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ffmpeg -re -i ~/Movies/sintel.mp4 -bsf:v h264_mp4toannexb
|
||||
-map 0:0 -map 0:1 -map 0:0 -map 0:1
|
||||
-c:v:0 copy
|
||||
-c:a:0 copy
|
||||
-c:v:1 libx264 -b:v:1 100k
|
||||
-c:a:1 libfaac -ac:a:1 1 -b:a:1 32k
|
||||
-program "st=0:st=1" -program "st=2:st=3"
|
||||
-f mpegts http://127.0.0.1:8000/publish/sintel
|
||||
|
||||
|
||||
.. _HLS: https://tools.ietf.org/html/draft-pantos-http-live-streaming-23
|
||||
.. _MPEG-DASH: https://en.wikipedia.org/wiki/Dynamic_Adaptive_Streaming_over_HTTP
|
||||
.. _dash.js: https://github.com/Dash-Industry-Forum/dash.js
|
||||
@@ -1,18 +0,0 @@
|
||||
ngx_module_type=HTTP
|
||||
ngx_addon_name=ngx_http_ts_module
|
||||
ngx_module_name=ngx_http_ts_module
|
||||
ngx_module_incs=
|
||||
ngx_module_deps="$ngx_addon_dir/src/ngx_ts_stream.h \
|
||||
$ngx_addon_dir/src/ngx_ts_hls.h \
|
||||
$ngx_addon_dir/src/ngx_ts_dash.h \
|
||||
$ngx_addon_dir/src/ngx_ts_avc.h \
|
||||
$ngx_addon_dir/src/ngx_ts_aac.h"
|
||||
ngx_module_srcs="$ngx_addon_dir/src/ngx_http_ts_module.c \
|
||||
$ngx_addon_dir/src/ngx_ts_stream.c \
|
||||
$ngx_addon_dir/src/ngx_ts_hls.c \
|
||||
$ngx_addon_dir/src/ngx_ts_dash.c \
|
||||
$ngx_addon_dir/src/ngx_ts_dash_mp4.c \
|
||||
$ngx_addon_dir/src/ngx_ts_avc.c \
|
||||
$ngx_addon_dir/src/ngx_ts_aac.c"
|
||||
|
||||
. auto/module
|
||||
@@ -1,263 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
#include "ngx_ts_stream.h"
|
||||
#include "ngx_ts_hls.h"
|
||||
#include "ngx_ts_dash.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_ts_hls_conf_t *hls;
|
||||
ngx_ts_dash_conf_t *dash;
|
||||
} ngx_http_ts_loc_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_ts_stream_t *ts;
|
||||
} ngx_http_ts_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_ts_handler(ngx_http_request_t *r);
|
||||
static void ngx_http_ts_init(ngx_http_request_t *r);
|
||||
static void ngx_http_ts_read_event_handler(ngx_http_request_t *r);
|
||||
|
||||
static char *ngx_http_ts(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
static void *ngx_http_ts_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_http_ts_merge_conf(ngx_conf_t *cf, void *parent, void *child);
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_ts_commands[] = {
|
||||
|
||||
{ ngx_string("ts"),
|
||||
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
|
||||
ngx_http_ts,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ts_hls"),
|
||||
NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_ts_hls_set_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_ts_loc_conf_t, hls),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ts_dash"),
|
||||
NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
||||
ngx_ts_dash_set_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_ts_loc_conf_t, dash),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_ts_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_http_ts_create_conf, /* create location configuration */
|
||||
ngx_http_ts_merge_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_ts_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_ts_module_ctx, /* module context */
|
||||
ngx_http_ts_commands, /* module directives */
|
||||
NGX_HTTP_MODULE, /* module type */
|
||||
NULL, /* init master */
|
||||
NULL, /* init module */
|
||||
NULL, /* init process */
|
||||
NULL, /* init thread */
|
||||
NULL, /* exit thread */
|
||||
NULL, /* exit process */
|
||||
NULL, /* exit master */
|
||||
NGX_MODULE_V1_PADDING
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_ts_handler(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_str_t name;
|
||||
ngx_uint_t n;
|
||||
ngx_http_ts_ctx_t *ctx;
|
||||
ngx_http_ts_loc_conf_t *tlcf;
|
||||
|
||||
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ts_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->ts = ngx_pcalloc(r->pool, sizeof(ngx_ts_stream_t));
|
||||
if (ctx->ts == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->ts->pool = r->pool;
|
||||
ctx->ts->log = r->connection->log;
|
||||
|
||||
for (n = 0; n < r->uri.len; n++) {
|
||||
if (r->uri.data[r->uri.len - 1 - n] == '/') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
name.data = &r->uri.data[r->uri.len - n];
|
||||
name.len = n;
|
||||
|
||||
/* XXX detect streams with the same name, add shared zone */
|
||||
|
||||
tlcf = ngx_http_get_module_loc_conf(r, ngx_http_ts_module);
|
||||
|
||||
if (tlcf->hls) {
|
||||
if (ngx_ts_hls_create(tlcf->hls, ctx->ts, &name) == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (tlcf->dash) {
|
||||
if (ngx_ts_dash_create(tlcf->dash, ctx->ts, &name) == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_ts_module);
|
||||
|
||||
r->request_body_no_buffering = 1;
|
||||
|
||||
rc = ngx_http_read_client_request_body(r, ngx_http_ts_init);
|
||||
|
||||
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return NGX_DONE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_ts_init(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_ts_ctx_t *ctx;
|
||||
ngx_http_request_body_t *rb;
|
||||
|
||||
rb = r->request_body;
|
||||
|
||||
if (rb == NULL) {
|
||||
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_ts_module);
|
||||
|
||||
if (ngx_ts_read(ctx->ts, rb->bufs) != NGX_OK) {
|
||||
ngx_http_finalize_request(r, NGX_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (r->reading_body) {
|
||||
r->read_event_handler = ngx_http_ts_read_event_handler;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_ts_read_event_handler(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_http_ts_ctx_t *ctx;
|
||||
ngx_http_request_body_t *rb;
|
||||
|
||||
if (ngx_exiting || ngx_terminate) {
|
||||
ngx_http_finalize_request(r, NGX_HTTP_CLOSE);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_ts_module);
|
||||
|
||||
rb = r->request_body;
|
||||
|
||||
for ( ;; ) {
|
||||
rc = ngx_http_read_unbuffered_request_body(r);
|
||||
|
||||
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
||||
ngx_http_finalize_request(r, rc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rb->bufs == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ngx_ts_read(ctx->ts, rb->bufs) != NGX_OK) {
|
||||
ngx_http_finalize_request(r, NGX_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
ngx_http_finalize_request(r, NGX_HTTP_NO_CONTENT);
|
||||
return;
|
||||
}
|
||||
|
||||
rb->bufs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ts(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
|
||||
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
|
||||
clcf->handler = ngx_http_ts_handler;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_http_ts_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_http_ts_loc_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ts_loc_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->hls = NGX_CONF_UNSET_PTR;
|
||||
conf->dash = NGX_CONF_UNSET_PTR;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ts_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_http_ts_loc_conf_t *prev = parent;
|
||||
ngx_http_ts_loc_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->hls, prev->hls, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->dash, prev->dash, NULL);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
#include "ngx_ts_stream.h"
|
||||
#include "ngx_ts_hls.h"
|
||||
#include "ngx_ts_dash.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_ts_hls_conf_t *hls;
|
||||
ngx_ts_dash_conf_t *dash;
|
||||
} ngx_stream_ts_srv_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_ts_stream_t *ts;
|
||||
} ngx_stream_ts_ctx_t;
|
||||
|
||||
|
||||
static void ngx_stream_ts_handler(ngx_stream_session_t *s);
|
||||
static char *ngx_stream_ts(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
static void *ngx_stream_ts_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_ts_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_ts_commands[] = {
|
||||
|
||||
{ ngx_string("ts"),
|
||||
NGX_STREAM_SRV_CONF|NGX_CONF_NOARGS,
|
||||
ngx_stream_ts,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ts_hls"),
|
||||
NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
|
||||
ngx_ts_hls_set_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ts_srv_conf_t, hls),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ts_dash"),
|
||||
NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
|
||||
ngx_ts_dash_set_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ts_srv_conf_t, dash),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_ts_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_stream_ts_create_conf, /* create server configuration */
|
||||
ngx_stream_ts_merge_conf /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_ts_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_ts_module_ctx, /* module context */
|
||||
ngx_stream_ts_commands, /* module directives */
|
||||
NGX_STREAM_MODULE, /* module type */
|
||||
NULL, /* init master */
|
||||
NULL, /* init module */
|
||||
NULL, /* init process */
|
||||
NULL, /* init thread */
|
||||
NULL, /* exit thread */
|
||||
NULL, /* exit process */
|
||||
NULL, /* exit master */
|
||||
NGX_MODULE_V1_PADDING
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_ts_handler(ngx_stream_session_t *s);
|
||||
{
|
||||
ngx_str_t name;
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_ts_ctx_t *ctx;
|
||||
ngx_stream_ts_srv_conf_t *tscf;
|
||||
|
||||
c = s->connection;
|
||||
|
||||
ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_ts_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->ts = ngx_pcalloc(c->pool, sizeof(ngx_ts_stream_t));
|
||||
if (ctx->ts == NULL) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->ts->pool = c->pool;
|
||||
ctx->ts->log = c->log;
|
||||
|
||||
/* XXX */
|
||||
ngx_ste_set(&name, "foo");
|
||||
|
||||
/* XXX detect streams with the same name, add shared zone */
|
||||
|
||||
tscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ts_module);
|
||||
|
||||
if (tscf->hls) {
|
||||
if (ngx_ts_hls_create(tscf->hls, ctx->ts, &name) == NULL) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (tscf->dash) {
|
||||
if (ngx_ts_dash_create(tscf->dash, ctx->ts, &name) == NULL) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_stream_set_ctx(r, ctx, ngx_stream_ts_module);
|
||||
|
||||
c->write->handler = ngx_stream_ts_read_handler;
|
||||
|
||||
ngx_stream_ts_read_handler(c->write);
|
||||
}
|
||||
|
||||
|
||||
static voi
|
||||
ngx_stream_ts_read_handler(ngx_event_t *rev)
|
||||
{
|
||||
ssize_t n;
|
||||
ngx_buf_t b;
|
||||
ngx_chain_t in;
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_ts_ctx_t *ctx;
|
||||
ngx_stream_session_t *s;
|
||||
u_char buf[4096];
|
||||
|
||||
c = rev->data;
|
||||
s = c->data;
|
||||
|
||||
if (ngx_exiting || ngx_terminate) {
|
||||
ngx_stream_finalize_request(s, NGX_STREAM_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rev->timedout) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = ngx_stream_get_module_ctx(s, ngx_stream_ts_module);
|
||||
|
||||
in.buf = &b;
|
||||
in.next = NULL;
|
||||
|
||||
ngx_memzero(&b, sizeof(ngx_buf_t));
|
||||
|
||||
while (rev->ready) {
|
||||
n = c->recv(c, buf, sizeof(buf));
|
||||
|
||||
if (n == NGX_ERROR || n == 0) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
if (n == NGX_AGAIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
b.pos = buf;
|
||||
b.last = buf + n;
|
||||
|
||||
if (ngx_ts_read(ctx->ts, &in) != NGX_OK) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_add_timer(rev, 5000); /* XXX */
|
||||
|
||||
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_ts(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_core_srv_conf_t *cscf;
|
||||
|
||||
cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module);
|
||||
cscf->handler = ngx_stream_ts_handler;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_ts_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_ts_srv_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ts_srv_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->hls = NGX_CONF_UNSET_PTR;
|
||||
conf->dash = NGX_CONF_UNSET_PTR;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_ts_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_stream_ts_srv_conf_t *prev = parent;
|
||||
ngx_stream_ts_srv_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->hls, prev->hls, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->dash, prev->dash, NULL);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
#include "ngx_ts_aac.h"
|
||||
|
||||
|
||||
ngx_ts_aac_params_t *
|
||||
ngx_ts_aac_decode_params(ngx_ts_stream_t *ts, u_char *adts, size_t adts_len)
|
||||
{
|
||||
/*
|
||||
* XXX
|
||||
* https://wiki.multimedia.cx/index.php/ADTS
|
||||
*/
|
||||
|
||||
ngx_ts_aac_params_t *aac;
|
||||
|
||||
static ngx_uint_t freq[] = {
|
||||
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
|
||||
16000, 12000, 11025, 8000, 7350, 0, 0, 0
|
||||
};
|
||||
|
||||
if (adts_len < 7) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
aac = ngx_pcalloc(ts->pool, sizeof(ngx_ts_aac_params_t));
|
||||
if (aac == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* MPEG Version: 0 for MPEG-4, 1 for MPEG-2 */
|
||||
aac->version = (adts[1] & 0x08) ? 1 : 0;
|
||||
|
||||
/*
|
||||
* MPEG-4 Audio Object Types:
|
||||
* 1: AAC Main
|
||||
* 2: AAC LC (Low Complexity)
|
||||
* 3: AAC SSR (Scalable Sample Rate)
|
||||
* 4: AAC LTP (Long Term Prediction)
|
||||
*/
|
||||
aac->profile = (adts[2] >> 6) + 1;
|
||||
|
||||
aac->freq_index = (adts[2] >> 2) & 0x0f;
|
||||
|
||||
aac->freq = freq[aac->freq_index];
|
||||
if (aac->freq == 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
aac->chan = ((adts[2] & 0x01) << 2) | (adts[3] >> 6);
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_CORE, ts->log, 0,
|
||||
"ts aac version:%ui, profile:%ui, freq:%ui, chan:%ui",
|
||||
aac->version, aac->profile, aac->freq, aac->chan);
|
||||
|
||||
return aac;
|
||||
|
||||
failed:
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, ts->log, 0, "failed to parse AAC parameters");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
#include "ngx_ts_stream.h"
|
||||
|
||||
|
||||
#ifndef _NGX_TS_AAC_H_INCLUDED_
|
||||
#define _NGX_TS_AAC_H_INCLUDED_
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t version;
|
||||
ngx_uint_t profile;
|
||||
ngx_uint_t freq_index;
|
||||
ngx_uint_t freq;
|
||||
ngx_uint_t chan;
|
||||
} ngx_ts_aac_params_t;
|
||||
|
||||
|
||||
ngx_ts_aac_params_t *ngx_ts_aac_decode_params(ngx_ts_stream_t *ts, u_char *adts,
|
||||
size_t adts_len);
|
||||
|
||||
|
||||
#endif /* _NGX_TS_AAC_H_INCLUDED_ */
|
||||
@@ -1,298 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
#include "ngx_ts_avc.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
u_char *pos;
|
||||
u_char *last;
|
||||
ngx_uint_t shift;
|
||||
ngx_uint_t err; /* unsigned err:1; */
|
||||
const char *name;
|
||||
ngx_log_t *log;
|
||||
} ngx_ts_avc_reader_t;
|
||||
|
||||
|
||||
static void ngx_ts_avc_init_reader(ngx_ts_avc_reader_t *br, u_char *buf,
|
||||
size_t len, ngx_log_t *log);
|
||||
static uint64_t ngx_ts_avc_read(ngx_ts_avc_reader_t *br, ngx_uint_t bits);
|
||||
static uint64_t ngx_ts_avc_read_golomb(ngx_ts_avc_reader_t *br);
|
||||
|
||||
|
||||
static void
|
||||
ngx_ts_avc_init_reader(ngx_ts_avc_reader_t *br, u_char *buf, size_t len,
|
||||
ngx_log_t *log)
|
||||
{
|
||||
ngx_memzero(br, sizeof(ngx_ts_avc_reader_t));
|
||||
|
||||
br->pos = buf;
|
||||
br->last = buf + len;
|
||||
br->log = log;
|
||||
}
|
||||
|
||||
|
||||
static uint64_t
|
||||
ngx_ts_avc_read(ngx_ts_avc_reader_t *br, ngx_uint_t bits)
|
||||
{
|
||||
uint64_t v;
|
||||
ngx_uint_t k, n;
|
||||
|
||||
if (br->err) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
v = 0;
|
||||
n = bits;
|
||||
|
||||
while (n) {
|
||||
if (br->pos == br->last) {
|
||||
br->err = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
k = ngx_min(8 - br->shift, n);
|
||||
|
||||
/*
|
||||
* [-------------|||||||||--------------]
|
||||
* br->shift k
|
||||
*/
|
||||
|
||||
v = (v << k) | (*br->pos & (0xff >> br->shift)) >> (8 - br->shift - k);
|
||||
|
||||
n -= k;
|
||||
br->shift += k;
|
||||
|
||||
if (br->shift == 8) {
|
||||
br->shift = 0;
|
||||
br->pos++;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_CORE, br->log, 0,
|
||||
"ts avc %s[%uL]:%uL", br->name, bits, v);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
static uint64_t
|
||||
ngx_ts_avc_read_golomb(ngx_ts_avc_reader_t *br)
|
||||
{
|
||||
/*
|
||||
* ISO/IEC 14496-10:2004(E)
|
||||
* 9.1 Parsing process for Exp-Golomb codes, p. 159
|
||||
*/
|
||||
|
||||
uint64_t v;
|
||||
ngx_uint_t n;
|
||||
|
||||
if (br->err) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
|
||||
while (ngx_ts_avc_read(br, 1) == 0) {
|
||||
if (br->err) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
v = ((uint64_t) 1 << n) - 1 + ngx_ts_avc_read(br, n);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_CORE, br->log, 0, "ts avc %s:%uL",
|
||||
br->name, v);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
ngx_ts_avc_params_t *
|
||||
ngx_ts_avc_decode_params(ngx_ts_stream_t *ts, u_char *sps, size_t sps_len,
|
||||
u_char *pps, size_t pps_len)
|
||||
{
|
||||
/*
|
||||
* ISO/IEC 14496-10:2004(E)
|
||||
* 7.3.2.1 Sequence parameter set RBSP syntax, p. 31
|
||||
*/
|
||||
|
||||
ngx_uint_t type, n, i;
|
||||
ngx_ts_avc_reader_t br;
|
||||
ngx_ts_avc_params_t *avc;
|
||||
|
||||
/* ignore PPS so far */
|
||||
|
||||
avc = ngx_pcalloc(ts->pool, sizeof(ngx_ts_avc_params_t));
|
||||
if (avc == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ngx_ts_avc_init_reader(&br, sps, sps_len, ts->log);
|
||||
|
||||
br.name = "nalu_type";
|
||||
type = ngx_ts_avc_read(&br, 8);
|
||||
if ((type & 0x1f) != 7) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
br.name = "profile_idc";
|
||||
avc->profile_idc = ngx_ts_avc_read(&br, 8);
|
||||
|
||||
br.name = "constraints";
|
||||
avc->constraints = ngx_ts_avc_read(&br, 8);
|
||||
|
||||
br.name = "level_idc";
|
||||
avc->level_idc = ngx_ts_avc_read(&br, 8);
|
||||
|
||||
br.name = "seq_parameter_set_id";
|
||||
avc->sps_id = ngx_ts_avc_read_golomb(&br);
|
||||
|
||||
if (avc->profile_idc == 100
|
||||
|| avc->profile_idc == 110
|
||||
|| avc->profile_idc == 122
|
||||
|| avc->profile_idc == 244
|
||||
|| avc->profile_idc == 44
|
||||
|| avc->profile_idc == 83
|
||||
|| avc->profile_idc == 86
|
||||
|| avc->profile_idc == 118
|
||||
|| avc->profile_idc == 128
|
||||
|| avc->profile_idc == 138
|
||||
|| avc->profile_idc == 139
|
||||
|| avc->profile_idc == 134)
|
||||
{
|
||||
br.name = "chroma_format_idc";
|
||||
avc->chroma_format_idc = ngx_ts_avc_read_golomb(&br);
|
||||
|
||||
if (avc->chroma_format_idc == 3) {
|
||||
br.name =
|
||||
"residual_colour_transform_flagseparate_colour_plane_flag";
|
||||
avc->residual_colour_transform_flagseparate_colour_plane_flag =
|
||||
ngx_ts_avc_read(&br, 1);
|
||||
}
|
||||
|
||||
br.name = "bit_depth_luma_minus8";
|
||||
avc->bit_depth_luma = ngx_ts_avc_read_golomb(&br) + 8;
|
||||
|
||||
br.name = "bit_depth_chroma_minus8";
|
||||
avc->bit_depth_chroma = ngx_ts_avc_read_golomb(&br) + 8;
|
||||
|
||||
br.name = "qpprime_y_zero_transform_bypass_flag";
|
||||
avc->qpprime_y_zero_transform_bypass_flag = ngx_ts_avc_read(&br, 1);
|
||||
|
||||
br.name = "seq_scaling_matrix_present_flag";
|
||||
avc->seq_scaling_matrix_present_flag = ngx_ts_avc_read(&br, 1);
|
||||
|
||||
if (avc->seq_scaling_matrix_present_flag) {
|
||||
n = (avc->chroma_format_idc != 3) ? 8 : 12;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
br.name = "seq_scaling_list_present_flag[i]";
|
||||
if (ngx_ts_avc_read(&br, 1)) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
br.name = "log2_max_frame_num_minus4";
|
||||
avc->max_frame_num = (1 << (ngx_ts_avc_read_golomb(&br) + 4));
|
||||
|
||||
br.name = "pic_order_cnt_type";
|
||||
avc->pic_order_cnt_type = ngx_ts_avc_read_golomb(&br);
|
||||
|
||||
if (avc->pic_order_cnt_type == 0) {
|
||||
br.name = "log2_max_pic_order_cnt_lsb_minus4";
|
||||
avc->max_pic_order_cnt_lsb = (1 << (ngx_ts_avc_read_golomb(&br) + 4));
|
||||
|
||||
} else if (avc->pic_order_cnt_type == 1) {
|
||||
br.name = "delta_pic_order_always_zero_flag";
|
||||
avc->delta_pic_order_always_zero_flag = ngx_ts_avc_read(&br, 1);
|
||||
|
||||
br.name = "offset_for_non_ref_pic";
|
||||
avc->offset_for_non_ref_pic = ngx_ts_avc_read_golomb(&br);
|
||||
|
||||
br.name = "offset_for_top_to_bottom_field";
|
||||
avc->offset_for_top_to_bottom_field = ngx_ts_avc_read_golomb(&br);
|
||||
|
||||
br.name = "num_ref_frames_in_pic_order_cnt_cycle";
|
||||
n = ngx_ts_avc_read_golomb(&br);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
br.name = "offset_for_ref_frame[i]";
|
||||
(void) ngx_ts_avc_read_golomb(&br);
|
||||
}
|
||||
}
|
||||
|
||||
br.name = "num_ref_frames";
|
||||
avc->num_ref_frames = ngx_ts_avc_read_golomb(&br);
|
||||
|
||||
br.name = "gaps_in_frame_num_value_allowed_flag";
|
||||
avc->gaps_in_frame_num_value_allowed_flag = ngx_ts_avc_read(&br, 1);
|
||||
|
||||
br.name = "pic_width_in_mbs_minus1";
|
||||
avc->pic_width_in_mbs = ngx_ts_avc_read_golomb(&br) + 1;
|
||||
|
||||
br.name = "pic_height_in_map_units_minus1";
|
||||
avc->pic_height_in_map_units = ngx_ts_avc_read_golomb(&br) + 1;
|
||||
|
||||
br.name = "frame_mbs_only_flag";
|
||||
avc->frame_mbs_only_flag = ngx_ts_avc_read(&br, 1);
|
||||
|
||||
if (!avc->frame_mbs_only_flag) {
|
||||
br.name = "mb_adaptive_frame_field_flag";
|
||||
avc->mb_adaptive_frame_field_flag = ngx_ts_avc_read(&br, 1);
|
||||
}
|
||||
|
||||
br.name = "direct_8x8_inference_flag";
|
||||
avc->direct_8x8_inference_flag = ngx_ts_avc_read(&br, 1);
|
||||
|
||||
br.name = "frame_cropping_flag";
|
||||
avc->frame_cropping_flag = ngx_ts_avc_read(&br, 1);
|
||||
|
||||
if (avc->frame_cropping_flag) {
|
||||
br.name = "frame_crop_left_offset";
|
||||
avc->frame_crop_left_offset = ngx_ts_avc_read_golomb(&br);
|
||||
|
||||
br.name = "frame_crop_right_offset";
|
||||
avc->frame_crop_right_offset = ngx_ts_avc_read_golomb(&br);
|
||||
|
||||
br.name = "frame_crop_top_offset";
|
||||
avc->frame_crop_top_offset = ngx_ts_avc_read_golomb(&br);
|
||||
|
||||
br.name = "frame_crop_bottom_offset";
|
||||
avc->frame_crop_bottom_offset = ngx_ts_avc_read_golomb(&br);
|
||||
}
|
||||
|
||||
if (br.err) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
avc->width =
|
||||
avc->pic_width_in_mbs * 16
|
||||
- (avc->frame_crop_left_offset + avc->frame_crop_right_offset) * 2;
|
||||
|
||||
avc->height =
|
||||
(2 - avc->frame_mbs_only_flag) * avc->pic_height_in_map_units * 16
|
||||
- (avc->frame_crop_top_offset + avc->frame_crop_bottom_offset) * 2;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_CORE, ts->log, 0,
|
||||
"ts avc width:%ui, height:%ui", avc->width, avc->height);
|
||||
|
||||
return avc;
|
||||
|
||||
failed:
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, ts->log, 0,
|
||||
"failed to parse AVC parameters");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
#include "ngx_ts_stream.h"
|
||||
|
||||
|
||||
#ifndef _NGX_TS_AVC_H_INCLUDED_
|
||||
#define _NGX_TS_AVC_H_INCLUDED_
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t profile_idc;
|
||||
ngx_uint_t constraints;
|
||||
ngx_uint_t level_idc;
|
||||
ngx_uint_t sps_id;
|
||||
ngx_uint_t chroma_format_idc;
|
||||
ngx_uint_t bit_depth_luma;
|
||||
ngx_uint_t bit_depth_chroma;
|
||||
ngx_uint_t max_frame_num;
|
||||
ngx_uint_t pic_order_cnt_type;
|
||||
ngx_uint_t max_pic_order_cnt_lsb;
|
||||
ngx_int_t offset_for_non_ref_pic;
|
||||
ngx_int_t offset_for_top_to_bottom_field;
|
||||
ngx_uint_t num_ref_frames_in_pic_order_cnt_cycle;
|
||||
ngx_uint_t num_ref_frames;
|
||||
ngx_uint_t pic_width_in_mbs;
|
||||
ngx_uint_t pic_height_in_map_units;
|
||||
ngx_uint_t frame_crop_left_offset;
|
||||
ngx_uint_t frame_crop_right_offset;
|
||||
ngx_uint_t frame_crop_top_offset;
|
||||
ngx_uint_t frame_crop_bottom_offset;
|
||||
ngx_uint_t width;
|
||||
ngx_uint_t height;
|
||||
|
||||
unsigned delta_pic_order_always_zero_flag:1;
|
||||
unsigned gaps_in_frame_num_value_allowed_flag:1;
|
||||
unsigned frame_mbs_only_flag:1;
|
||||
unsigned mb_adaptive_frame_field_flag:1;
|
||||
unsigned direct_8x8_inference_flag:1;
|
||||
unsigned frame_cropping_flag:1;
|
||||
unsigned residual_colour_transform_flagseparate_colour_plane_flag:1;
|
||||
unsigned qpprime_y_zero_transform_bypass_flag:1;
|
||||
unsigned seq_scaling_matrix_present_flag:1;
|
||||
} ngx_ts_avc_params_t;
|
||||
|
||||
|
||||
ngx_ts_avc_params_t *ngx_ts_avc_decode_params(ngx_ts_stream_t *ts, u_char *sps,
|
||||
size_t sps_len, u_char *pps, size_t pps_len);
|
||||
|
||||
|
||||
#endif /* _NGX_TS_AVC_H_INCLUDED_ */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,134 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
#include "ngx_ts_stream.h"
|
||||
#include "ngx_ts_avc.h"
|
||||
#include "ngx_ts_aac.h"
|
||||
|
||||
|
||||
#ifndef _NGX_TS_DASH_H_INCLUDED_
|
||||
#define _NGX_TS_DASH_H_INCLUDED_
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_path_t *path;
|
||||
ngx_msec_t min_seg;
|
||||
ngx_msec_t max_seg;
|
||||
ngx_msec_t analyze;
|
||||
size_t max_size;
|
||||
ngx_uint_t nsegs;
|
||||
} ngx_ts_dash_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint64_t start;
|
||||
uint64_t duration;
|
||||
} ngx_ts_dash_segment_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
u_char *dts; /* 64-bit */
|
||||
u_char *pts; /* 64-bit */
|
||||
u_char *seq;
|
||||
u_char *duration;
|
||||
u_char *sample_duration;
|
||||
u_char *nsamples;
|
||||
u_char *traf;
|
||||
u_char *trun;
|
||||
u_char *moof;
|
||||
u_char *moof_mdat;
|
||||
u_char *moof_data;
|
||||
u_char *mdat;
|
||||
} ngx_ts_dash_subs_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_ts_es_t *es;
|
||||
|
||||
ngx_ts_dash_segment_t *segs;
|
||||
ngx_uint_t nsegs;
|
||||
ngx_uint_t seg;
|
||||
uint64_t seg_pts;
|
||||
uint64_t seg_dts;
|
||||
uint64_t dts;
|
||||
|
||||
ngx_uint_t bandwidth;
|
||||
ngx_uint_t bandwidth_bytes;
|
||||
uint64_t bandwidth_dts;
|
||||
|
||||
u_char *sps;
|
||||
u_char *pps;
|
||||
size_t sps_len;
|
||||
size_t pps_len;
|
||||
|
||||
ngx_ts_avc_params_t *avc;
|
||||
ngx_ts_aac_params_t *aac;
|
||||
|
||||
ngx_str_t path;
|
||||
u_char *init_path;
|
||||
u_char *init_tmp_path;
|
||||
|
||||
ngx_chain_t *meta;
|
||||
ngx_chain_t *last_meta;
|
||||
ngx_chain_t *data;
|
||||
ngx_chain_t *last_data;
|
||||
|
||||
ngx_uint_t nsamples;
|
||||
ngx_uint_t nmeta;
|
||||
ngx_uint_t ndata;
|
||||
|
||||
ngx_ts_dash_subs_t subs;
|
||||
} ngx_ts_dash_rep_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_ts_dash_rep_t *reps;
|
||||
ngx_uint_t nreps;
|
||||
ngx_uint_t video; /* unsigned video:1; */
|
||||
} ngx_ts_dash_set_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_ts_stream_t *ts;
|
||||
ngx_ts_dash_conf_t *conf;
|
||||
|
||||
u_char *mpd_path;
|
||||
u_char *mpd_tmp_path;
|
||||
ngx_str_t path;
|
||||
size_t playlist_len;
|
||||
size_t init_seg_len;
|
||||
time_t availability_start;
|
||||
|
||||
ngx_chain_t *free;
|
||||
ngx_ts_dash_set_t *sets;
|
||||
ngx_uint_t nsets;
|
||||
|
||||
ngx_uint_t flush; /* unsigned flush:1; */
|
||||
} ngx_ts_dash_t;
|
||||
|
||||
|
||||
ngx_ts_dash_t *ngx_ts_dash_create(ngx_ts_dash_conf_t *conf, ngx_ts_stream_t *ts,
|
||||
ngx_str_t *name);
|
||||
char *ngx_ts_dash_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
|
||||
void ngx_ts_dash_write_init_segment(ngx_buf_t *b, ngx_ts_dash_rep_t *rep);
|
||||
|
||||
ngx_int_t ngx_ts_dash_start_segment(ngx_ts_dash_t *dash,
|
||||
ngx_ts_dash_rep_t *rep);
|
||||
ngx_chain_t *ngx_ts_dash_end_segment(ngx_ts_dash_t *dash,
|
||||
ngx_ts_dash_rep_t *rep);
|
||||
void ngx_ts_dash_free_segment(ngx_ts_dash_t *dash, ngx_ts_dash_rep_t *rep,
|
||||
ngx_chain_t *out);
|
||||
ngx_int_t ngx_ts_dash_append_meta(ngx_ts_dash_t *dash, ngx_ts_dash_rep_t *rep,
|
||||
size_t size, uint64_t dts);
|
||||
ngx_int_t ngx_ts_dash_append_data(ngx_ts_dash_t *dash, ngx_ts_dash_rep_t *rep,
|
||||
u_char *data, size_t len);
|
||||
|
||||
|
||||
#endif /* _NGX_TS_DASH_H_INCLUDED_ */
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,75 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
#include "ngx_ts_stream.h"
|
||||
|
||||
|
||||
#ifndef _NGX_TS_HLS_H_INCLUDED_
|
||||
#define _NGX_TS_HLS_H_INCLUDED_
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_path_t *path;
|
||||
ngx_msec_t min_seg;
|
||||
ngx_msec_t max_seg;
|
||||
ngx_msec_t analyze;
|
||||
size_t max_size;
|
||||
ngx_uint_t nsegs;
|
||||
} ngx_ts_hls_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t id;
|
||||
uint64_t duration;
|
||||
off_t size;
|
||||
} ngx_ts_hls_segment_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_file_t file;
|
||||
ngx_chain_t *prologue;
|
||||
|
||||
ngx_uint_t bandwidth;
|
||||
ngx_uint_t bandwidth_bytes;
|
||||
uint64_t bandwidth_dts;
|
||||
|
||||
ngx_ts_hls_segment_t *segs;
|
||||
ngx_uint_t nsegs;
|
||||
ngx_uint_t seg;
|
||||
uint64_t seg_dts;
|
||||
|
||||
u_char *m3u8_path;
|
||||
u_char *m3u8_tmp_path;
|
||||
ngx_str_t path;
|
||||
|
||||
ngx_ts_program_t *prog;
|
||||
} ngx_ts_hls_variant_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_ts_stream_t *ts;
|
||||
ngx_ts_hls_conf_t *conf;
|
||||
|
||||
u_char *m3u8_path;
|
||||
u_char *m3u8_tmp_path;
|
||||
ngx_str_t path;
|
||||
|
||||
ngx_ts_hls_variant_t *vars;
|
||||
ngx_uint_t nvars;
|
||||
|
||||
ngx_uint_t done; /* unsigned done:1; */
|
||||
} ngx_ts_hls_t;
|
||||
|
||||
|
||||
ngx_ts_hls_t *ngx_ts_hls_create(ngx_ts_hls_conf_t *conf, ngx_ts_stream_t *ts,
|
||||
ngx_str_t *name);
|
||||
char *ngx_ts_hls_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
|
||||
|
||||
#endif /* _NGX_TS_HLS_H_INCLUDED_ */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,113 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
|
||||
#ifndef _NGX_TS_STREAM_H_INCLUDED_
|
||||
#define _NGX_TS_STREAM_H_INCLUDED_
|
||||
|
||||
|
||||
/*
|
||||
* ISO/IEC 13818-1 : 2000 (E)
|
||||
* Table 2-29 – Stream type assignments, p. 48
|
||||
*
|
||||
* ISO/IEC 13818-1:2007/Amd.3:2009 (E)
|
||||
* Table 2-34 – Stream type assignments, p. 6
|
||||
*/
|
||||
|
||||
#define NGX_TS_VIDEO_MPEG1 0x01 /* ISO/IEC 11172-2, MPEG-1 Video */
|
||||
#define NGX_TS_VIDEO_MPEG2 0x02 /* ISO/IEC 13818-2, MPEG-2 Video */
|
||||
#define NGX_TS_VIDEO_MPEG4 0x10 /* ISO/IEC 14496-2, MPEG-4 Video */
|
||||
#define NGX_TS_VIDEO_AVC 0x1b /* ISO/IEC 14496-10, AVC */
|
||||
|
||||
#define NGX_TS_AUDIO_MPEG1 0x03 /* ISO/IEC 11172-3, MPEG-1 Audio */
|
||||
#define NGX_TS_AUDIO_MPEG2 0x04 /* ISO/IEC 13818-3, MPEG-2 Audio */
|
||||
#define NGX_TS_AUDIO_AAC 0x0f /* ISO/IEC 13818-7, MPEG-2 AAC ADTS Audio */
|
||||
|
||||
|
||||
typedef enum {
|
||||
NGX_TS_PAT = 0,
|
||||
NGX_TS_PMT,
|
||||
NGX_TS_PES
|
||||
} ngx_ts_event_e;
|
||||
|
||||
|
||||
typedef struct {
|
||||
u_char type;
|
||||
u_char sid;
|
||||
u_char cont;
|
||||
uint16_t pid;
|
||||
uint64_t pts;
|
||||
uint64_t dts;
|
||||
unsigned ptsf:1;
|
||||
unsigned rand:1;
|
||||
unsigned video:1;
|
||||
ngx_chain_t *bufs; /* ES */
|
||||
} ngx_ts_es_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint16_t number;
|
||||
uint16_t pid;
|
||||
uint16_t pcr_pid;
|
||||
uint64_t pcr;
|
||||
ngx_uint_t video; /* unisgned video:1; */
|
||||
ngx_uint_t nes;
|
||||
ngx_ts_es_t *es;
|
||||
ngx_chain_t *bufs; /* PMT */
|
||||
} ngx_ts_program_t;
|
||||
|
||||
|
||||
typedef struct ngx_ts_handler_s ngx_ts_handler_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t nprogs;
|
||||
ngx_ts_program_t *progs;
|
||||
ngx_log_t *log;
|
||||
ngx_pool_t *pool;
|
||||
ngx_buf_t *buf;
|
||||
ngx_chain_t *free;
|
||||
ngx_chain_t *bufs; /* PAT */
|
||||
ngx_ts_handler_t *handlers;
|
||||
void *data;
|
||||
} ngx_ts_stream_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_ts_event_e event;
|
||||
ngx_ts_stream_t *ts;
|
||||
ngx_ts_program_t *prog;
|
||||
ngx_ts_es_t *es;
|
||||
ngx_chain_t *bufs;
|
||||
void *data;
|
||||
} ngx_ts_handler_data_t;
|
||||
|
||||
|
||||
typedef ngx_int_t (*ngx_ts_handler_pt)(ngx_ts_handler_data_t *hd);
|
||||
|
||||
|
||||
struct ngx_ts_handler_s {
|
||||
ngx_ts_handler_pt handler;
|
||||
void *data;
|
||||
ngx_ts_handler_t *next;
|
||||
};
|
||||
|
||||
|
||||
ngx_int_t ngx_ts_add_handler(ngx_ts_stream_t *ts, ngx_ts_handler_pt handler,
|
||||
void *data);
|
||||
ngx_int_t ngx_ts_read(ngx_ts_stream_t *ts, ngx_chain_t *in);
|
||||
ngx_chain_t *ngx_ts_write_pat(ngx_ts_stream_t *ts, ngx_ts_program_t *prog);
|
||||
ngx_chain_t *ngx_ts_write_pmt(ngx_ts_stream_t *ts, ngx_ts_program_t *prog);
|
||||
ngx_chain_t *ngx_ts_write_pes(ngx_ts_stream_t *ts, ngx_ts_program_t *prog,
|
||||
ngx_ts_es_t *es, ngx_chain_t *bufs);
|
||||
void ngx_ts_free_chain(ngx_ts_stream_t *ts, ngx_chain_t **ll);
|
||||
ngx_uint_t ngx_ts_dash_get_oti(u_char type);
|
||||
|
||||
|
||||
#endif /* _NGX_TS_STREAM_H_INCLUDED_ */
|
||||
Reference in new issue
Block a user