[-]
[+]
|
Changed |
nginx-1.4.changes
|
|
[-]
[+]
|
Changed |
nginx-1.4.spec
^
|
|
[-]
[+]
|
Changed |
nginx-rtmp-module-0.9.20.tar.bz2/hls/ngx_rtmp_hls_module.c
^
|
@@ -15,8 +15,6 @@
static ngx_rtmp_stream_eof_pt next_stream_eof;
-static char *ngx_rtmp_hls_path(ngx_conf_t *cf, ngx_command_t *cmd,
- void *conf);
static ngx_int_t ngx_rtmp_hls_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_hls_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf,
@@ -84,6 +82,7 @@
ngx_path_t *slot;
ngx_msec_t max_audio_delay;
size_t audio_buffer_size;
+ ngx_flag_t cleanup;
} ngx_rtmp_hls_app_conf_t;
@@ -136,9 +135,9 @@
{ ngx_string("hls_path"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
- ngx_rtmp_hls_path,
+ ngx_conf_set_str_slot,
NGX_RTMP_APP_CONF_OFFSET,
- 0,
+ offsetof(ngx_rtmp_hls_app_conf_t, path),
NULL },
{ ngx_string("hls_playlist_length"),
@@ -204,6 +203,13 @@
offsetof(ngx_rtmp_hls_app_conf_t, audio_buffer_size),
NULL },
+ { ngx_string("hls_cleanup"),
+ 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_rtmp_hls_app_conf_t, cleanup),
+ NULL },
+
ngx_null_command
};
@@ -1793,51 +1799,6 @@
}
-static char *
-ngx_rtmp_hls_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
-{
- ngx_rtmp_hls_app_conf_t *hacf = conf;
-
- ngx_rtmp_hls_cleanup_t *cleanup;
- ngx_str_t *value;
-
- if (hacf->slot) {
- return "is duplicate";
- }
-
- value = cf->args->elts;
- hacf->path = value[1];
-
- if (hacf->path.data[hacf->path.len - 1] == '/') {
- hacf->path.len--;
- }
-
- cleanup = ngx_pcalloc(cf->pool, sizeof(*cleanup));
- if (cleanup == NULL) {
- return NGX_CONF_ERROR;
- }
-
- cleanup->path = hacf->path;
-
- hacf->slot = ngx_pcalloc(cf->pool, sizeof(*hacf->slot));
- if (hacf->slot == NULL) {
- return NGX_CONF_ERROR;
- }
-
- hacf->slot->manager = ngx_rtmp_hls_cleanup;
- hacf->slot->name = hacf->path;
- hacf->slot->data = cleanup;
- hacf->slot->conf_file = cf->conf_file->file.name.data;
- hacf->slot->line = cf->conf_file->line;
-
- if (ngx_add_path(cf, &hacf->slot) != NGX_OK) {
- return NGX_CONF_ERROR;
- }
-
- return NGX_CONF_OK;
-}
-
-
static void *
ngx_rtmp_hls_create_app_conf(ngx_conf_t *cf)
{
@@ -1860,6 +1821,7 @@
conf->slicing = NGX_CONF_UNSET_UINT;
conf->max_audio_delay = NGX_CONF_UNSET;
conf->audio_buffer_size = NGX_CONF_UNSET;
+ conf->cleanup = NGX_CONF_UNSET;
return conf;
}
@@ -1890,16 +1852,43 @@
300);
ngx_conf_merge_size_value(conf->audio_buffer_size, prev->audio_buffer_size,
NGX_RTMP_HLS_BUFSIZE);
+ ngx_conf_merge_value(conf->cleanup, prev->cleanup, 1);
if (conf->fraglen) {
conf->winfrags = conf->playlen / conf->fraglen;
}
- if (conf->slot) {
- cleanup = conf->slot->data;
- if (cleanup->playlen < conf->playlen) {
- cleanup->playlen = conf->playlen;
- }
+ /* schedule cleanup */
+
+ if (conf->path.len == 0 || !conf->cleanup) {
+ return NGX_CONF_OK;
+ }
+
+ if (conf->path.data[conf->path.len - 1] == '/') {
+ conf->path.len--;
+ }
+
+ cleanup = ngx_pcalloc(cf->pool, sizeof(*cleanup));
+ if (cleanup == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ cleanup->path = conf->path;
+ cleanup->playlen = conf->playlen;
+
+ conf->slot = ngx_pcalloc(cf->pool, sizeof(*conf->slot));
+ if (conf->slot == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->slot->manager = ngx_rtmp_hls_cleanup;
+ conf->slot->name = conf->path;
+ conf->slot->data = cleanup;
+ conf->slot->conf_file = cf->conf_file->file.name.data;
+ conf->slot->line = cf->conf_file->line;
+
+ if (ngx_add_path(cf, &conf->slot) != NGX_OK) {
+ return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
|
[-]
[+]
|
Changed |
nginx-rtmp-module-0.9.20.tar.bz2/ngx_rtmp_play_module.c
^
|
@@ -140,6 +140,8 @@
return NULL;
}
+ pacf->nbuckets = 1024;
+
return pacf;
}
@@ -155,12 +157,12 @@
ngx_conf_merge_str_value(conf->local_path, prev->local_path, "");
if (prev->entries.nelts == 0) {
- return NGX_CONF_OK;
+ goto done;
}
if (conf->entries.nelts == 0) {
conf->entries = prev->entries;
- return NGX_CONF_OK;
+ goto done;
}
ppe = ngx_array_push_n(&conf->entries, prev->entries.nelts);
@@ -170,10 +172,91 @@
ngx_memcpy(ppe, prev->entries.elts, prev->entries.nelts * sizeof(void *));
+done:
+
+ if (conf->entries.nelts == 0) {
+ return NGX_CONF_OK;
+ }
+
+ conf->ctx = ngx_pcalloc(cf->pool, sizeof(void *) * conf->nbuckets);
+ if (conf->ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
return NGX_CONF_OK;
}
+static ngx_int_t
+ngx_rtmp_play_join(ngx_rtmp_session_t *s)
+{
+ ngx_rtmp_play_ctx_t *ctx, **pctx;
+ ngx_rtmp_play_app_conf_t *pacf;
+ ngx_uint_t h;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "play: join");
+
+ pacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_play_module);
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module);
+ if (ctx == NULL || ctx->joined) {
+ return NGX_ERROR;
+ }
+
+ h = ngx_hash_key(ctx->name, ngx_strlen(ctx->name));
+ pctx = &pacf->ctx[h % pacf->nbuckets];
+
+ while (*pctx) {
+ if (!ngx_strncmp((*pctx)->name, ctx->name, NGX_RTMP_MAX_NAME)) {
+ break;
+ }
+ pctx = &(*pctx)->next;
+ }
+
+ ctx->next = *pctx;
+ *pctx = ctx;
+ ctx->joined = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_play_leave(ngx_rtmp_session_t *s)
+{
+ ngx_rtmp_play_ctx_t *ctx, **pctx;
+ ngx_rtmp_play_app_conf_t *pacf;
+ ngx_uint_t h;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "play: leave");
+
+ pacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_play_module);
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module);
+ if (ctx == NULL || !ctx->joined) {
+ return NGX_ERROR;
+ }
+
+ h = ngx_hash_key(ctx->name, ngx_strlen(ctx->name));
+ pctx = &pacf->ctx[h % pacf->nbuckets];
+
+ while (*pctx && *pctx != ctx) {
+ pctx = &(*pctx)->next;
+ }
+
+ if (*pctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ *pctx = (*pctx)->next;
+ ctx->joined = 0;
+
+ return NGX_OK;
+}
+
+
static void
ngx_rtmp_play_send(ngx_event_t *e)
{
@@ -475,6 +558,8 @@
ngx_rtmp_play_cleanup_local_file(s);
}
+ ngx_rtmp_play_leave(s);
+
next:
return next_close_stream(s, v);
}
@@ -644,11 +729,14 @@
ngx_memzero(ctx, sizeof(*ctx));
+ ctx->session = s;
ctx->aindex = ngx_rtmp_play_parse_index('a', v->args);
ctx->vindex = ngx_rtmp_play_parse_index('v', v->args);
-
+
ctx->file.log = s->connection->log;
+ ngx_memcpy(ctx->name, v->name, NGX_RTMP_MAX_NAME);
+
name.len = ngx_strlen(v->name);
name.data = v->name;
@@ -813,6 +901,10 @@
return NGX_ERROR;
}
+ if (ngx_rtmp_play_join(s) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
e = &ctx->send_evt;
e->data = s;
e->handler = ngx_rtmp_play_send;
|
[-]
[+]
|
Changed |
nginx-rtmp-module-0.9.20.tar.bz2/ngx_rtmp_play_module.h
^
|
@@ -8,6 +8,7 @@
#include "ngx_rtmp.h"
+#include "ngx_rtmp_cmd_module.h"
typedef ngx_int_t (*ngx_rtmp_play_init_pt) (ngx_rtmp_session_t *s,
@@ -38,12 +39,17 @@
} ngx_rtmp_play_fmt_t;
-typedef struct {
+typedef struct ngx_rtmp_play_ctx_s ngx_rtmp_play_ctx_t;
+
+
+struct ngx_rtmp_play_ctx_s {
+ ngx_rtmp_session_t *session;
ngx_file_t file;
ngx_rtmp_play_fmt_t *fmt;
ngx_event_t send_evt;
unsigned playing:1;
unsigned opened:1;
+ unsigned joined:1;
ngx_uint_t ncrs;
ngx_uint_t nheader;
ngx_uint_t nbody;
@@ -53,7 +59,9 @@
ngx_int_t aindex, vindex;
ngx_uint_t nentry;
ngx_uint_t post_seek;
-} ngx_rtmp_play_ctx_t;
+ u_char name[NGX_RTMP_MAX_NAME];
+ ngx_rtmp_play_ctx_t *next;
+};
typedef struct {
@@ -66,6 +74,8 @@
ngx_str_t temp_path;
ngx_str_t local_path;
ngx_array_t entries; /* ngx_rtmp_play_entry_t * */
+ ngx_uint_t nbuckets;
+ ngx_rtmp_play_ctx_t **ctx;
} ngx_rtmp_play_app_conf_t;
|
[-]
[+]
|
Changed |
nginx-rtmp-module-0.9.20.tar.bz2/ngx_rtmp_stat_module.c
^
|
@@ -8,6 +8,7 @@
#include "ngx_rtmp.h"
#include "ngx_rtmp_live_module.h"
+#include "ngx_rtmp_play_module.h"
#include "ngx_rtmp_codec_module.h"
@@ -26,6 +27,7 @@
#define NGX_RTMP_STAT_GLOBAL 0x01
#define NGX_RTMP_STAT_LIVE 0x02
#define NGX_RTMP_STAT_CLIENTS 0x04
+#define NGX_RTMP_STAT_PLAY 0x08
/*
* global: stat-{bufs-{total,free,used}, total bytes in/out, bw in/out} - cscf
@@ -309,6 +311,50 @@
#endif
+
+static void
+ngx_rtmp_stat_client(ngx_http_request_t *r, ngx_chain_t ***lll,
+ ngx_rtmp_session_t *s)
+{
+ u_char buf[NGX_OFF_T_LEN + 1];
+
+#ifdef NGX_RTMP_POOL_DEBUG
+ ngx_rtmp_stat_dump_pool(r, lll, s->connection->pool);
+#endif
+ NGX_RTMP_STAT_L("<id>");
+ NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui",
+ (ngx_uint_t) s->connection->number) - buf);
+ NGX_RTMP_STAT_L("</id>");
+
+ NGX_RTMP_STAT_L("<address>");
+ NGX_RTMP_STAT_S(&s->connection->addr_text);
+ NGX_RTMP_STAT_L("</address>");
+
+ NGX_RTMP_STAT_L("<time>");
+ NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%M",
+ ngx_current_msec - s->epoch) - buf);
+ NGX_RTMP_STAT_L("</time>");
+
+ if (s->flashver.len) {
+ NGX_RTMP_STAT_L("<flashver>");
+ NGX_RTMP_STAT_ES(&s->flashver);
+ NGX_RTMP_STAT_L("</flashver>");
+ }
+
+ if (s->page_url.len) {
+ NGX_RTMP_STAT_L("<pageurl>");
+ NGX_RTMP_STAT_ES(&s->page_url);
+ NGX_RTMP_STAT_L("</pageurl>");
+ }
+
+ if (s->swf_url.len) {
+ NGX_RTMP_STAT_L("<swfurl>");
+ NGX_RTMP_STAT_ES(&s->swf_url);
+ NGX_RTMP_STAT_L("</swfurl>");
+ }
+}
+
+
static void
ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_live_app_conf_t *lacf)
@@ -323,6 +369,10 @@
ngx_rtmp_stat_loc_conf_t *slcf;
u_char *cname;
+ if (!lacf->live) {
+ return;
+ }
+
slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module);
NGX_RTMP_STAT_L("<live>\r\n");
@@ -350,24 +400,7 @@
if (slcf->stat & NGX_RTMP_STAT_CLIENTS) {
NGX_RTMP_STAT_L("<client>");
-#ifdef NGX_RTMP_POOL_DEBUG
- ngx_rtmp_stat_dump_pool(r, lll, s->connection->pool);
-#endif
- NGX_RTMP_STAT_L("<id>");
- NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui",
- (ngx_uint_t) s->connection->number)
- - buf);
- NGX_RTMP_STAT_L("</id>");
-
- NGX_RTMP_STAT_L("<address>");
- NGX_RTMP_STAT_S(&s->connection->addr_text);
- NGX_RTMP_STAT_L("</address>");
-
- NGX_RTMP_STAT_L("<time>");
- NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%M",
- ngx_current_msec - s->epoch)
- - buf);
- NGX_RTMP_STAT_L("</time>");
+ ngx_rtmp_stat_client(r, lll, s);
NGX_RTMP_STAT_L("<dropped>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
@@ -384,24 +417,6 @@
}
NGX_RTMP_STAT_L("</avsync>");
- if (s->flashver.len) {
- NGX_RTMP_STAT_L("<flashver>");
- NGX_RTMP_STAT_ES(&s->flashver);
- NGX_RTMP_STAT_L("</flashver>");
- }
-
- if (s->page_url.len) {
- NGX_RTMP_STAT_L("<pageurl>");
- NGX_RTMP_STAT_ES(&s->page_url);
- NGX_RTMP_STAT_L("</pageurl>");
- }
-
- if (s->swf_url.len) {
- NGX_RTMP_STAT_L("<swfurl>");
- NGX_RTMP_STAT_ES(&s->swf_url);
- NGX_RTMP_STAT_L("</swfurl>");
- }
-
if (ctx->publishing) {
NGX_RTMP_STAT_L("<publishing/>");
}
@@ -481,6 +496,73 @@
static void
+ngx_rtmp_stat_play(ngx_http_request_t *r, ngx_chain_t ***lll,
+ ngx_rtmp_play_app_conf_t *pacf)
+{
+ ngx_rtmp_play_ctx_t *ctx, *sctx;
+ ngx_rtmp_session_t *s;
+ ngx_uint_t n;
+ size_t nclients, total_nclients;
+ u_char buf[NGX_OFF_T_LEN + 1];
+ ngx_rtmp_stat_loc_conf_t *slcf;
+
+ if (pacf->entries.nelts == 0) {
+ return;
+ }
+
+ slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module);
+
+ NGX_RTMP_STAT_L("<play>\r\n");
+
+ total_nclients = 0;
+ for (n = 0; n < pacf->nbuckets; ++n) {
+ for (ctx = pacf->ctx[n]; ctx; ) {
+ NGX_RTMP_STAT_L("<stream>\r\n");
+
+ NGX_RTMP_STAT_L("<name>");
+ NGX_RTMP_STAT_ECS(ctx->name);
+ NGX_RTMP_STAT_L("</name>\r\n");
+
+ nclients = 0;
+ sctx = ctx;
+ for (; ctx; ctx = ctx->next) {
+ if (ngx_strcmp(ctx->name, sctx->name)) {
+ break;
+ }
+
+ ++nclients;
+
+ s = ctx->session;
+ if (slcf->stat & NGX_RTMP_STAT_CLIENTS) {
+ NGX_RTMP_STAT_L("<client>");
+
+ ngx_rtmp_stat_client(r, lll, s);
+
+ NGX_RTMP_STAT_L("</client>\r\n");
+ }
+ }
+ total_nclients += nclients;
+
+ NGX_RTMP_STAT_L("<active/>");
+ NGX_RTMP_STAT_L("<nclients>");
+ NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
+ "%uz", nclients) - buf);
+ NGX_RTMP_STAT_L("</nclients>\r\n");
+
+ NGX_RTMP_STAT_L("</stream>\r\n");
+ }
+ }
+
+ NGX_RTMP_STAT_L("<nclients>");
+ NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
+ "%uz", total_nclients) - buf);
+ NGX_RTMP_STAT_L("</nclients>\r\n");
+
+ NGX_RTMP_STAT_L("</play>\r\n");
+}
+
+
+static void
ngx_rtmp_stat_application(ngx_http_request_t *r, ngx_chain_t ***lll,
ngx_rtmp_core_app_conf_t *cacf)
{
@@ -498,6 +580,11 @@
cacf->app_conf[ngx_rtmp_live_module.ctx_index]);
}
+ if (slcf->stat & NGX_RTMP_STAT_PLAY) {
+ ngx_rtmp_stat_play(r, lll,
+ cacf->app_conf[ngx_rtmp_play_module.ctx_index]);
+ }
+
NGX_RTMP_STAT_L("</application>\r\n");
}
|
[-]
[+]
|
Changed |
nginx-rtmp-module-0.9.20.tar.bz2/stat.xsl
^
|
@@ -51,13 +51,15 @@
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bwin"/>
<xsl:with-param name="bits" select="1"/>
- </xsl:call-template>/s
+ <xsl:with-param name="persec" select="1"/>
+ </xsl:call-template>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bwout"/>
<xsl:with-param name="bits" select="1"/>
- </xsl:call-template>/s
+ <xsl:with-param name="persec" select="1"/>
+ </xsl:call-template>
</td>
<td colspan="5"/>
<td>
@@ -81,6 +83,7 @@
</td>
</tr>
<xsl:apply-templates select="live"/>
+ <xsl:apply-templates select="play"/>
</xsl:template>
<xsl:template match="live">
@@ -95,6 +98,18 @@
<xsl:apply-templates select="stream"/>
</xsl:template>
+<xsl:template match="play">
+ <tr bgcolor="#aaaaaa">
+ <td>
+ <i>vod streams</i>
+ </td>
+ <td align="middle">
+ <xsl:value-of select="nclients"/>
+ </td>
+ </tr>
+ <xsl:apply-templates select="stream"/>
+</xsl:template>
+
<xsl:template match="stream">
<tr valign="top">
<xsl:attribute name="bgcolor">
@@ -131,15 +146,21 @@
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bwin"/>
<xsl:with-param name="bits" select="1"/>
- </xsl:call-template>/s
+ <xsl:with-param name="persec" select="1"/>
+ </xsl:call-template>
</td>
<td>
<xsl:call-template name="showsize">
<xsl:with-param name="size" select="bwout"/>
<xsl:with-param name="bits" select="1"/>
- </xsl:call-template>/s
+ <xsl:with-param name="persec" select="1"/>
+ </xsl:call-template>
+ </td>
+ <td>
+ <xsl:if test="meta/width > 0">
+ <xsl:value-of select="meta/width"/>x<xsl:value-of select="meta/height"/>
+ </xsl:if>
</td>
- <td><xsl:value-of select="meta/width"/>x<xsl:value-of select="meta/height"/></td>
<td align="middle"><xsl:value-of select="meta/framerate"/></td>
<td>
<xsl:value-of select="meta/video"/>
@@ -180,28 +201,31 @@
<xsl:template name="showtime">
<xsl:param name="time"/>
- <xsl:variable name="sec">
- <xsl:value-of select="floor($time div 1000)"/>
- </xsl:variable>
-
- <xsl:if test="$sec >= 86400">
- <xsl:value-of select="floor($sec div 86400)"/>d
- </xsl:if>
+ <xsl:if test="$time > 0">
+ <xsl:variable name="sec">
+ <xsl:value-of select="floor($time div 1000)"/>
+ </xsl:variable>
+
+ <xsl:if test="$sec >= 86400">
+ <xsl:value-of select="floor($sec div 86400)"/>d
+ </xsl:if>
+
+ <xsl:if test="$sec >= 3600">
+ <xsl:value-of select="(floor($sec div 3600)) mod 24"/>h
+ </xsl:if>
+
+ <xsl:if test="$sec >= 60">
+ <xsl:value-of select="(floor($sec div 60)) mod 60"/>m
+ </xsl:if>
- <xsl:if test="$sec >= 3600">
- <xsl:value-of select="(floor($sec div 3600)) mod 24"/>h
+ <xsl:value-of select="$sec mod 60"/>s
</xsl:if>
-
- <xsl:if test="$sec >= 60">
- <xsl:value-of select="(floor($sec div 60)) mod 60"/>m
- </xsl:if>
-
- <xsl:value-of select="$sec mod 60"/>s
</xsl:template>
<xsl:template name="showsize">
<xsl:param name="size"/>
<xsl:param name="bits" select="0" />
+ <xsl:param name="persec" select="0" />
<xsl:variable name="sizen">
<xsl:value-of select="floor($size div 1024)"/>
</xsl:variable>
@@ -217,10 +241,13 @@
<xsl:when test="$sizen >= 0">
<xsl:value-of select="$sizen"/> K</xsl:when>
</xsl:choose>
- <xsl:choose>
- <xsl:when test="$bits = 1">b</xsl:when>
- <xsl:otherwise>B</xsl:otherwise>
- </xsl:choose>
+ <xsl:if test="string-length($size) > 0">
+ <xsl:choose>
+ <xsl:when test="$bits = 1">b</xsl:when>
+ <xsl:otherwise>B</xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="$persec = 1">/s</xsl:if>
+ </xsl:if>
</xsl:template>
<xsl:template name="streamstate">
|