mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-11-05 01:44:24 +08:00
update 2026-09-24 10:46:53
This commit is contained in:
1 parent
907a158127
commit
b31a5c3878
4914 files changed
-1716179
No files matched your search
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,95 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Pingo (cczjp89@gmail.com)
|
||||
*/
|
||||
|
||||
#ifndef _NGX_HLS_LIVE_MODULE_H_INCLUDE_
|
||||
#define _NGX_HLS_LIVE_MODULE_H_INCLUDE_
|
||||
|
||||
#define ngx_hls_live_next(s, pos) ((pos + 1) % s->out_queue)
|
||||
#define ngx_hls_live_prev(s, pos) (pos == 0 ? s->out_queue - 1 : pos - 1)
|
||||
|
||||
typedef struct ngx_hls_live_frag_s ngx_hls_live_frag_t;
|
||||
typedef struct ngx_hls_live_play_s ngx_hls_live_play_t;
|
||||
|
||||
struct ngx_hls_live_play_s {
|
||||
ngx_str_t name;
|
||||
/* connection parameters */
|
||||
ngx_rtmp_addr_conf_t *addr_conf;
|
||||
ngx_str_t app;
|
||||
ngx_str_t stream;
|
||||
ngx_str_t args;
|
||||
ngx_str_t flashver;
|
||||
ngx_str_t swf_url;
|
||||
ngx_str_t tc_url;
|
||||
uint32_t acodecs;
|
||||
uint32_t vcodecs;
|
||||
ngx_str_t page_url;
|
||||
ngx_str_t domain;
|
||||
ngx_str_t serverid;
|
||||
ngx_log_t *log;
|
||||
};
|
||||
|
||||
struct ngx_hls_live_frag_s {
|
||||
ngx_uint_t ref;
|
||||
ngx_hls_live_frag_t *next;
|
||||
time_t last_modified_time;
|
||||
uint64_t id;
|
||||
uint64_t key_id;
|
||||
double duration;
|
||||
unsigned active:1;
|
||||
unsigned discont:1; /* before */
|
||||
ngx_uint_t length;
|
||||
ngx_chain_t *out;
|
||||
ngx_uint_t content_last;
|
||||
ngx_uint_t content_pos;
|
||||
ngx_mpegts_frame_t *content[0];
|
||||
};
|
||||
|
||||
struct ngx_hls_live_ctx_s {
|
||||
unsigned opened:1;
|
||||
unsigned playing:1;
|
||||
|
||||
ngx_buf_t *patpmt;
|
||||
ngx_rtmp_session_t *session;
|
||||
|
||||
ngx_str_t sid;
|
||||
ngx_live_stream_t *stream;
|
||||
ngx_str_t name;
|
||||
|
||||
uint64_t nfrag;
|
||||
uint64_t frag_ts;
|
||||
uint64_t key_id;
|
||||
ngx_uint_t nfrags;
|
||||
ngx_hls_live_frag_t **frags; /* circular 2 * winfrags + 1 */
|
||||
ngx_hls_live_frag_t *frag;
|
||||
|
||||
ngx_uint_t audio_cc;
|
||||
ngx_uint_t video_cc;
|
||||
ngx_uint_t key_frags;
|
||||
|
||||
uint64_t aframe_base;
|
||||
uint64_t aframe_num;
|
||||
|
||||
ngx_buf_t *aframe;
|
||||
uint64_t aframe_pts;
|
||||
ngx_event_t ev;
|
||||
ngx_msec_t timeout;
|
||||
ngx_msec_t last_time;
|
||||
time_t playlist_modified_time;
|
||||
ngx_buf_t *playlist;
|
||||
ngx_hls_live_ctx_t *next;
|
||||
};
|
||||
|
||||
ngx_int_t ngx_hls_live_write_playlist(ngx_rtmp_session_t *s, ngx_buf_t *out,
|
||||
time_t *last_modified_time);
|
||||
ngx_hls_live_frag_t* ngx_hls_live_find_frag(ngx_rtmp_session_t *s,
|
||||
ngx_str_t *name);
|
||||
ngx_chain_t* ngx_hls_live_prepare_frag(ngx_rtmp_session_t *s,
|
||||
ngx_hls_live_frag_t *frag);
|
||||
void ngx_hls_live_free_frag(ngx_hls_live_frag_t *frag);
|
||||
ngx_rtmp_session_t* ngx_hls_live_fetch_session(ngx_str_t *server,
|
||||
ngx_str_t *stream, ngx_str_t *session);
|
||||
void ngx_rtmp_shared_acquire_frag(ngx_hls_live_frag_t *frag);
|
||||
|
||||
#endif
|
||||
@@ -1,731 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Pingo (cczjp89@gmail.com)
|
||||
*/
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include "ngx_rtmp.h"
|
||||
#include "ngx_rtmp_codec_module.h"
|
||||
#include "ngx_rtmp_live_module.h"
|
||||
#include "ngx_mpegts_live_module.h"
|
||||
|
||||
|
||||
static ngx_rtmp_close_stream_pt next_close_stream;
|
||||
|
||||
static ngx_mpegts_video_pt next_mpegts_video;
|
||||
static ngx_mpegts_audio_pt next_mpegts_audio;
|
||||
|
||||
static void *ngx_mpegts_gop_create_app_conf(ngx_conf_t *cf);
|
||||
static char *ngx_mpegts_gop_merge_app_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
|
||||
static ngx_int_t ngx_mpegts_gop_postconfiguration(ngx_conf_t *cf);
|
||||
|
||||
#define ngx_mpegts_gop_next(s, pos) ((pos + 1) % s->out_queue)
|
||||
#define ngx_mpegts_gop_prev(s, pos) (pos == 0 ? s->out_queue - 1 : pos - 1)
|
||||
|
||||
typedef struct {
|
||||
/* publisher: head of cache
|
||||
* player: cache send position of publisher's out
|
||||
*/
|
||||
size_t gop_pos;
|
||||
/* tail of cache */
|
||||
size_t gop_last;
|
||||
/* 0 for not send, 1 for sending, 2 for sent */
|
||||
ngx_flag_t send_gop;
|
||||
|
||||
ngx_mpegts_frame_t *keyframe;
|
||||
|
||||
ngx_uint_t meta_version;
|
||||
|
||||
uint64_t first_timestamp;
|
||||
uint64_t current_timestamp;
|
||||
|
||||
ngx_uint_t base_type;
|
||||
|
||||
/* only for publisher, must at last of ngx_mpegts_gop_ctx_t */
|
||||
ngx_mpegts_frame_t *cache[];
|
||||
} ngx_mpegts_gop_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
ngx_msec_t cache_time;
|
||||
ngx_msec_t roll_back;
|
||||
ngx_msec_t one_off_send;
|
||||
ngx_flag_t low_latency;
|
||||
ngx_flag_t send_all;
|
||||
ngx_msec_t fix_timestamp;
|
||||
ngx_flag_t zero_start;
|
||||
} ngx_mpegts_gop_app_conf_t;
|
||||
|
||||
|
||||
static ngx_command_t ngx_mpegts_gop_commands[] = {
|
||||
|
||||
{ ngx_string("mpegts_cache_time"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_RTMP_APP_CONF_OFFSET,
|
||||
offsetof(ngx_mpegts_gop_app_conf_t, cache_time),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("mpegts_roll_back"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_RTMP_APP_CONF_OFFSET,
|
||||
offsetof(ngx_mpegts_gop_app_conf_t, roll_back),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("mpegts_one_off_send"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_RTMP_APP_CONF_OFFSET,
|
||||
offsetof(ngx_mpegts_gop_app_conf_t, one_off_send),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("mpegts_low_latency"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_RTMP_APP_CONF_OFFSET,
|
||||
offsetof(ngx_mpegts_gop_app_conf_t, low_latency),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("mpegts_send_all"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_RTMP_APP_CONF_OFFSET,
|
||||
offsetof(ngx_mpegts_gop_app_conf_t, send_all),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("mpegts_fix_timestamp"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_RTMP_APP_CONF_OFFSET,
|
||||
offsetof(ngx_mpegts_gop_app_conf_t, fix_timestamp),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("mpegts_zero_start"),
|
||||
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_RTMP_APP_CONF_OFFSET,
|
||||
offsetof(ngx_mpegts_gop_app_conf_t, zero_start),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_rtmp_module_t ngx_mpegts_gop_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_mpegts_gop_postconfiguration, /* postconfiguration */
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
ngx_mpegts_gop_create_app_conf, /* create app configuration */
|
||||
ngx_mpegts_gop_merge_app_conf /* merge app configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_mpegts_gop_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_mpegts_gop_module_ctx, /* module context */
|
||||
ngx_mpegts_gop_commands, /* module directives */
|
||||
NGX_RTMP_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_mpegts_gop_create_app_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_mpegts_gop_app_conf_t *gacf;
|
||||
|
||||
gacf = ngx_pcalloc(cf->pool, sizeof(ngx_mpegts_gop_app_conf_t));
|
||||
if (gacf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gacf->cache_time = NGX_CONF_UNSET_MSEC;
|
||||
gacf->roll_back = NGX_CONF_UNSET_MSEC;
|
||||
gacf->one_off_send = NGX_CONF_UNSET_MSEC;
|
||||
gacf->low_latency = NGX_CONF_UNSET;
|
||||
gacf->send_all = NGX_CONF_UNSET;
|
||||
gacf->fix_timestamp = NGX_CONF_UNSET_MSEC;
|
||||
gacf->zero_start = NGX_CONF_UNSET;
|
||||
|
||||
return gacf;
|
||||
}
|
||||
|
||||
static char *
|
||||
ngx_mpegts_gop_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_mpegts_gop_app_conf_t *prev = parent;
|
||||
ngx_mpegts_gop_app_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_msec_value(conf->cache_time, prev->cache_time, 0);
|
||||
ngx_conf_merge_msec_value(conf->roll_back, prev->roll_back, conf->cache_time);
|
||||
ngx_conf_merge_msec_value(conf->one_off_send, prev->one_off_send, 3000);
|
||||
ngx_conf_merge_value(conf->low_latency, prev->low_latency, 0);
|
||||
ngx_conf_merge_value(conf->send_all, prev->send_all, 0);
|
||||
ngx_conf_merge_msec_value(conf->fix_timestamp, prev->fix_timestamp, 10000);
|
||||
ngx_conf_merge_value(conf->zero_start, prev->zero_start, 0);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_mpegts_gop_link_frame(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame)
|
||||
{
|
||||
ngx_uint_t nmsg;
|
||||
|
||||
if (frame == NULL) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
nmsg = (s->out_last - s->out_pos) % s->out_queue + 1;
|
||||
|
||||
if (nmsg >= s->out_queue) {
|
||||
ngx_log_error(NGX_LOG_ERR, s->log, 0,
|
||||
"link frame nmsg(%ui) >= out_queue(%O)", nmsg, s->out_queue);
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
#if 0
|
||||
ngx_log_error(NGX_LOG_DEBUG, s->log, 0,
|
||||
"link frame pos[%O] last[%O], pts[%ud]",
|
||||
s->out_pos, s->out_last, frame->pts);
|
||||
#endif
|
||||
|
||||
s->mpegts_out[s->out_last] = frame;
|
||||
s->out_last = ngx_mpegts_gop_next(s, s->out_last);
|
||||
|
||||
ngx_rtmp_shared_acquire_mpegts_frame(frame);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_mpegts_gop_reset_gop(ngx_rtmp_session_t *s, ngx_mpegts_gop_ctx_t *ctx,
|
||||
ngx_mpegts_frame_t *frame)
|
||||
{
|
||||
ngx_mpegts_gop_app_conf_t *gacf;
|
||||
ngx_mpegts_frame_t *f, *next_keyframe;
|
||||
size_t pos;
|
||||
ngx_uint_t nmsg;
|
||||
ngx_msec_t cache_time;
|
||||
|
||||
pos = ctx->gop_pos;
|
||||
|
||||
f = ctx->cache[pos];
|
||||
if (f == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
gacf = ngx_rtmp_get_module_app_conf(s, ngx_mpegts_gop_module);
|
||||
|
||||
cache_time = gacf->cache_time > gacf->roll_back ?
|
||||
gacf->cache_time : gacf->roll_back;
|
||||
|
||||
/* only audio in cache */
|
||||
if (ctx->keyframe == NULL) {
|
||||
if (frame->pts - ctx->cache[ctx->gop_pos]->pts > cache_time * 90) {
|
||||
ngx_rtmp_shared_free_mpegts_frame(f);
|
||||
ctx->cache[ctx->gop_pos] = NULL;
|
||||
ctx->gop_pos = ngx_mpegts_gop_next(s, ctx->gop_pos);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* only video of video + audio */
|
||||
next_keyframe = ctx->keyframe->next;
|
||||
|
||||
/* only one gop in cache */
|
||||
if (next_keyframe == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
nmsg = (ctx->gop_last - ctx->gop_pos) % s->out_queue + 2;
|
||||
if (nmsg >= s->out_queue) {
|
||||
goto reset;
|
||||
}
|
||||
|
||||
if (frame->type == NGX_MPEGTS_MSG_AUDIO) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame->type == NGX_MPEGTS_MSG_VIDEO && frame->pts
|
||||
- next_keyframe->pts < cache_time * 90)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
reset:
|
||||
for (pos = ctx->gop_pos; ctx->cache[pos] != next_keyframe;
|
||||
pos = ngx_mpegts_gop_next(s, pos))
|
||||
{
|
||||
f = ctx->cache[pos];
|
||||
|
||||
ngx_rtmp_shared_free_mpegts_frame(f);
|
||||
|
||||
ctx->cache[pos] = NULL;
|
||||
}
|
||||
|
||||
ctx->keyframe = next_keyframe;
|
||||
ctx->gop_pos = pos;
|
||||
}
|
||||
|
||||
static void
|
||||
ngx_mpegts_gop_print_cache(ngx_rtmp_session_t *s, ngx_mpegts_gop_ctx_t *ctx)
|
||||
{
|
||||
#if (NGX_DEBUG)
|
||||
ngx_mpegts_frame_t *frame;
|
||||
u_char content[10240], *p;
|
||||
size_t pos;
|
||||
|
||||
ngx_memzero(content, sizeof(content));
|
||||
|
||||
p = content;
|
||||
for (pos = ctx->gop_pos; pos != ctx->gop_last;
|
||||
pos = ngx_mpegts_gop_next(s, pos))
|
||||
{
|
||||
frame = ctx->cache[pos];
|
||||
switch (frame->type) {
|
||||
case NGX_MPEGTS_MSG_AUDIO:
|
||||
*p++ = 'A';
|
||||
break;
|
||||
case NGX_MPEGTS_MSG_VIDEO:
|
||||
*p++ = 'V';
|
||||
break;
|
||||
default:
|
||||
*p++ = 'O';
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame->key) {
|
||||
*p++ = 'I';
|
||||
}
|
||||
|
||||
*p++ = ' ';
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_DEBUG, s->log, 0,
|
||||
"[%z %z] %s", ctx->gop_pos, ctx->gop_last, content);
|
||||
#endif
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
ngx_mpegts_gop_cache(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame)
|
||||
{
|
||||
ngx_mpegts_gop_app_conf_t *gacf;
|
||||
ngx_mpegts_gop_ctx_t *ctx;
|
||||
ngx_mpegts_frame_t **keyframe;
|
||||
ngx_uint_t nmsg;
|
||||
|
||||
gacf = ngx_rtmp_get_module_app_conf(s, ngx_mpegts_gop_module);
|
||||
if (gacf->cache_time == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
|
||||
if (ctx == NULL) {
|
||||
ctx = ngx_pcalloc(s->pool, sizeof(ngx_mpegts_gop_ctx_t)
|
||||
+ s->out_queue * sizeof(ngx_mpegts_frame_t *));
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
ngx_rtmp_set_ctx(s, ctx, ngx_mpegts_gop_module);
|
||||
}
|
||||
|
||||
nmsg = (ctx->gop_last - ctx->gop_pos) % s->out_queue + 1;
|
||||
if (nmsg >= s->out_queue) {
|
||||
ngx_log_error(NGX_LOG_ERR, s->log, 0,
|
||||
"cache frame nmsg(%ui) >= out_queue(%z)", nmsg, s->out_queue);
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
#if 0
|
||||
ngx_log_error(NGX_LOG_DEBUG, s->log, 0,
|
||||
"cache frame: %ud[%d], %ud, %ud",
|
||||
frame->type, frame->key, frame->pts, frame->length);
|
||||
#endif
|
||||
/* first video frame is not intra_frame */
|
||||
if (ctx->keyframe == NULL && frame->type == NGX_MPEGTS_MSG_VIDEO
|
||||
&& !frame->key)
|
||||
{
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
/* video intra_frame */
|
||||
if (frame->key) {
|
||||
for (keyframe = &ctx->keyframe; *keyframe;
|
||||
keyframe = &((*keyframe)->next));
|
||||
*keyframe = frame;
|
||||
}
|
||||
|
||||
frame->pos = ctx->gop_last;
|
||||
ctx->current_timestamp = frame->pts;
|
||||
ctx->cache[ctx->gop_last] = frame;
|
||||
ctx->gop_last = ngx_mpegts_gop_next(s, ctx->gop_last);
|
||||
|
||||
ngx_rtmp_shared_acquire_mpegts_frame(frame);
|
||||
|
||||
ngx_mpegts_gop_reset_gop(s, ctx, frame);
|
||||
|
||||
ngx_mpegts_gop_print_cache(s, ctx);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_mpegts_gop_send_gop(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss)
|
||||
{
|
||||
ngx_mpegts_gop_app_conf_t *gacf;
|
||||
ngx_mpegts_gop_ctx_t *sctx, *ssctx;
|
||||
ngx_mpegts_frame_t *frame, *keyframe;
|
||||
size_t pos;
|
||||
|
||||
gacf = ngx_rtmp_get_module_app_conf(s, ngx_mpegts_gop_module);
|
||||
|
||||
sctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
|
||||
ssctx = ngx_rtmp_get_module_ctx(ss, ngx_mpegts_gop_module);
|
||||
|
||||
/* already send gop */
|
||||
if (ssctx->send_gop == 2) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
//
|
||||
// TODO: send pat pmt
|
||||
//
|
||||
//*****************************************************************
|
||||
|
||||
/* link frame in s to ss */
|
||||
if (ssctx->send_gop == 0) {
|
||||
ssctx->gop_pos = sctx->gop_pos;
|
||||
if (sctx->cache[ssctx->gop_pos] == NULL) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ssctx->send_gop = 1;
|
||||
ssctx->first_timestamp = sctx->cache[ssctx->gop_pos]->pts;
|
||||
ssctx->base_type = sctx->cache[ssctx->gop_pos]->type;
|
||||
} else {
|
||||
if (sctx->cache[ssctx->gop_pos] == NULL) {
|
||||
ssctx->gop_pos = sctx->gop_pos;
|
||||
}
|
||||
}
|
||||
|
||||
frame = NULL;
|
||||
|
||||
keyframe = sctx->keyframe;
|
||||
while (ss->roll_back && keyframe &&
|
||||
((sctx->current_timestamp - keyframe->pts) > ss->roll_back * 90))
|
||||
{
|
||||
ngx_log_error(NGX_LOG_INFO, s->log, 0,
|
||||
"rtmp-gop: send_gop| curr %D - k %D, %D",
|
||||
sctx->current_timestamp, keyframe->pts, ss->roll_back * 90);
|
||||
frame = keyframe;
|
||||
keyframe = keyframe->next;
|
||||
}
|
||||
|
||||
if (frame == NULL) {
|
||||
pos = ssctx->gop_pos;
|
||||
frame = sctx->cache[pos];
|
||||
} else {
|
||||
pos = frame->pos;
|
||||
}
|
||||
|
||||
while (frame) {
|
||||
|
||||
ngx_log_error(NGX_LOG_DEBUG, s->log, 0,
|
||||
"send gop link %D, type %d, curr %D",
|
||||
frame->pts/90, frame->type, sctx->current_timestamp/90);
|
||||
if (ngx_mpegts_gop_link_frame(ss, frame) == NGX_AGAIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!gacf->send_all && frame->type == ssctx->base_type &&
|
||||
frame->pts - ssctx->first_timestamp >= gacf->one_off_send * 90)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_DEBUG, s->log, 0,
|
||||
"gone %D, type %d, first %D, curr %D, send %D",
|
||||
frame->pts/90, frame->type, ssctx->first_timestamp/90,
|
||||
sctx->current_timestamp/90, gacf->one_off_send);
|
||||
|
||||
ssctx->send_gop = 2;
|
||||
pos = ngx_mpegts_gop_next(s, pos);
|
||||
break;
|
||||
}
|
||||
|
||||
pos = ngx_mpegts_gop_next(s, pos);
|
||||
frame = sctx->cache[pos];
|
||||
}
|
||||
|
||||
if (frame == NULL) { /* send all frame in cache */
|
||||
ssctx->send_gop = 2;
|
||||
}
|
||||
|
||||
ssctx->gop_pos = pos;
|
||||
ngx_rtmp_send_message(ss, NULL, 0);
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
ngx_mpegts_gop_send(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss)
|
||||
{
|
||||
ngx_mpegts_gop_app_conf_t *gacf;
|
||||
ngx_mpegts_gop_ctx_t *sctx, *ssctx;
|
||||
ngx_mpegts_frame_t *frame;
|
||||
|
||||
gacf = ngx_rtmp_get_module_app_conf(s, ngx_mpegts_gop_module);
|
||||
if (gacf->cache_time == 0) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
sctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
|
||||
if (sctx == NULL) { /* publisher doesn't publish av frame */
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
ssctx = ngx_rtmp_get_module_ctx(ss, ngx_mpegts_gop_module);
|
||||
if (ssctx == NULL) {
|
||||
ssctx = ngx_pcalloc(ss->pool, sizeof(ngx_mpegts_gop_ctx_t));
|
||||
if (ssctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
ngx_rtmp_set_ctx(ss, ssctx, ngx_mpegts_gop_module);
|
||||
}
|
||||
|
||||
if (ngx_mpegts_gop_send_gop(s, ss) == NGX_AGAIN) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (sctx->cache[ssctx->gop_pos] == NULL) {
|
||||
ngx_log_error(NGX_LOG_ERR, ss->log, 0,
|
||||
"mpegts-gop: gop_send| current gop pos is NULL, "
|
||||
"skip to new postion [pos %d last %d] %d",
|
||||
sctx->gop_pos, sctx->gop_last, ssctx->gop_pos);
|
||||
|
||||
ssctx->gop_pos = sctx->gop_pos;
|
||||
}
|
||||
|
||||
frame = sctx->cache[ssctx->gop_pos];
|
||||
if (ngx_mpegts_gop_link_frame(ss, frame) == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ssctx->gop_pos = ngx_mpegts_gop_next(s, ssctx->gop_pos);
|
||||
ngx_rtmp_send_message(ss, NULL, 0);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
ngx_mpegts_gop_offset_frames(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss,
|
||||
ngx_msec_t time_offset, ngx_msec_t duration)
|
||||
{
|
||||
ngx_mpegts_gop_ctx_t *sctx, *ssctx;
|
||||
ngx_mpegts_frame_t *frame, *keyframe;
|
||||
size_t pos;
|
||||
|
||||
sctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
|
||||
ssctx = ngx_rtmp_get_module_ctx(ss, ngx_mpegts_gop_module);
|
||||
|
||||
/* already send gop */
|
||||
if (ssctx->send_gop == 2) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
//
|
||||
// TODO: send pat pmt
|
||||
//
|
||||
//*****************************************************************
|
||||
|
||||
/* link frame in s to ss */
|
||||
if (ssctx->send_gop == 0) {
|
||||
ssctx->gop_pos = sctx->gop_pos;
|
||||
if (sctx->cache[ssctx->gop_pos] == NULL) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ssctx->send_gop = 1;
|
||||
ssctx->first_timestamp = sctx->cache[ssctx->gop_pos]->pts;
|
||||
ssctx->base_type = sctx->cache[ssctx->gop_pos]->type;
|
||||
} else {
|
||||
if (sctx->cache[ssctx->gop_pos] == NULL) {
|
||||
ssctx->gop_pos = sctx->gop_pos;
|
||||
}
|
||||
}
|
||||
|
||||
frame = NULL;
|
||||
|
||||
keyframe = sctx->keyframe;
|
||||
while (time_offset && keyframe &&
|
||||
((sctx->current_timestamp - keyframe->pts) > time_offset * 90))
|
||||
{
|
||||
ngx_log_error(NGX_LOG_DEBUG, ss->log, 0,
|
||||
"mpegts-gop: offset_frames| curr %D - k %D = %D, %D",
|
||||
sctx->current_timestamp, keyframe->pts,
|
||||
(sctx->current_timestamp - keyframe->pts) / 90, time_offset);
|
||||
frame = keyframe;
|
||||
keyframe = keyframe->next;
|
||||
ssctx->first_timestamp = frame->pts;
|
||||
ssctx->base_type = frame->type;
|
||||
}
|
||||
|
||||
if (frame == NULL) {
|
||||
pos = ssctx->gop_pos;
|
||||
frame = sctx->cache[pos];
|
||||
} else {
|
||||
pos = frame->pos;
|
||||
}
|
||||
|
||||
while (frame) {
|
||||
|
||||
if (ngx_mpegts_gop_link_frame(ss, frame) == NGX_AGAIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame->type == ssctx->base_type &&
|
||||
frame->pts - ssctx->first_timestamp >= duration * 90)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_INFO, ss->log, 0, "gone %D, first %D, curr %D",
|
||||
frame->pts, ssctx->first_timestamp, sctx->current_timestamp);
|
||||
ssctx->send_gop = 2;
|
||||
pos = ngx_mpegts_gop_next(s, pos);
|
||||
break;
|
||||
}
|
||||
|
||||
pos = ngx_mpegts_gop_next(s, pos);
|
||||
frame = sctx->cache[pos];
|
||||
}
|
||||
|
||||
if (frame == NULL) { /* send all frame in cache */
|
||||
ssctx->send_gop = 2;
|
||||
}
|
||||
|
||||
ssctx->gop_pos = pos;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ngx_int_t
|
||||
ngx_mpegts_gop_link(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss,
|
||||
ngx_msec_t time_offset, ngx_msec_t duration)
|
||||
{
|
||||
ngx_mpegts_gop_app_conf_t *gacf;
|
||||
ngx_mpegts_gop_ctx_t *sctx, *ssctx;
|
||||
ngx_mpegts_frame_t *frame;
|
||||
|
||||
gacf = ngx_rtmp_get_module_app_conf(s, ngx_mpegts_gop_module);
|
||||
if (gacf->cache_time == 0) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
sctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
|
||||
if (sctx == NULL) { /* publisher doesn't publish av frame */
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
ssctx = ngx_rtmp_get_module_ctx(ss, ngx_mpegts_gop_module);
|
||||
if (ssctx == NULL) {
|
||||
ssctx = ngx_pcalloc(ss->pool, sizeof(ngx_mpegts_gop_ctx_t));
|
||||
if (ssctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
ngx_rtmp_set_ctx(ss, ssctx, ngx_mpegts_gop_module);
|
||||
}
|
||||
|
||||
if (ngx_mpegts_gop_offset_frames(s, ss, time_offset, duration) == NGX_AGAIN)
|
||||
{
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
/* new frame is video key frame */
|
||||
if (sctx->cache[ssctx->gop_pos] == NULL) {
|
||||
ngx_log_error(NGX_LOG_ERR, ss->log, 0,
|
||||
"mpegts-gop: link| current gop pos is NULL, "
|
||||
"skip to new postion [pos %d last %d] %d",
|
||||
sctx->gop_pos, sctx->gop_last, ssctx->gop_pos);
|
||||
|
||||
ssctx->gop_pos = sctx->gop_pos;
|
||||
}
|
||||
|
||||
frame = sctx->cache[ssctx->gop_pos];
|
||||
if (ngx_mpegts_gop_link_frame(ss, frame) == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ssctx->gop_pos = ngx_mpegts_gop_next(s, ssctx->gop_pos);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_mpegts_gop_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v)
|
||||
{
|
||||
ngx_mpegts_gop_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_mpegts_gop_module);
|
||||
if (ctx == NULL) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (!s->published) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* free cache in publisher */
|
||||
while (ctx->gop_pos != ctx->gop_last) {
|
||||
ngx_rtmp_shared_free_mpegts_frame(ctx->cache[ctx->gop_pos]);
|
||||
ctx->gop_pos = ngx_mpegts_gop_next(s, ctx->gop_pos);
|
||||
}
|
||||
|
||||
next:
|
||||
return next_close_stream(s, v);
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
ngx_mpegts_gop_av(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame)
|
||||
{
|
||||
if (frame->type == NGX_MPEGTS_MSG_VIDEO) {
|
||||
ngx_mpegts_gop_cache(s, frame);
|
||||
|
||||
return next_mpegts_video(s, frame);
|
||||
} else if (frame->type == NGX_MPEGTS_MSG_AUDIO) {
|
||||
ngx_mpegts_gop_cache(s, frame);
|
||||
|
||||
return next_mpegts_audio(s, frame);
|
||||
}
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
ngx_mpegts_gop_postconfiguration(ngx_conf_t *cf)
|
||||
{
|
||||
next_close_stream = ngx_rtmp_close_stream;
|
||||
ngx_rtmp_close_stream = ngx_mpegts_gop_close_stream;
|
||||
|
||||
next_mpegts_video = ngx_mpegts_video;
|
||||
ngx_mpegts_video = ngx_mpegts_gop_av;
|
||||
|
||||
next_mpegts_audio = ngx_mpegts_audio;
|
||||
ngx_mpegts_audio = ngx_mpegts_gop_av;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Pingo (cczjp89@gmail.com)
|
||||
*/
|
||||
|
||||
#ifndef _NGX_MPEGTS_GOP_MODULE_H_INCLUDE_
|
||||
#define _NGX_MPEGTS_GOP_MODULE_H_INCLUDE_
|
||||
|
||||
#include "ngx_rtmp.h"
|
||||
|
||||
ngx_int_t
|
||||
ngx_mpegts_gop_cache(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame);
|
||||
ngx_int_t
|
||||
ngx_mpegts_gop_link(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss,
|
||||
ngx_msec_t time_offset, ngx_msec_t duration);
|
||||
ngx_int_t
|
||||
ngx_mpegts_gop_send(ngx_rtmp_session_t *s, ngx_rtmp_session_t *ss);
|
||||
|
||||
#endif
|
||||
@@ -1,611 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Pingo (cczjp89@gmail.com)
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
#include <ngx_http.h>
|
||||
#include "ngx_rtmp.h"
|
||||
#include "ngx_rtmp_cmd_module.h"
|
||||
#include "ngx_rbuf.h"
|
||||
#include "ngx_http_set_header.h"
|
||||
#include "ngx_rtmp_monitor_module.h"
|
||||
#include "ngx_mpegts_gop_module.h"
|
||||
#include "ngx_mpegts_live_module.h"
|
||||
|
||||
|
||||
static char *ngx_mpegts_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
|
||||
static void *ngx_mpegts_http_create_loc_conf(ngx_conf_t *cf);
|
||||
static char *ngx_mpegts_http_merge_loc_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
|
||||
static ngx_keyval_t ngx_mpegts_http_headers[] = {
|
||||
{ ngx_string("Cache-Control"), ngx_string("no-cache") },
|
||||
// { ngx_string("Content-Type"), ngx_string("video/x-ts") },
|
||||
{ ngx_null_string, ngx_null_string }
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ngx_rtmp_session_t *session;
|
||||
} ngx_mpegts_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_rtmp_addr_conf_t *addr_conf;
|
||||
} ngx_mpegts_http_loc_conf_t;
|
||||
|
||||
|
||||
static ngx_command_t ngx_mpegts_http_commands[] = {
|
||||
|
||||
{ ngx_string("ts_live"),
|
||||
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
|
||||
ngx_mpegts_http,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_mpegts_http_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
ngx_mpegts_http_create_loc_conf, /* create location configuration */
|
||||
ngx_mpegts_http_merge_loc_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_mpegts_http_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_mpegts_http_module_ctx, /* module context */
|
||||
ngx_mpegts_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 ngx_int_t
|
||||
ngx_mpegts_http_send_header(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_keyval_t *h;
|
||||
ngx_chain_t out;
|
||||
ngx_mpegts_http_ctx_t *ctx;
|
||||
ngx_rtmp_session_t *s, *ps;
|
||||
|
||||
if (r->header_sent) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_mpegts_http_module);
|
||||
s = ctx->session;
|
||||
ps = s->live_stream->publish_ctx->session;
|
||||
|
||||
r->headers_out.status = NGX_HTTP_OK;
|
||||
r->keepalive = 0; /* set Connection to closed */
|
||||
|
||||
h = ngx_mpegts_http_headers;
|
||||
while (h->key.len) {
|
||||
rc = ngx_http_set_header_out(r, &h->key, &h->value);
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
++h;
|
||||
}
|
||||
|
||||
rc = ngx_http_send_header(r);
|
||||
if (rc == NGX_ERROR || rc > NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
ngx_memzero(&out, sizeof(out));
|
||||
out.buf = ngx_create_temp_buf(s->pool, 376);
|
||||
out.buf->last = ngx_cpymem(out.buf->pos,
|
||||
ngx_rtmp_mpegts_pat, 188);
|
||||
|
||||
ngx_rtmp_mpegts_gen_pmt(ps->vcodec, ps->acodec, s->log, out.buf->last);
|
||||
out.buf->last += 188;
|
||||
|
||||
out.buf->flush = 1;
|
||||
out.buf->memory = 1;
|
||||
|
||||
return ngx_http_output_filter(r, &out);
|
||||
}
|
||||
|
||||
static void
|
||||
ngx_mpegts_http_write_handler(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_mpegts_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;
|
||||
|
||||
if (r->connection->destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_mpegts_http_module);
|
||||
s = ctx->session;
|
||||
|
||||
if (wev->timedout) {
|
||||
ngx_log_error(NGX_LOG_INFO, s->log, NGX_ETIMEDOUT,
|
||||
"mpegts-http: write_handler| client timed out");
|
||||
r->connection->timedout = 1;
|
||||
s->finalize_reason = NGX_LIVE_FLV_SEND_TIMEOUT;
|
||||
if (r->header_sent) {
|
||||
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
|
||||
} else {
|
||||
r->error_page = 1;
|
||||
ngx_http_finalize_request(r, NGX_HTTP_SERVICE_UNAVAILABLE);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (wev->timer_set) {
|
||||
ngx_del_timer(wev);
|
||||
}
|
||||
|
||||
if (ngx_rtmp_core_main_conf->fast_reload && (ngx_exiting || ngx_terminate)) {
|
||||
r->error_page = 1;
|
||||
ngx_http_finalize_request(r, NGX_HTTP_SERVICE_UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ngx_rtmp_prepare_merge_frame(s) == NGX_ERROR) {
|
||||
ngx_http_finalize_request(r, NGX_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->out_chain) {
|
||||
rc = ngx_mpegts_http_send_header(r);
|
||||
if (rc == NGX_ERROR || rc > NGX_OK) {
|
||||
s->finalize_reason = NGX_LIVE_FLV_SEND_ERR;
|
||||
ngx_http_finalize_request(r, rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (s->out_chain) {
|
||||
present = r->connection->sent;
|
||||
|
||||
if (r->connection->buffered) {
|
||||
rc = ngx_http_output_filter(r, NULL);
|
||||
} else {
|
||||
rc = ngx_http_output_filter(r, s->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, r->connection->log, ngx_errno,
|
||||
"mpegts-http: write_handler| write event failed");
|
||||
ngx_http_finalize_request(r, NGX_ERROR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
|
||||
"mpegts-http: write_handler| send error");
|
||||
s->finalize_reason = NGX_LIVE_FLV_SEND_ERR;
|
||||
ngx_http_finalize_request(r, NGX_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* NGX_OK */
|
||||
for (cl = s->out_chain; cl;) {
|
||||
s->out_chain = cl->next;
|
||||
ngx_free_chain(s->pool, cl);
|
||||
cl = s->out_chain;
|
||||
}
|
||||
|
||||
if (ngx_rtmp_prepare_merge_frame(s) == NGX_ERROR) {
|
||||
ngx_http_finalize_request(r, NGX_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (wev->active) {
|
||||
ngx_del_event(wev, NGX_WRITE_EVENT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_mpegts_http_parse_url(ngx_http_request_t *r, ngx_str_t *app,
|
||||
ngx_str_t *name)
|
||||
{
|
||||
u_char *p, *end, *pos;
|
||||
|
||||
p = r->uri.data + 1; /* skip '/' */
|
||||
end = r->uri.data + r->uri.len;
|
||||
app->data = p;
|
||||
|
||||
pos = (u_char *) ngx_strnstr(p, ".ts", end - p);
|
||||
if (pos) {
|
||||
end = pos;
|
||||
}
|
||||
|
||||
p = (u_char *) ngx_strnstr(p, "/", end - p);
|
||||
while (p) {
|
||||
name->data = p;
|
||||
p = (u_char *) ngx_strnstr(p + 1, "/", end - p);
|
||||
}
|
||||
|
||||
if (name->data == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
app->len = name->data - app->data;
|
||||
|
||||
++name->data;
|
||||
name->len = end - name->data;
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
ngx_mpegts_http_parse(ngx_http_request_t *r, ngx_rtmp_session_t *s,
|
||||
ngx_rtmp_play_t *v)
|
||||
{
|
||||
ngx_mpegts_http_loc_conf_t *hflcf;
|
||||
ngx_str_t app, stream;
|
||||
size_t tcurl_len;
|
||||
u_char *p;
|
||||
|
||||
hflcf = ngx_http_get_module_loc_conf(r, ngx_mpegts_http_module);
|
||||
|
||||
ngx_memzero(&app, sizeof(ngx_str_t));
|
||||
ngx_memzero(&stream, sizeof(ngx_str_t));
|
||||
|
||||
ngx_mpegts_http_parse_url(r, &app, &stream);
|
||||
|
||||
if (app.len == 0 || stream.len == 0 || stream.len > NGX_RTMP_MAX_NAME) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"mpegts-http: http_parse| url error: %V", &r->uri);
|
||||
return NGX_HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (hflcf->app.len) {
|
||||
app = hflcf->app;
|
||||
}
|
||||
|
||||
ngx_http_arg(r, (u_char *) "app", 3, &app);
|
||||
|
||||
s->app = app;
|
||||
|
||||
if (ngx_http_arg(r, (u_char *) "flashver", 8, &s->flashver) != NGX_OK) {
|
||||
s->flashver = hflcf->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 + 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, app.data, app.len);
|
||||
|
||||
/* page_url */
|
||||
if (r->headers_in.referer) {
|
||||
s->page_url = r->headers_in.referer->value;
|
||||
} else {
|
||||
s->page_url = hflcf->page_url;
|
||||
}
|
||||
|
||||
s->acodecs = 0x0DF7;
|
||||
s->vcodecs = 0xFC;
|
||||
|
||||
ngx_memcpy(v->name, stream.data, stream.len);
|
||||
|
||||
if (r->args.len) {
|
||||
ngx_memcpy(v->args, r->args.data,
|
||||
ngx_min(r->args.len, NGX_RTMP_MAX_ARGS));
|
||||
}
|
||||
|
||||
ngx_rtmp_cmd_middleware_init(s);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
ngx_mpegts_http_cleanup(void *data)
|
||||
{
|
||||
ngx_http_request_t *r;
|
||||
ngx_mpegts_http_ctx_t *ctx;
|
||||
|
||||
r = data;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_mpegts_http_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
|
||||
"mpegts-http: cleanup| cleanup");
|
||||
|
||||
if (ctx->session) {
|
||||
ctx->session->request = NULL;
|
||||
|
||||
if (ctx->session->finalize_reason == 0) {
|
||||
ctx->session->finalize_reason = r->connection->read->error?
|
||||
NGX_LIVE_FLV_RECV_ERR : NGX_LIVE_NORMAL_CLOSE;
|
||||
}
|
||||
|
||||
ngx_rtmp_finalize_fake_session(ctx->session);
|
||||
}
|
||||
}
|
||||
|
||||
static ngx_chain_t *
|
||||
ngx_mpegts_http_prepare_out_chain(ngx_rtmp_session_t *s)
|
||||
{
|
||||
ngx_mpegts_frame_t *frame;
|
||||
ngx_chain_t *head, **ll, *cl;
|
||||
|
||||
frame = NULL;
|
||||
head = NULL;
|
||||
|
||||
if (s->out_pos != s->out_last) {
|
||||
frame = s->mpegts_out[s->out_pos];
|
||||
}
|
||||
|
||||
/* no frame to send */
|
||||
if (frame == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ll = &head;
|
||||
|
||||
for (cl = frame->chain; cl; cl = cl->next) {
|
||||
(*ll) = ngx_get_chainbuf(0, 0);
|
||||
if (*ll == NULL) {
|
||||
goto falied;
|
||||
}
|
||||
(*ll)->buf->pos = cl->buf->pos;
|
||||
(*ll)->buf->last = cl->buf->last;
|
||||
if (!(*ll)->next) {
|
||||
(*ll)->buf->flush = 1;
|
||||
}
|
||||
|
||||
ll = &(*ll)->next;
|
||||
}
|
||||
|
||||
return head;
|
||||
|
||||
falied:
|
||||
ngx_put_chainbufs(head);
|
||||
|
||||
ngx_rtmp_finalize_session(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
ngx_mpegts_http_handler(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_mpegts_http_loc_conf_t *hflcf;
|
||||
ngx_mpegts_http_ctx_t *ctx;
|
||||
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_http_cleanup_t *cln;
|
||||
ngx_rtmp_core_main_conf_t *cmcf;
|
||||
|
||||
ctx = ngx_pcalloc(r->pool, sizeof(ngx_mpegts_http_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
ngx_http_set_ctx(r, ctx, ngx_mpegts_http_module);
|
||||
|
||||
/* cleanup handler */
|
||||
cln = ngx_http_cleanup_add(r, 0);
|
||||
if (cln == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
cln->handler = ngx_mpegts_http_cleanup;
|
||||
cln->data = r;
|
||||
|
||||
hflcf = ngx_http_get_module_loc_conf(r, ngx_mpegts_http_module);
|
||||
|
||||
/* create fake session */
|
||||
s = ngx_rtmp_create_session(hflcf->addr_conf);
|
||||
if (s == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
ctx->session = s;
|
||||
s->connection = r->connection;
|
||||
ngx_rtmp_set_combined_log(s, r->connection->log->data,
|
||||
r->connection->log->handler);
|
||||
s->log->connection = r->connection->number;
|
||||
s->number = r->connection->number;
|
||||
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);
|
||||
|
||||
/* get host, app, stream name */
|
||||
ngx_memzero(&v, sizeof(ngx_rtmp_play_t));
|
||||
rc = ngx_mpegts_http_parse(r, s, &v);
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ngx_rtmp_set_virtual_server(s, &s->domain)) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
|
||||
|
||||
s->live_type = NGX_MPEGTS_LIVE;
|
||||
s->live_server = ngx_live_create_server(&s->serverid);
|
||||
s->request = r;
|
||||
|
||||
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,
|
||||
"mpegts-http: http_handler| "
|
||||
"application not found '%V'", &s->app);
|
||||
return NGX_HTTP_NOT_FOUND;
|
||||
}
|
||||
|
||||
s->app_conf = cscf->default_app->app_conf;
|
||||
}
|
||||
|
||||
s->prepare_handler = ngx_mpegts_http_prepare_out_chain;
|
||||
|
||||
s->stage = NGX_LIVE_PLAY;
|
||||
s->ptime = ngx_current_msec;
|
||||
|
||||
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 NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_rtmp_play_filter(s, &v) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ngx_add_timer(r->connection->write, s->timeout);
|
||||
|
||||
r->read_event_handler = ngx_http_test_reading;
|
||||
r->write_event_handler = ngx_mpegts_http_write_handler;
|
||||
|
||||
++r->count;
|
||||
|
||||
return NGX_DONE;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_mpegts_http_create_loc_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_mpegts_http_loc_conf_t *hflcf;
|
||||
|
||||
hflcf = ngx_pcalloc(cf->pool, sizeof(ngx_mpegts_http_loc_conf_t));
|
||||
if (hflcf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return hflcf;
|
||||
}
|
||||
|
||||
static char *
|
||||
ngx_mpegts_http_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_mpegts_http_loc_conf_t *prev = parent;
|
||||
ngx_mpegts_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 char *
|
||||
ngx_mpegts_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_core_loc_conf_t *clcf;
|
||||
ngx_mpegts_http_loc_conf_t *hflcf;
|
||||
ngx_str_t *value, v;
|
||||
ngx_uint_t i;
|
||||
|
||||
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
|
||||
clcf->handler = ngx_mpegts_http_handler;
|
||||
|
||||
hflcf = conf;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
hflcf->addr_conf = ngx_rtmp_find_related_addr_conf(cf->cycle, &value[1]);
|
||||
if (hflcf->addr_conf == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
for (i = 2; i < cf->args->nelts; ++i) {
|
||||
if (ngx_strncmp(value[i].data, "app=", 4) == 0) {
|
||||
v.data = value[i].data + 4;
|
||||
v.len = value[i].len - 4;
|
||||
hflcf->app = v;
|
||||
} else {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
-1891
File diff suppressed because it is too large.
Load diff
@@ -1,29 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Pingo (cczjp89@gmail.com)
|
||||
*/
|
||||
|
||||
#ifndef _NGX_RTMP_MPEGTS_MODULE_H
|
||||
#define _NGX_RTMP_MPEGTS_MODULE_H
|
||||
|
||||
#include "ngx_rtmp.h"
|
||||
|
||||
typedef ngx_int_t (*ngx_mpegts_video_pt)(ngx_rtmp_session_t *s,
|
||||
ngx_mpegts_frame_t *frame);
|
||||
typedef ngx_int_t (*ngx_mpegts_audio_pt)(ngx_rtmp_session_t *s,
|
||||
ngx_mpegts_frame_t *frame);
|
||||
|
||||
extern ngx_mpegts_video_pt ngx_mpegts_video;
|
||||
extern ngx_mpegts_audio_pt ngx_mpegts_audio;
|
||||
|
||||
ngx_int_t
|
||||
ngx_rtmp_mpegts_gen_pmt(ngx_int_t vcodec, ngx_int_t acodec,
|
||||
ngx_log_t *log, u_char *pmt);
|
||||
ngx_int_t
|
||||
ngx_mpegts_live_video_filter(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame);
|
||||
ngx_int_t
|
||||
ngx_mpegts_live_audio_filter(ngx_rtmp_session_t *s, ngx_mpegts_frame_t *frame);
|
||||
|
||||
extern u_char ngx_rtmp_mpegts_pat[];
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user