1304 lines
37 KiB
C
1304 lines
37 KiB
C
|
|
/*
|
|
* Copyright (C) Pingo (cczjp89@gmail.com)
|
|
*/
|
|
|
|
#include <ngx_config.h>
|
|
#include <ngx_core.h>
|
|
#include <ngx_http.h>
|
|
#include <ngx_rtmp.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <ngx_rtmp_cmd_module.h>
|
|
#include "ngx_http_set_header.h"
|
|
#include "ngx_mpegts_live_module.h"
|
|
#include "ngx_hls_live_module.h"
|
|
#include "ngx_rbuf.h"
|
|
#include "ngx_rtmp_dynamic.h"
|
|
|
|
#ifndef NGX_HTTP_GONE
|
|
#define NGX_HTTP_GONE 410
|
|
#endif
|
|
|
|
static ngx_keyval_t ngx_302_headers[] = {
|
|
{ ngx_string("Location"), ngx_null_string },
|
|
{ ngx_null_string, ngx_null_string }
|
|
};
|
|
|
|
static ngx_keyval_t ngx_m3u8_headers[] = {
|
|
{ ngx_string("Cache-Control"), ngx_string("no-cache") },
|
|
{ ngx_string("Content-Type"), ngx_string("application/vnd.apple.mpegurl") },
|
|
{ ngx_null_string, ngx_null_string }
|
|
};
|
|
|
|
static ngx_keyval_t ngx_ts_headers[] = {
|
|
{ ngx_string("Cache-Control"), ngx_string("no-cache") },
|
|
{ ngx_string("Content-Type"), ngx_string("video/mp2t") },
|
|
{ ngx_null_string, ngx_null_string }
|
|
};
|
|
|
|
|
|
typedef struct {
|
|
ngx_str_t app;
|
|
ngx_str_t name;
|
|
ngx_str_t stream;
|
|
ngx_str_t serverid;
|
|
ngx_str_t sid;
|
|
ngx_rtmp_session_t *session;
|
|
ngx_msec_t timeout;
|
|
ngx_uint_t content_pos;
|
|
ngx_chain_t *m3u8;
|
|
ngx_uint_t out_pos;
|
|
ngx_uint_t out_last;
|
|
ngx_chain_t *out_chain;
|
|
ngx_hls_live_frag_t *frag;
|
|
} ngx_hls_http_ctx_t;
|
|
|
|
|
|
typedef struct {
|
|
ngx_str_t app;
|
|
ngx_str_t flashver;
|
|
ngx_str_t swf_url;
|
|
ngx_str_t tc_url;
|
|
ngx_str_t page_url;
|
|
ngx_msec_t timeout;
|
|
ngx_rtmp_addr_conf_t *addr_conf;
|
|
} ngx_hls_http_loc_conf_t;
|
|
|
|
static u_char NGX_HLS_LIVE_ARG_SESSION[] = "session";
|
|
|
|
static ngx_int_t NGX_HLS_LIVE_ARG_SESSION_LENGTH = 7;
|
|
|
|
static void * ngx_hls_http_create_loc_conf(ngx_conf_t *cf);
|
|
static char * ngx_hls_http_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
|
|
static char * ngx_http_hls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
|
//static char * ngx_hls_http_variant(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
|
static ngx_int_t ngx_hls_http_postconfiguration(ngx_conf_t *cf);
|
|
|
|
static ngx_command_t ngx_hls_http_commands[] = {
|
|
|
|
{ ngx_string("hls2_live"),
|
|
NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
|
|
ngx_http_hls,
|
|
NGX_HTTP_LOC_CONF_OFFSET,
|
|
0,
|
|
NULL },
|
|
|
|
ngx_null_command
|
|
};
|
|
|
|
static ngx_http_module_t ngx_hls_http_module_ctx = {
|
|
NULL, /* preconfiguration */
|
|
ngx_hls_http_postconfiguration, /* postconfiguration */
|
|
|
|
NULL, /* create main configuration */
|
|
NULL, /* init main configuration */
|
|
|
|
NULL, /* create server configuration */
|
|
NULL, /* merge server configuration */
|
|
|
|
ngx_hls_http_create_loc_conf, /* create location configuration */
|
|
ngx_hls_http_merge_loc_conf /* merge location configuration */
|
|
};
|
|
|
|
ngx_module_t ngx_hls_http_module = {
|
|
NGX_MODULE_V1,
|
|
&ngx_hls_http_module_ctx, /* module context */
|
|
ngx_hls_http_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 void *
|
|
ngx_hls_http_create_loc_conf(ngx_conf_t *cf)
|
|
{
|
|
ngx_hls_http_loc_conf_t *hlcf;
|
|
|
|
hlcf = ngx_pcalloc(cf->pool, sizeof(ngx_hls_http_loc_conf_t));
|
|
if (hlcf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return hlcf;
|
|
}
|
|
|
|
static char *
|
|
ngx_hls_http_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
|
{
|
|
ngx_hls_http_loc_conf_t *prev = parent;
|
|
ngx_hls_http_loc_conf_t *conf = child;
|
|
|
|
ngx_conf_merge_str_value(conf->app, prev->app, "");
|
|
ngx_conf_merge_str_value(conf->flashver, prev->flashver, "");
|
|
ngx_conf_merge_str_value(conf->swf_url, prev->swf_url, "");
|
|
ngx_conf_merge_str_value(conf->tc_url, prev->tc_url, "");
|
|
ngx_conf_merge_str_value(conf->page_url, prev->page_url, "");
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_ctx_init(ngx_http_request_t *r, ngx_rtmp_addr_conf_t *addr_conf)
|
|
{
|
|
u_char *p, *e;
|
|
ngx_buf_t *buf;
|
|
ngx_rtmp_core_srv_conf_t *cscf;
|
|
ngx_hls_http_loc_conf_t *hlcf;
|
|
ngx_hls_http_ctx_t *ctx;
|
|
ngx_str_t *app, *name, *stream, *domain, *serverid;
|
|
ngx_int_t rc;
|
|
|
|
hlcf = ngx_http_get_module_loc_conf(r, ngx_hls_http_module);
|
|
ctx = ngx_http_get_module_ctx(r, ngx_hls_http_module);
|
|
|
|
app = &ctx->app;
|
|
name = &ctx->name;
|
|
stream = &ctx->stream;
|
|
serverid = &ctx->serverid;
|
|
domain = &r->headers_in.server;
|
|
|
|
p = r->uri.data;
|
|
e = r->uri.data + r->uri.len;
|
|
|
|
p++;
|
|
app->data = p;
|
|
p = ngx_strlchr(p, e, '/');
|
|
if (p == NULL) {
|
|
app->data = NULL;
|
|
app->len = 0;
|
|
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: ctx_init| invalid uri, lack of app");
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
app->len = p - app->data;
|
|
|
|
if (hlcf->app.len > 0 && hlcf->app.data) {
|
|
*app = hlcf->app;
|
|
}
|
|
|
|
p++;
|
|
name->data = p;
|
|
if (ngx_strncmp(&e[-5], ".m3u8", 5) == 0) {
|
|
p = ngx_strlchr(p, e, '.');
|
|
} else if (ngx_strncmp(&e[-3], ".ts", 3) == 0) {
|
|
p = ngx_strlchr(p, e, '-');
|
|
} else {
|
|
p = NULL;
|
|
}
|
|
|
|
if (p == NULL) {
|
|
name->data = NULL;
|
|
name->len = 0;
|
|
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: ctx_init| invalid uri, lack of name");
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
name->len = p - name->data;
|
|
|
|
cscf = addr_conf->default_server->
|
|
ctx->srv_conf[ngx_rtmp_core_module.ctx_index];
|
|
|
|
rc = ngx_rtmp_find_virtual_server(addr_conf->virtual_names, domain, &cscf);
|
|
if (rc != NGX_OK && rc != NGX_DECLINED) {
|
|
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: ctx_init| server(%V) not found.", domain);
|
|
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
if (cscf && cscf->serverid.len) {
|
|
serverid->data = ngx_pcalloc(r->connection->pool, cscf->serverid.len);
|
|
if (serverid->data == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: ctx_init| pcalloc failed.");
|
|
return NGX_ERROR;
|
|
}
|
|
serverid->len = cscf->serverid.len;
|
|
ngx_memcpy(serverid->data, cscf->serverid.data, cscf->serverid.len);
|
|
} else {
|
|
*serverid = *domain;
|
|
}
|
|
|
|
buf = ngx_create_temp_buf(r->connection->pool,
|
|
serverid->len + 1 + app->len + name->len + 1);
|
|
buf->last = ngx_slprintf(buf->start, buf->end,
|
|
"%V/%V/%V", serverid, app, name);
|
|
|
|
stream->data = buf->pos;
|
|
stream->len = buf->last - buf->pos;
|
|
|
|
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
|
|
"hls-http: ctx_init| hls stream (%V).", stream);
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_send_header(ngx_http_request_t *r, ngx_uint_t status, ngx_keyval_t *h)
|
|
{
|
|
ngx_int_t rc;
|
|
|
|
r->headers_out.status = status;
|
|
// r->keepalive = 0; /* set Connection to closed */
|
|
|
|
//set eTag
|
|
if (ngx_http_set_etag(r) != NGX_OK) {
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
while (h && h->key.len) {
|
|
rc = ngx_http_set_header_out(r, &h->key, &h->value);
|
|
if (rc != NGX_OK) {
|
|
return rc;
|
|
}
|
|
++h;
|
|
}
|
|
|
|
return ngx_http_send_header(r);
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_master_m3u8_handler(ngx_http_request_t *r,
|
|
ngx_rtmp_addr_conf_t *addr_conf)
|
|
{
|
|
|
|
ngx_int_t rc;
|
|
ngx_str_t m3u8_url;
|
|
ngx_str_t host;
|
|
u_char sstr[NGX_RTMP_MAX_SESSION] = {0};
|
|
static ngx_uint_t sindex = 0;
|
|
ngx_str_t location = ngx_string("");
|
|
ngx_str_t uri;
|
|
ngx_str_t uri_tail;
|
|
ngx_buf_t *m3u8;
|
|
ngx_chain_t out;
|
|
ngx_str_t scheme = ngx_string("");
|
|
ngx_str_t http = ngx_string("http");
|
|
ngx_str_t https = ngx_string("https");
|
|
u_char *p;
|
|
|
|
host = r->headers_in.host->value;
|
|
|
|
rc = ngx_http_discard_request_body(r);
|
|
if (rc != NGX_OK) {
|
|
return rc;
|
|
}
|
|
|
|
*ngx_snprintf(sstr, sizeof(sstr) - 1, "%uDt-%uDi-%dp-%uDc",
|
|
time(NULL), sindex++, ngx_process_slot, r->connection->number) = 0;
|
|
|
|
ngx_http_arg(r, (u_char*)"location", 8, &location);
|
|
if (location.len == 0) {
|
|
uri = r->uri;
|
|
} else {
|
|
uri_tail.data =
|
|
ngx_strlchr(r->uri.data + 1, r->uri.data + r->uri.len - 1, '/');
|
|
if (uri_tail.data == NULL) {
|
|
uri_tail = r->uri;
|
|
} else {
|
|
uri_tail.len = r->uri.data+r->uri.len - uri_tail.data;
|
|
}
|
|
|
|
uri.len = location.len + uri_tail.len;
|
|
uri.data = ngx_pcalloc(r->pool, uri.len);
|
|
if (uri.data == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: master_m3u8_handler| pcalloc uri buffer failed");
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
ngx_snprintf(uri.data, uri.len, "%V%V", &location, &uri_tail);
|
|
}
|
|
|
|
ngx_http_arg(r, (u_char*)"scheme", 6, &scheme);
|
|
if (scheme.len == 0) {
|
|
#if (NGX_HTTP_SSL)
|
|
if (r->connection->ssl) {
|
|
scheme = https;
|
|
} else
|
|
#endif
|
|
{
|
|
scheme = http;
|
|
}
|
|
}
|
|
|
|
m3u8_url.len = scheme.len + ngx_strlen("://") +
|
|
host.len +
|
|
uri.len +
|
|
NGX_HLS_LIVE_ARG_SESSION_LENGTH + 2 +
|
|
ngx_strlen(sstr);
|
|
|
|
if (r->args.len > 0) {
|
|
m3u8_url.len += 1 + r->args.len;
|
|
}
|
|
|
|
m3u8_url.data = ngx_pcalloc(r->connection->pool, m3u8_url.len);
|
|
|
|
p = ngx_snprintf(m3u8_url.data, m3u8_url.len, "%V://%V%V?%s=%s",
|
|
&scheme, &host, &uri, NGX_HLS_LIVE_ARG_SESSION, sstr);
|
|
|
|
if (r->args.len > 0) {
|
|
ngx_slprintf(p, m3u8_url.data + m3u8_url.len, "&%V", &r->args);
|
|
}
|
|
|
|
m3u8 = ngx_create_temp_buf(r->connection->pool, 64 * 1024);
|
|
m3u8->memory = 1;
|
|
m3u8->flush = 1;
|
|
out.buf = m3u8;
|
|
out.next = NULL;
|
|
|
|
m3u8->last = ngx_snprintf(m3u8->pos, m3u8->end - m3u8->start,
|
|
"#EXTM3U\n"
|
|
"#EXT-X-STREAM-INF:BANDWIDTH=1,AVERAGE-BANDWIDTH=1\n"
|
|
//"#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1\n"
|
|
"%V\n", &m3u8_url);
|
|
|
|
r->headers_out.content_length_n = m3u8->last - m3u8->pos;
|
|
|
|
rc = ngx_hls_http_send_header(r, NGX_HTTP_OK, ngx_m3u8_headers);
|
|
if (rc != NGX_OK) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: master_m3u8_handler| "
|
|
"send header failed, redirect url: %V, rc=%d", &m3u8_url, rc);
|
|
} else {
|
|
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
"hls-http: master_m3u8_handler| redirect url %V", &m3u8_url);
|
|
}
|
|
|
|
return ngx_http_output_filter(r, &out);
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_redirect_handler(ngx_http_request_t *r,
|
|
ngx_rtmp_addr_conf_t *addr_conf)
|
|
{
|
|
ngx_int_t rc;
|
|
ngx_str_t m3u8_url;
|
|
ngx_str_t host;
|
|
u_char sstr[NGX_RTMP_MAX_SESSION] = {0};
|
|
static ngx_uint_t sindex = 0;
|
|
ngx_str_t location = ngx_string("");
|
|
ngx_str_t uri;
|
|
ngx_str_t uri_tail;
|
|
ngx_str_t scheme = ngx_string("");
|
|
ngx_str_t http = ngx_string("http");
|
|
ngx_str_t https = ngx_string("https");
|
|
u_char *p;
|
|
|
|
host = r->headers_in.host->value;
|
|
|
|
rc = ngx_http_discard_request_body(r);
|
|
if (rc != NGX_OK) {
|
|
return rc;
|
|
}
|
|
|
|
*ngx_snprintf(sstr, sizeof(sstr) - 1, "%uDt-%uDi-%dp-%uDc",
|
|
time(NULL), sindex++, ngx_process_slot, r->connection->number) = 0;
|
|
|
|
ngx_http_arg(r, (u_char*)"location", 8, &location);
|
|
if (location.len == 0) {
|
|
uri = r->uri;
|
|
} else {
|
|
uri_tail.data =
|
|
ngx_strlchr(r->uri.data + 1, r->uri.data + r->uri.len - 1, '/');
|
|
if (uri_tail.data == NULL) {
|
|
uri_tail = r->uri;
|
|
} else {
|
|
uri_tail.len = r->uri.data+r->uri.len - uri_tail.data;
|
|
}
|
|
|
|
uri.len = location.len + uri_tail.len;
|
|
uri.data = ngx_pcalloc(r->pool, uri.len);
|
|
if (uri.data == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: redirect_handler| pcalloc uri buffer failed");
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
ngx_snprintf(uri.data, uri.len, "%V%V", &location, &uri_tail);
|
|
}
|
|
|
|
ngx_http_arg(r, (u_char*)"scheme", 6, &scheme);
|
|
if (scheme.len == 0) {
|
|
#if (NGX_HTTP_SSL)
|
|
if (r->connection->ssl) {
|
|
scheme = https;
|
|
} else
|
|
#endif
|
|
{
|
|
scheme = http;
|
|
}
|
|
}
|
|
|
|
m3u8_url.len = scheme.len + ngx_strlen("://") +
|
|
host.len +
|
|
uri.len +
|
|
NGX_HLS_LIVE_ARG_SESSION_LENGTH + 2 +
|
|
ngx_strlen(sstr);
|
|
|
|
if (r->args.len > 0) {
|
|
m3u8_url.len += 1 + r->args.len;
|
|
}
|
|
|
|
m3u8_url.data = ngx_pcalloc(r->connection->pool, m3u8_url.len);
|
|
|
|
p = ngx_snprintf(m3u8_url.data, m3u8_url.len, "%V://%V%V?%s=%s",
|
|
&scheme, &host, &uri, NGX_HLS_LIVE_ARG_SESSION, sstr);
|
|
|
|
if (r->args.len > 0) {
|
|
ngx_slprintf(p, m3u8_url.data + m3u8_url.len, "&%V", &r->args);
|
|
}
|
|
|
|
ngx_http_set_header_out(r, &ngx_302_headers[0].key, &m3u8_url);
|
|
|
|
r->headers_out.content_length_n = 0;
|
|
r->header_only = 1;
|
|
|
|
rc = ngx_hls_http_send_header(r, NGX_HTTP_MOVED_TEMPORARILY, NULL);
|
|
if (rc != NGX_OK) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: redirect_handler| "
|
|
"send header failed, redirect url: %V, rc=%d", &m3u8_url, rc);
|
|
} else {
|
|
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
"hls-http: redirect_handler| redirect url %V", &m3u8_url);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static ngx_hls_http_ctx_t *
|
|
ngx_hls_http_create_ctx(ngx_http_request_t *r, ngx_rtmp_addr_conf_t *addr_conf)
|
|
{
|
|
ngx_hls_http_ctx_t *ctx;
|
|
ngx_rtmp_core_srv_conf_t *cscf;
|
|
u_char *p;
|
|
|
|
cscf = addr_conf->default_server->ctx->
|
|
srv_conf[ngx_rtmp_core_module.ctx_index];
|
|
|
|
ctx = ngx_http_get_module_ctx(r, ngx_hls_http_module);
|
|
if (ctx == NULL) {
|
|
p = ngx_pcalloc(r->connection->pool, sizeof(ngx_hls_http_ctx_t));
|
|
|
|
if (p == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: create_ctx| alloc hls live ctx failed");
|
|
return NULL;
|
|
}
|
|
|
|
ctx = (ngx_hls_http_ctx_t *)p;
|
|
|
|
ngx_http_set_ctx(r, ctx, ngx_hls_http_module);
|
|
}
|
|
|
|
ctx->timeout = cscf->timeout;
|
|
|
|
ngx_hls_http_ctx_init(r, addr_conf);
|
|
|
|
if (ctx->app.len == 0 || ctx->name.len == 0) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: create_ctx| parse app or name failed, uri %V", &r->uri);
|
|
return NULL;
|
|
}
|
|
|
|
ngx_http_arg(r, NGX_HLS_LIVE_ARG_SESSION, NGX_HLS_LIVE_ARG_SESSION_LENGTH,
|
|
&ctx->sid);
|
|
|
|
return ctx;
|
|
}
|
|
|
|
|
|
static void
|
|
ngx_hls_http_cleanup(void *data)
|
|
{
|
|
ngx_http_request_t *r;
|
|
ngx_hls_http_ctx_t *ctx;
|
|
ngx_chain_t *cl;
|
|
|
|
r = data;
|
|
ctx = ngx_http_get_module_ctx(r, ngx_hls_http_module);
|
|
|
|
if (!ctx) {
|
|
return;
|
|
}
|
|
|
|
if (ctx->out_chain == NULL) {
|
|
cl = ctx->out_chain;
|
|
while (cl) {
|
|
ctx->out_chain = cl->next;
|
|
ngx_put_chainbuf(cl);
|
|
cl = ctx->out_chain;
|
|
}
|
|
}
|
|
|
|
ctx->content_pos = 0;
|
|
|
|
if (ctx->session) {
|
|
ctx->session->request = NULL;
|
|
ctx->session->connection = NULL;
|
|
}
|
|
|
|
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
|
|
"hls-http: cleanup_handler| http cleanup");
|
|
|
|
if (ctx->frag) {
|
|
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
|
|
"hls-http: cleanup_handler| free frag[%p]", ctx->frag);
|
|
ngx_hls_live_free_frag(ctx->frag);
|
|
ctx->frag = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_parse(ngx_http_request_t *r, ngx_rtmp_session_t *s,
|
|
ngx_rtmp_play_t *v)
|
|
{
|
|
ngx_hls_http_loc_conf_t *hlcf;
|
|
size_t tcurl_len;
|
|
u_char *p;
|
|
ngx_hls_http_ctx_t *ctx;
|
|
|
|
ctx = ngx_http_get_module_ctx(r, ngx_hls_http_module);
|
|
|
|
hlcf = ngx_http_get_module_loc_conf(r, ngx_hls_http_module);
|
|
|
|
#define NGX_HLS_HTTP_SET_VAL(_val) \
|
|
s->_val.data = ngx_pcalloc(s->pool, ctx->_val.len); \
|
|
s->_val.len = ctx->_val.len; \
|
|
ngx_memcpy(s->_val.data, ctx->_val.data, ctx->_val.len)
|
|
|
|
NGX_HLS_HTTP_SET_VAL(app);
|
|
NGX_HLS_HTTP_SET_VAL(stream);
|
|
|
|
#undef NGX_HLS_HTTP_SET_VAL
|
|
|
|
if (ngx_http_arg(r, (u_char *) "flashver", 8, &s->flashver) != NGX_OK) {
|
|
s->flashver = hlcf->flashver;
|
|
}
|
|
|
|
/* tc_url */
|
|
#if (NGX_HTTP_SSL)
|
|
if (r->connection->ssl) {
|
|
tcurl_len = sizeof("https://") - 1;
|
|
} else
|
|
#endif
|
|
{
|
|
tcurl_len = sizeof("http://") - 1;
|
|
}
|
|
tcurl_len += r->headers_in.server.len + 1 + s->app.len;
|
|
|
|
s->tc_url.len = tcurl_len;
|
|
s->tc_url.data = ngx_pcalloc(r->pool, tcurl_len);
|
|
if (s->tc_url.data == NULL) {
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
p = s->tc_url.data;
|
|
|
|
#if (NGX_HTTP_SSL)
|
|
if (r->connection->ssl) {
|
|
p = ngx_cpymem(p, "https://", sizeof("https://") - 1);
|
|
} else
|
|
#endif
|
|
{
|
|
p = ngx_cpymem(p, "http://", sizeof("http://") - 1);
|
|
}
|
|
|
|
p = ngx_cpymem(p, r->headers_in.server.data, r->headers_in.server.len);
|
|
*p++ = '/';
|
|
p = ngx_cpymem(p, s->app.data, s->app.len);
|
|
|
|
/* page_url */
|
|
if (r->headers_in.referer) {
|
|
s->page_url = r->headers_in.referer->value;
|
|
} else {
|
|
s->page_url = hlcf->page_url;
|
|
}
|
|
|
|
s->acodecs = 0x0DF7;
|
|
s->vcodecs = 0xFC;
|
|
|
|
ngx_memcpy(v->name, ctx->name.data, ctx->name.len);
|
|
|
|
if (r->args.len) {
|
|
ngx_memcpy(v->args, r->args.data,
|
|
ngx_min(r->args.len, NGX_RTMP_MAX_ARGS));
|
|
}
|
|
|
|
ngx_memcpy(v->session, ctx->sid.data, ctx->sid.len);
|
|
|
|
ngx_rtmp_cmd_middleware_init(s);
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static ngx_rtmp_session_t*
|
|
ngx_hls_http_create_session(ngx_http_request_t *r)
|
|
{
|
|
ngx_hls_http_loc_conf_t *hlcf;
|
|
ngx_rtmp_session_t *s;
|
|
ngx_rtmp_play_t v;
|
|
ngx_int_t rc;
|
|
ngx_uint_t n;
|
|
ngx_rtmp_core_srv_conf_t *cscf;
|
|
ngx_rtmp_core_app_conf_t **cacfp;
|
|
ngx_rtmp_core_main_conf_t *cmcf;
|
|
|
|
hlcf = ngx_http_get_module_loc_conf(r, ngx_hls_http_module);
|
|
|
|
/* create fake session */
|
|
s = ngx_rtmp_create_session(hlcf->addr_conf);
|
|
if (s == NULL) {
|
|
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
"hls-http: create_session| create session failed");
|
|
return NULL;
|
|
}
|
|
|
|
/* get host, app, stream name */
|
|
ngx_memzero(&v, sizeof(ngx_rtmp_play_t));
|
|
|
|
rc = ngx_hls_http_parse(r, s, &v);
|
|
if (rc != NGX_OK) {
|
|
return NULL;
|
|
}
|
|
|
|
if (ngx_rtmp_set_virtual_server(s, &s->domain)) {
|
|
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
"hls-http: create_session| set virtual server failed, %V",
|
|
&s->domain);
|
|
return NULL;
|
|
}
|
|
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
|
|
|
|
s->log->connection = r->connection->number;
|
|
s->number = r->connection->number;
|
|
s->live_type = NGX_HLS_LIVE;
|
|
s->live_server = ngx_live_create_server(&s->serverid);
|
|
s->remote_addr_text.data = ngx_pcalloc(s->pool, r->connection->addr_text.len);
|
|
s->remote_addr_text.len = r->connection->addr_text.len;
|
|
ngx_memcpy(s->remote_addr_text.data,
|
|
r->connection->addr_text.data, r->connection->addr_text.len);
|
|
|
|
v.silent = 1;
|
|
|
|
cacfp = cscf->applications.elts;
|
|
for (n = 0; n < cscf->applications.nelts; ++n, ++cacfp) {
|
|
if ((*cacfp)->name.len == s->app.len &&
|
|
ngx_strncmp((*cacfp)->name.data, s->app.data, s->app.len) == 0)
|
|
{
|
|
/* found app! */
|
|
s->app_conf = (*cacfp)->app_conf;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (s->app_conf == NULL) {
|
|
|
|
if (cscf->default_app == NULL || cscf->default_app->app_conf == NULL) {
|
|
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
"hls-http: create_session| application not found '%V'", &s->app);
|
|
|
|
ngx_rtmp_finalize_fake_session(s);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
s->app_conf = cscf->default_app->app_conf;
|
|
}
|
|
|
|
s->stage = NGX_LIVE_PLAY;
|
|
s->ptime = ngx_current_msec;
|
|
// s->connection = r->connection;
|
|
|
|
cmcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_core_module);
|
|
s->variables = ngx_pcalloc(s->pool, cmcf->variables.nelts
|
|
* sizeof(ngx_http_variable_value_t));
|
|
if (s->variables == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (ngx_rtmp_play_filter(s, &v) != NGX_OK) {
|
|
return NULL;
|
|
}
|
|
|
|
ngx_add_timer(r->connection->write, s->timeout);
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_m3u8_handler(ngx_http_request_t *r, ngx_rtmp_addr_conf_t *addr_conf)
|
|
{
|
|
ngx_hls_http_ctx_t *ctx;
|
|
ngx_int_t rc;
|
|
ngx_rtmp_session_t *s;
|
|
ngx_chain_t *out;
|
|
ngx_buf_t *buf;
|
|
|
|
ctx = ngx_hls_http_create_ctx(r, addr_conf);
|
|
if (ctx == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: m3u8_handler| create ctx failed");
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
s = ngx_hls_live_fetch_session(&ctx->serverid, &ctx->stream, &ctx->sid);
|
|
if (s == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: m3u8_handler| hls session %V not found", &ctx->sid);
|
|
|
|
s = ngx_hls_http_create_session(r);
|
|
if (s == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: m3u8_handler| create hls session %V error",
|
|
&ctx->sid);
|
|
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
s->sockaddr = ngx_pcalloc(s->pool, sizeof(struct sockaddr));
|
|
ngx_memcpy(s->sockaddr, r->connection->sockaddr, sizeof(struct sockaddr));
|
|
}
|
|
|
|
s->request = r;
|
|
|
|
ctx->session = s;
|
|
|
|
if (!ctx->m3u8) {
|
|
ctx->m3u8 = ngx_pcalloc(r->connection->pool, sizeof(ngx_chain_t));
|
|
ctx->m3u8->buf = ngx_create_temp_buf(r->connection->pool, 1024*512);
|
|
}
|
|
|
|
out = ctx->m3u8;
|
|
buf = out->buf;
|
|
buf->last = buf->pos = buf->start;
|
|
buf->memory = 1;
|
|
buf->flush = 1;
|
|
// buf->last_in_chain = 1;
|
|
// buf->last_buf = 1;
|
|
|
|
rc = ngx_hls_live_write_playlist(s, buf, &r->headers_out.last_modified_time);
|
|
if (rc != NGX_OK) {
|
|
goto again;
|
|
}
|
|
|
|
r->headers_out.content_length_n = buf->last - buf->pos;
|
|
|
|
s->out_bytes += r->headers_out.content_length_n;
|
|
|
|
if (!r->header_sent) {
|
|
rc = ngx_hls_http_send_header(r, NGX_HTTP_OK, ngx_m3u8_headers);
|
|
if (rc != NGX_OK) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: m3u8_handler| send http header failed");
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
rc = ngx_http_output_filter(r, out);
|
|
if (rc != NGX_OK) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: m3u8_handler| send http content failed");
|
|
return rc;
|
|
}
|
|
|
|
return rc;
|
|
|
|
again:
|
|
r->count++;
|
|
|
|
return NGX_DONE;
|
|
}
|
|
|
|
|
|
static u_char*
|
|
ngx_hls_http_strrchr(ngx_str_t *str, u_char c)
|
|
{
|
|
u_char *s, *e;
|
|
|
|
s = str->data;
|
|
e = str->data + str->len;
|
|
|
|
e--;
|
|
while(e != s) {
|
|
if (*e == c) {
|
|
break;
|
|
}
|
|
e--;
|
|
}
|
|
|
|
if (e == s) {
|
|
return NULL;
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_parse_frag(ngx_http_request_t *r, ngx_str_t *name)
|
|
{
|
|
u_char *s, *e;
|
|
|
|
e = ngx_hls_http_strrchr(&r->uri, '?');
|
|
if (e == NULL) {
|
|
e = r->uri.data + r->uri.len;
|
|
}
|
|
|
|
s = ngx_hls_http_strrchr(&r->uri, '/');
|
|
if (s == NULL) {
|
|
s = r->uri.data;
|
|
} else {
|
|
s++;
|
|
}
|
|
|
|
name->data = s;
|
|
name->len = e - s;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_chain_t *
|
|
ngx_hls_http_prepare_out_chain(ngx_http_request_t *r, ngx_int_t nframes)
|
|
{
|
|
ngx_hls_http_ctx_t *ctx;
|
|
ngx_hls_live_frag_t *frag;
|
|
ngx_rtmp_session_t *s;
|
|
ngx_chain_t *out, *cl, **ll;
|
|
ngx_mpegts_frame_t *frame;
|
|
ngx_int_t i = 0;
|
|
|
|
ctx = ngx_http_get_module_ctx(r, ngx_hls_http_module);
|
|
s = ctx->session;
|
|
frag = ctx->frag;
|
|
|
|
out = NULL;
|
|
|
|
ll = &out;
|
|
while (i < nframes && ctx->content_pos != frag->content_last) {
|
|
frame = frag->content[ctx->content_pos];
|
|
|
|
for (cl = frame->chain; cl; cl = cl->next) {
|
|
*ll = ngx_get_chainbuf(0, 0);
|
|
(*ll)->buf->pos = cl->buf->pos;
|
|
(*ll)->buf->last = cl->buf->last;
|
|
(*ll)->buf->flush = 1;
|
|
|
|
ll = &(*ll)->next;
|
|
}
|
|
|
|
*ll = NULL;
|
|
ctx->content_pos = ngx_hls_live_next(s, ctx->content_pos);
|
|
i++;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
static void
|
|
ngx_hls_http_write_handler(ngx_http_request_t *r)
|
|
{
|
|
ngx_hls_http_ctx_t *ctx;
|
|
ngx_rtmp_session_t *s;
|
|
ngx_event_t *wev;
|
|
size_t present, sent;
|
|
ngx_int_t rc;
|
|
ngx_chain_t *cl;
|
|
|
|
wev = r->connection->write; //wev->handler = ngx_http_request_handler;
|
|
|
|
if (r->connection->destroyed) {
|
|
return;
|
|
}
|
|
|
|
ctx = ngx_http_get_module_ctx(r, ngx_hls_http_module);
|
|
s = ctx->session;
|
|
|
|
if (wev->timedout) {
|
|
ngx_log_error(NGX_LOG_INFO, s->log, NGX_ETIMEDOUT,
|
|
"hls_http: write_handler| client timed out");
|
|
r->connection->timedout = 1;
|
|
if (r->header_sent) {
|
|
ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
|
|
} else {
|
|
r->error_page = 1;
|
|
ngx_http_finalize_request(r, NGX_HTTP_SERVICE_UNAVAILABLE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (wev->timer_set) {
|
|
ngx_del_timer(wev);
|
|
}
|
|
|
|
if (ctx->out_chain == NULL) {
|
|
ctx->out_chain = ngx_hls_http_prepare_out_chain(r, 4);
|
|
}
|
|
|
|
rc = NGX_OK;
|
|
|
|
while (ctx->out_chain) {
|
|
present = r->connection->sent;
|
|
|
|
if (r->connection->buffered) {
|
|
rc = ngx_http_output_filter(r, NULL);
|
|
} else {
|
|
rc = ngx_http_output_filter(r, ctx->out_chain);
|
|
}
|
|
|
|
sent = r->connection->sent - present;
|
|
|
|
ngx_rtmp_update_bandwidth(&ngx_rtmp_bw_out, sent);
|
|
|
|
if (rc == NGX_AGAIN) {
|
|
ngx_add_timer(wev, s->timeout);
|
|
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
|
|
ngx_log_error(NGX_LOG_ERR, s->log, ngx_errno,
|
|
"hls_http: write_handler| handle write event failed");
|
|
ngx_http_finalize_request(r, NGX_ERROR);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (rc == NGX_ERROR) {
|
|
ngx_log_error(NGX_LOG_ERR, s->log, ngx_errno,
|
|
"hls_http: write_handler| send error");
|
|
ngx_http_finalize_request(r, NGX_ERROR);
|
|
|
|
return;
|
|
}
|
|
|
|
/* NGX_OK */
|
|
cl = ctx->out_chain;
|
|
while (cl) {
|
|
ctx->out_chain = cl->next;
|
|
ngx_put_chainbuf(cl);
|
|
cl = ctx->out_chain;
|
|
}
|
|
|
|
if (ctx->frag->content_pos == ctx->frag->content_last) {
|
|
ctx->out_chain = NULL;
|
|
break;
|
|
}
|
|
|
|
ctx->out_chain = ngx_hls_http_prepare_out_chain(r, 4);
|
|
}
|
|
|
|
if (wev->active) {
|
|
ngx_del_event(wev, NGX_WRITE_EVENT, 0);
|
|
}
|
|
|
|
ngx_http_finalize_request(r, NGX_HTTP_OK);
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_ts_handler(ngx_http_request_t *r, ngx_rtmp_addr_conf_t *addr_conf)
|
|
{
|
|
ngx_hls_http_ctx_t *ctx;
|
|
ngx_rtmp_session_t *s;
|
|
ngx_hls_live_frag_t *frag;
|
|
ngx_int_t rc;
|
|
ngx_str_t name;
|
|
|
|
ctx = ngx_hls_http_create_ctx(r, addr_conf);
|
|
if (ctx == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: ts_handler| create ctx failed");
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
s = ngx_hls_live_fetch_session(&ctx->serverid, &ctx->stream, &ctx->sid);
|
|
if (s == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: ts_handler| hls session %V not found", &ctx->sid);
|
|
return NGX_DECLINED;
|
|
}
|
|
ctx->session = s;
|
|
s->request = r;
|
|
|
|
// ngx_rtmp_set_combined_log(s, r->connection->log->data,
|
|
// r->connection->log->handler);
|
|
|
|
rc = ngx_hls_http_parse_frag(r, &name);
|
|
if (rc != NGX_OK) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: ts_handler| parse frag args failed %V", &r->uri);
|
|
return NGX_HTTP_NOT_ALLOWED;
|
|
}
|
|
|
|
frag = ngx_hls_live_find_frag(s, &name);
|
|
if (frag == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: ts_handler| ts not found, %V", &r->uri);
|
|
return NGX_HTTP_NOT_FOUND;
|
|
}
|
|
|
|
ctx->frag = frag;
|
|
|
|
r->headers_out.content_length_n = frag->length;
|
|
r->headers_out.last_modified_time = frag->last_modified_time;
|
|
s->out_bytes += r->headers_out.content_length_n;
|
|
|
|
rc = ngx_hls_http_send_header(r, NGX_HTTP_OK, ngx_ts_headers);
|
|
if (rc != NGX_OK) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: ts_handler| send http header failed, %V", &r->uri);
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
ngx_rtmp_shared_acquire_frag(frag);
|
|
|
|
if (1) {
|
|
r->write_event_handler = ngx_hls_http_write_handler;
|
|
|
|
r->count++;
|
|
|
|
ngx_hls_http_write_handler(r);
|
|
|
|
return NGX_DONE;
|
|
} else {
|
|
ctx->out_chain = ngx_hls_live_prepare_frag(s, frag);
|
|
|
|
ngx_rtmp_update_bandwidth(&ngx_rtmp_bw_out, frag->length);
|
|
|
|
return ngx_http_output_filter(r, ctx->out_chain);
|
|
}
|
|
}
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_handler(ngx_http_request_t *r)
|
|
{
|
|
ngx_hls_http_loc_conf_t *hlcf;
|
|
ngx_int_t rc;
|
|
ngx_http_cleanup_t *cln;
|
|
ngx_str_t sstr;
|
|
ngx_hls_http_ctx_t *ctx;
|
|
|
|
hlcf = ngx_http_get_module_loc_conf(r, ngx_hls_http_module);
|
|
|
|
rc = ngx_http_discard_request_body(r);
|
|
|
|
if (rc != NGX_OK) {
|
|
return rc;
|
|
}
|
|
|
|
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: live_handler| donnot support the method");
|
|
return NGX_HTTP_NOT_ALLOWED;
|
|
}
|
|
|
|
if (ngx_rtmp_core_main_conf->fast_reload && (ngx_exiting || ngx_terminate)) {
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
if (r->uri.len < 4) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: live_handler| donnot support the file type");
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
ctx = ngx_http_get_module_ctx(r, ngx_hls_http_module);
|
|
if (!ctx) {
|
|
cln = ngx_http_cleanup_add(r, 0);
|
|
if (cln == NULL) {
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
cln->handler = ngx_hls_http_cleanup;
|
|
cln->data = r;
|
|
r->read_event_handler = ngx_http_test_reading;
|
|
}
|
|
|
|
if(!ngx_strncmp(r->uri.data + r->uri.len - 5, ".m3u8", 5)) {
|
|
|
|
rc = ngx_http_arg(r, NGX_HLS_LIVE_ARG_SESSION,
|
|
NGX_HLS_LIVE_ARG_SESSION_LENGTH, &sstr);
|
|
|
|
if (rc != NGX_OK || sstr.len == 0) {
|
|
if (1) {
|
|
return ngx_hls_http_master_m3u8_handler(r, hlcf->addr_conf);
|
|
} else {
|
|
return ngx_hls_http_redirect_handler(r, hlcf->addr_conf);
|
|
}
|
|
} else {
|
|
return ngx_hls_http_m3u8_handler(r, hlcf->addr_conf);
|
|
}
|
|
|
|
} else if (!ngx_strncmp(r->uri.data + r->uri.len - 3, ".ts", 3)) {
|
|
return ngx_hls_http_ts_handler(r, hlcf->addr_conf);
|
|
}
|
|
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_m3u8(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in)
|
|
{
|
|
ngx_http_request_t *r;
|
|
ngx_int_t rc;
|
|
ngx_hls_http_ctx_t *ctx;
|
|
ngx_buf_t *buf;
|
|
ngx_chain_t *out;
|
|
|
|
r = s->request;
|
|
if (!r) {
|
|
return NGX_ERROR;
|
|
}
|
|
|
|
ctx = ngx_http_get_module_ctx(r, ngx_hls_http_module);
|
|
|
|
if (!ctx->m3u8) {
|
|
ctx->m3u8 = ngx_pcalloc(r->connection->pool, sizeof(ngx_chain_t));
|
|
ctx->m3u8->buf = ngx_create_temp_buf(r->connection->pool, 1024*512);
|
|
}
|
|
|
|
out = ctx->m3u8;
|
|
buf = out->buf;
|
|
buf->last = buf->pos = buf->start;
|
|
buf->memory = 1;
|
|
buf->flush = 1;
|
|
// buf->last_in_chain = 1;
|
|
// buf->last_buf = 1;
|
|
|
|
ngx_hls_live_write_playlist(s, buf, &r->headers_out.last_modified_time);
|
|
|
|
r->headers_out.content_length_n = buf->last - buf->pos;
|
|
|
|
s->out_bytes += r->headers_out.content_length_n;
|
|
|
|
rc = ngx_hls_http_send_header(r, NGX_HTTP_OK, ngx_m3u8_headers);
|
|
if (rc != NGX_OK) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: m3u8| send http header failed");
|
|
return rc;
|
|
}
|
|
|
|
rc = ngx_http_output_filter(r, out);
|
|
if (rc != NGX_OK) {
|
|
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
"hls-http: m3u8| send http content failed");
|
|
return rc;
|
|
}
|
|
|
|
ngx_http_finalize_request(r, NGX_HTTP_OK);
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_close(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in)
|
|
{
|
|
ngx_http_request_t *r;
|
|
ngx_hls_http_ctx_t *ctx;
|
|
|
|
r = s->request;
|
|
if (!r) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
ctx = ngx_http_get_module_ctx(r, ngx_hls_http_module);
|
|
if (!ctx) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
s->request = NULL;
|
|
s->connection = NULL;
|
|
ctx->session = NULL;
|
|
|
|
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
|
"hls-http: close| finalize http request");
|
|
|
|
ngx_http_finalize_request(r, NGX_HTTP_GONE);
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
|
|
static char *
|
|
ngx_http_hls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
|
{
|
|
ngx_http_core_loc_conf_t *clcf;
|
|
ngx_hls_http_loc_conf_t *hlcf;
|
|
ngx_str_t *value;
|
|
ngx_uint_t n;
|
|
|
|
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
|
|
clcf->handler = ngx_hls_http_handler;
|
|
|
|
hlcf = conf;
|
|
|
|
value = cf->args->elts;
|
|
|
|
hlcf->addr_conf = ngx_rtmp_find_related_addr_conf(cf->cycle, &value[1]);
|
|
if (hlcf->addr_conf == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
for (n = 2; n < cf->args->nelts; ++n) {
|
|
#define PARSE_CONF_ARGS(arg) \
|
|
{ \
|
|
size_t len = sizeof(#arg"=") - 1; \
|
|
if (ngx_memcmp(value[n].data, #arg"=", len) == 0) { \
|
|
hlcf->arg.data = value[n].data + len; \
|
|
hlcf->arg.len = value[n].len - len; \
|
|
continue; \
|
|
} \
|
|
}
|
|
|
|
PARSE_CONF_ARGS(app);
|
|
PARSE_CONF_ARGS(flashver);
|
|
PARSE_CONF_ARGS(swf_url);
|
|
PARSE_CONF_ARGS(tc_url);
|
|
PARSE_CONF_ARGS(page_url);
|
|
|
|
#undef PARSE_CONF_ARGS
|
|
|
|
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
"\"%V\" para not support", &value[n]);
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
static ngx_int_t
|
|
ngx_hls_http_postconfiguration(ngx_conf_t *cf)
|
|
{
|
|
ngx_rtmp_core_main_conf_t *cmcf;
|
|
ngx_rtmp_handler_pt *h;
|
|
|
|
cmcf = ngx_rtmp_core_main_conf;
|
|
|
|
if (!cmcf) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
h = ngx_array_push(&cmcf->events[NGX_MPEGTS_MSG_M3U8]);
|
|
*h = ngx_hls_http_m3u8;
|
|
|
|
h = ngx_array_push(&cmcf->events[NGX_MPEGTS_MSG_CLOSE]);
|
|
*h = ngx_hls_http_close;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|