diff --git a/Doxyfile b/Doxyfile index be3218cc..794b6cbc 100644 --- a/Doxyfile +++ b/Doxyfile @@ -220,7 +220,7 @@ TAB_SIZE = 2 # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. -ALIASES = +ALIASES = "api=\xrefitem api \"API call\" \"API calls\"" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" @@ -794,7 +794,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = */.git/* +EXCLUDE_PATTERNS = */.git/* */tinythread.* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the diff --git a/Makefile b/Makefile index 01845249..a3ae5abd 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ LDLIBS = -lmist -lrt .DEFAULT_GOAL := all -all: controller buffers connectors analysers converters +all: MistConnHTTP controller analysers inputs outputs DOXYGEN := $(shell doxygen -v 2> /dev/null) ifdef DOXYGEN @@ -33,71 +33,15 @@ $(warning Doxygen not installed - not building source documentation.) endif controller: MistController +MistController: override LDLIBS += $(THREADLIB) MistController: src/controller/server.html.h src/controller/* $(CXX) $(LDFLAGS) $(CPPFLAGS) src/controller/*.cpp $(LDLIBS) -o $@ -buffers: MistPlayer -MistPlayer: src/buffer/player.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -buffers: MistBuffer -MistBuffer: override LDLIBS += $(THREADLIB) -MistBuffer: src/buffer/buffer.cpp src/buffer/buffer_stream.h src/buffer/buffer_stream.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) src/buffer/buffer.cpp src/buffer/buffer_stream.cpp $(LDLIBS) -o $@ - -connectors: MistConnRaw -MistConnRaw: src/connectors/conn_raw.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -connectors: MistConnRTMP -MistConnRTMP: src/connectors/conn_rtmp.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - connectors: MistConnHTTP MistConnHTTP: override LDLIBS += $(THREADLIB) MistConnHTTP: src/connectors/conn_http.cpp src/connectors/embed.js.h src/connectors/icon.h $(CXX) $(LDFLAGS) $(CPPFLAGS) $< $(LDLIBS) -o $@ -connectors: MistConnHTTPProgressiveFLV -MistConnHTTPProgressiveFLV: src/connectors/conn_http_progressive_flv.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -connectors: MistConnHTTPProgressiveMP3 -MistConnHTTPProgressiveMP3: src/connectors/conn_http_progressive_mp3.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -connectors: MistConnHTTPProgressiveMP4 -MistConnHTTPProgressiveMP4: src/connectors/conn_http_progressive_mp4.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -connectors: MistConnHTTPProgressiveOGG -MistConnHTTPProgressiveOGG: src/connectors/conn_http_progressive_ogg.cpp src/converters/oggconv.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -connectors: MistConnHTTPDynamic -MistConnHTTPDynamic: src/connectors/conn_http_dynamic.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -connectors: MistConnHTTPSmooth -MistConnHTTPSmooth: src/connectors/conn_http_smooth.cpp src/connectors/xap.h - $(CXX) $(LDFLAGS) $(CPPFLAGS) $< $(LDLIBS) -o $@ - -connectors: MistConnHTTPLive -MistConnHTTPLive: src/connectors/conn_http_live.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -connectors: MistConnHTTPSRT -MistConnHTTPSRT: src/connectors/conn_http_srt.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -connectors: MistConnHTTPJSON -MistConnHTTPJSON: src/connectors/conn_http_json.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -connectors: MistConnTS -MistConnTS: src/connectors/conn_ts.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - analysers: MistAnalyserRTMP MistAnalyserRTMP: src/analysers/rtmp_analyser.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ @@ -134,14 +78,6 @@ converters: MistFLV2DTSC MistFLV2DTSC: src/converters/flv2dtsc.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ -converters: MistOGG2DTSC -MistOGG2DTSC: src/converters/ogg2dtsc.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - -converters: MistDTSC2OGG -MistDTSC2OGG: src/converters/dtsc2ogg.cpp src/converters/oggconv.cpp - $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ - converters: MistDTSCFix MistDTSCFix: src/converters/dtscfix.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ @@ -162,8 +98,98 @@ converters: MistDTSC2SRT MistDTSC2SRT: src/converters/dtsc2srt.cpp $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ +inputs: MistInDTSC +MistInDTSC: override LDLIBS += $(THREADLIB) +MistInDTSC: override CPPFLAGS += "-DINPUTTYPE=\"input_dtsc.h\"" +MistInDTSC: src/input/mist_in.cpp src/input/input.cpp src/input/input_dtsc.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +inputs: MistInFLV +MistInFLV: override LDLIBS += $(THREADLIB) +MistInFLV: override CPPFLAGS += "-DINPUTTYPE=\"input_flv.h\"" +MistInFLV: src/input/mist_in.cpp src/input/input.cpp src/input/input_flv.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +inputs: MistInOGG +MistInOGG: override LDLIBS += $(THREADLIB) +MistInOGG: override CPPFLAGS += "-DINPUTTYPE=\"input_ogg.h\"" +MistInOGG: src/input/mist_in.cpp src/input/input.cpp src/input/input_ogg.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +inputs: MistInBuffer +MistInBuffer: override LDLIBS += $(THREADLIB) +MistInBuffer: override CPPFLAGS += "-DINPUTTYPE=\"input_buffer.h\"" +MistInBuffer: src/input/mist_in.cpp src/input/input.cpp src/input/input_buffer.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutFLV +MistOutFLV: override LDLIBS += $(THREADLIB) +MistOutFLV: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_flv.h\"" +MistOutFLV: src/output/mist_out.cpp src/output/output.cpp src/output/output_progressive_flv.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutMP4 +MistOutMP4: override LDLIBS += $(THREADLIB) +MistOutMP4: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_mp4.h\"" +MistOutMP4: src/output/mist_out.cpp src/output/output.cpp src/output/output_progressive_mp4.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutMP3 +MistOutMP3: override LDLIBS += $(THREADLIB) +MistOutMP3: override CPPFLAGS += "-DOUTPUTTYPE=\"output_progressive_mp3.h\"" +MistOutMP3: src/output/mist_out.cpp src/output/output.cpp src/output/output_progressive_mp3.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutRTMP +MistOutRTMP: override LDLIBS += $(THREADLIB) +MistOutRTMP: override CPPFLAGS += "-DOUTPUTTYPE=\"output_rtmp.h\"" +MistOutRTMP: src/output/mist_out.cpp src/output/output.cpp src/output/output_rtmp.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutRaw +MistOutRaw: override LDLIBS += $(THREADLIB) +MistOutRaw: override CPPFLAGS += "-DOUTPUTTYPE=\"output_raw.h\"" +MistOutRaw: src/output/mist_out.cpp src/output/output.cpp src/output/output_raw.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutTS +MistOutTS: override LDLIBS += $(THREADLIB) +MistOutTS: override CPPFLAGS += "-DOUTPUTTYPE=\"output_ts.h\"" +MistOutTS: src/output/mist_out.cpp src/output/output.cpp src/output/output_ts.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutHSS +MistOutHSS: override LDLIBS += $(THREADLIB) +MistOutHSS: override CPPFLAGS += "-DOUTPUTTYPE=\"output_hss.h\"" +MistOutHSS: src/output/mist_out.cpp src/output/output.cpp src/output/output_hss.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutHLS +MistOutHLS: override LDLIBS += $(THREADLIB) +MistOutHLS: override CPPFLAGS += "-DOUTPUTTYPE=\"output_hls.h\"" +MistOutHLS: src/output/mist_out.cpp src/output/output.cpp src/output/output_hls.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutHDS +MistOutHDS: override LDLIBS += $(THREADLIB) +MistOutHDS: override CPPFLAGS += "-DOUTPUTTYPE=\"output_hds.h\"" +MistOutHDS: src/output/mist_out.cpp src/output/output.cpp src/output/output_hds.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutSRT +MistOutSRT: override LDLIBS += $(THREADLIB) +MistOutSRT: override CPPFLAGS += "-DOUTPUTTYPE=\"output_srt.h\"" +MistOutSRT: src/output/mist_out.cpp src/output/output.cpp src/output/output_srt.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + +outputs: MistOutJSON +MistOutJSON: override LDLIBS += $(THREADLIB) +MistOutJSON: override CPPFLAGS += "-DOUTPUTTYPE=\"output_json.h\"" +MistOutJSON: src/output/mist_out.cpp src/output/output.cpp src/output/output_json.cpp + $(CXX) $(LDFLAGS) $(CPPFLAGS) $^ $(LDLIBS) -o $@ + BUILT_SOURCES=controller/server.html.h connectors/embed.js.h -lspSOURCES=lsp/jquery.js lsp/placeholder.js lsp/md5.js lsp/main.js lsp/pages.js lsp/tablesort.js +lspSOURCES=lsp/plugins/jquery.js lsp/plugins/placeholder.js lsp/plugins/md5.js lsp/main.js lsp/pages.js lsp/plugins/tablesort.js lsp/plugins/jquery.flot.min.js lsp/plugins/jquery.flot.time.min.js lsp/plugins/jquery.flot.crosshair.min.js lspDATA=lsp/header.html lsp/main.css lsp/footer.html JAVA := $(shell which java 2> /dev/null) @@ -201,7 +227,7 @@ clean: rm -f *.o Mist* sourcery src/controller/server.html src/connectors/embed.js.h src/controller/server.html.h rm -rf ./docs -install: controller buffers connectors analysers converters +install: all install ./Mist* $(DESTDIR)$(bindir) uninstall: diff --git a/lsp/footer.html b/lsp/footer.html index b9209e1e..f2fe2ffe 100644 --- a/lsp/footer.html +++ b/lsp/footer.html @@ -1,3 +1,4 @@ + <link rel='icon' href='data:image/png;base64,AAABAAQAEBAAAAEAIABoBAAARgAAADAwAAABACAAqCUAAK4EAAAgIAAAAQAgAKgQAABWKgAAQEAAAAEAIAAoQgAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////ANu2kgfPsozBz7OM0NOxkBf///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDStYw+z7OM/8+zjP/Qsoxd////AP///wD///8A////AP///wD///8A////AP///wD///8A////AMzMmQXQso1n0LSMgc+0jKrPtI3E0LKMh86zjGjMzJkF////AP///wD///8A////AP+AgALMtooj////AM6yjVPOso2I0LWNJv///wDPtI06z7ONZf///wDQtY0mz7OMidCyi1b///8BzLaKI/+AgALPs4y2z7OM/9C1jsTSvJZE38+vEN/PrxDfz68Q28imwdrFo/DezrUf38+vEN/PrxDSvJZEz7SOxs+zjP/Ps4y20LOM18+zjP/RtpDd4dCybeLTtWji07Vo4tO1aNzJqfDaxqP94dK2d+LTtWji07Vo4NKzbNG2kN3Ps4z/0LOM19C1jSbPtIyfz7WLT8+zjJDPtIxf/4CAAuHStDPaw6GQ07uVleLTtmn///8A0LOLUdCzjJfNs4tR0LSNo9C1jSb///8AzMyZBc60jX3///8A0LSNQc+0jKbbxqSpz7ONZc+zjJvcyaiK0LWPrc+0jVX///8Az7ONf7+/gAj///8A////AP///wDQsoxx1biOEv///wDi1LaC0raPfc+zjfzPs4z/z7OMr+DQsJL///8Az6+PENC0i3f///8A////AP///wD///8AzLOMFM6xi3Li1bRO3sqsXM6zi43Qs4z5z7OM/8+zjLbYxKBW49O0XM+zjG/MuI8Z////AP///wD///8A////AP///wDQtIzn0LaP89CzjJ7RtY1T0LOLUdG0i2PRtY5IzrONk9C2jvDQtIzuqqqqA////wD///8A////AP///wD///8Az7OLwM6zi8bPs4tAz7OLQM+zi0DPs4tAz7OLQM+zi0DOs4y7z7SMz/+AgAL///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AAD+fwAA/n8AAPw/AAD37wAAHngAAB54AACudQAA+R8AAPofAAD8PwAA48cAAOfnAAD//wAA//8AACgAAAAwAAAAYAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDMu4gPzrSLmM+zjN7Ps4zuz7OMu8+yjDX///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANG5iwvPs4zYz7OM/8+zjP/Ps4z/z7OM/8+zjPvQs4pG////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+zjInPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4zg/4CAAv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+zjM/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LKOK////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+zjN3Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LOMPP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7+ABNCzjMzPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Os4z8z6+AEP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOso1Tz7OM2c+zjP/Ps4z9z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7SM38+zjFv///8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrWMPs+zjMjPs4z/z7OMyc+zi0DPsYxFz7OM5M+zjP/Ps4z/zrOM/M+yjIXMs4w8zrONws+zjP/Ps4zP0LSMR////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANG0iyzPs4y0z7OM/8+zjNnOs4xU////AP///wD///8AzMyZBc6zi7LPs4z/z7KMNf///wD///8A////ANGzi03Ps4zUz7OM/86zjL3OtY40////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDTsI0dz7SMn8+zjP3Qs4znzrOMaL+/gAT///8A////AP///wD///8A////AM+zjaXPs4z/zrONL////wD///8A////AP///wD/gIAC0LOLYc+zjOPPs4z+zrOMqMy2iiP///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0rSHEc+0i4vQs4z3z7OM8s60jX3RuYsL////AP///wD///8A////AP///wD///8A////AM+zjKvPs4z/z7KMNf///wD///8A////AP///wD///8A////AL+/gAjPs4x2z7OM78+zjPrPs4yU0a6LFv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANC0jVzPs4yg0LOLrM+zi3XMs4AK////AL+/gAjPs4x2z7OM78+zjPnQs42RzraSFf///wD///8A////AP///wD///8A////AP///wD///8A////AM6zi7LPs4z/z7GKO////wD///8A////AP///wD///8A////AP///wD///8A0rSHEc+0i4vQs4z3z7OM88+zjX/VqpUM////AMyzgArPs4t10LOLrM+zjKDQtI1c////AP///wC/v4AEz7OMtM+zjP/Ps4z/z7OM/8+zjP/Ps4za0LSMc8+zjOPPs4z+z7ONpdGyiyH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A49O0bdS8l/rPs4z/3cqq7+LTtlf///8A////AP///wD///8A////AP///wD///8A////AP///wDMs4gez7OMoM+zjP3Ps4zoz7KNe8+zjNvPs4z/z7OM/8+zjP/Ps4z/z7OMtL+/gATPso17z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+0jLrQsY0x////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDh1bZU4tO1/9S7l//Ps4z/3cqp/+LTtf3h1LY7////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANK1ji3Ps4u1z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+yjXvPs4zTz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LSN/t7Nqz3k1bUw5NW1MOTVtTDk1bUw5NW1MOTVtTDk1bUw5NW1MOTVtTDk1bUw5NW1MOTVtTDj07XF4tO1/9O7lv/Ps4z/3Mmo/+LTtf/i07Wv5NW1MOTVtTDk1bUw5NW1MOTVtTDk1bUw5NW1MOTVtTDk1bUw5NW1MOTVtTDezas90LSN/s+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjNPPs4zzz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/97Nrf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/9O6lf/Ps4z/3Min/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/eza3/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjPPPs4zKz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LOM/d7KqU3j07NA49OzQOPTs0Dj07NA49OzQOPTs0Dj07NA49OzQOPTs0Dj07NA49OzQOPTs0Di1Lan4tO1/9O5lP/Ps4z/28im/+LTtf/i0rWU49OzQOPTs0Dj07NA49OzQOPTs0Dj07NA49OzQOPTs0Dj07NA49OzQOPTs0DdzKpL0LOM/c+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjMrPtIxfz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjeHPs4xb////Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDe1bM249S1/dK4k//Ps4z/28el/+LTtfnc0bkW////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANGzi03Ps4zbz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+0jF////8Az7SLi8+zjP3Ps4z/z7OM/8+zjP/Ps4y+zbSMUtCzjNfPs4z/0LOMzNC0jUH///8A////AP///wD///8A////AP///wD///8A////ANXVqgbh07XP4tO03tC1junPs4z/17+bpOLTtPPi0rag////AP///wD///8A////AP///wD///8A////AP///wDNtI8pzrOMuM+zjP/Ps4zlz7SOX86yi73Ps4z/z7OM/8+zjP/Ps4z9z7SLi////wD///8A////AM+1ijDPs4t1z7OMkM+zjfzQtIt3////AMzMmQXPtItwz7OM78+zjP/Ps4y0zrCMKv///wD///8A////AP///wD///8A////AOHTtYri07X85NKzOc+zjeHPs4z/z7OMb+PStmzi07X/4tS0R////wD///8A////AP///wD///8Az6+PEM+yjY/Ps4z7z7OM+8+0jI7YsYkN////AM60jG3Ps4z+zrOLls+zi3XPtYow////AP///wD///8A////AP///wD///8A////ANCzjKfPs4zjv7+ABP///wD///8AzraSFc+yjJnPs4z9z7OM/M+yjJnKtYoY////AP///wD///8A49OzQOLTtf3h1LaB////ANCzjOfPs4z/z7OLdf///wHi07XE49O14OrVvwz///8A////AP+AgALQtIxmz7OM68+zjP/Ps4zFzbGLLv///wD///8A/4CAAs+zjNvQsou3////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANG1izfPs4z/0LONV////wD///8A////AP///wDPtYowz7KMwc+zjP/Ps4zzz7ONf9G5iwvfz68Q4tO14uLStcj/v78E////AM+zjO7Ps4z/z7KNe////wDf0rMo4tO1+OLUtJP///8AzrWMPs+zjM/Ps4z/z7OM69C0jWL///8B////AP///wD///8A0bOLTc+zjP/QtIxH////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQs4zH0LOMx////wD///8A////AP///wD///8A////ANCyi1bPs43hz7OM/8+zjObYwqDK4tO19OPVuCT///8A////AM+zjPTPs4z/0LOLgv///wD///8A4tO2euDPsP7RtpC9z7OM/8+zjP7Ps42a1biOEv///wD///8A////AP///wD///8A0LOMvM+zi9b///8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQs41Xz7OM/9G1izf///8A////AP///wD///8A////AP///wDGqo4JzrKMfs+zjPnPs4z/0LaP5c6yjEnMs4AKz7KNj8+zjP/Ps4z/z7OM9s+zi4DItpIO0LONgtC0jf3Ps4z/z7ON0M2yjTj///8A////AP///wD///8A////AP///wDRtIssz7OM/9CyjWf///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wC/v4AEz7OM486zjKj///8A////AP///wD///8A////AP///wD///8A3ta1H9/QsPLUvJfjz7OM/8+zjP/Ps4zrz7OM/8+zjP/Ps4z/z7OM/8+zjP/Qs43zz7OM/8+zjPTZxaL94tO1hv///wD///8A////AP///wD///8A////AP///wDPs4ybz7OM7cyzgAr///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0LSLd8+zjPvRtokc////AP///wD///8A////AP///wD//4AC4tS1wuLTtefm2bMU0LOMPM+zjNrPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7ONtcy4jxnj1LaI4tO1++TVtTD///8A////AP///wD///8A////AMyzjBTQs4z30LOMh////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A1biOEs+zjfXOso2I////AP///wD///8A////AP///wDh07V54tO1/uLUtEf///8A////AM+zjYDPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LOLYf///wDf378I4tS12ePStc7/v78E////AP///wD///8A////AM+0jHrPs4z70baJHP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANCzjJfPs4ztzLOACv///wD///8A////AOHStDPi07X64dO2kv///wD///8A////AM+yjI7Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LSMZv///wD///8A4tK1PuLTtf7h0rV4////AP///wD///8AzMyZBc+zjOXQs4yn////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM20jynPs4z+zrOMaP///wD///8A5syzCuHTtdji07XV39+/CP///wDStY4tz7SMqdCzjP3Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM8s60jX3YsYkN////AOLTtJbi07X35dG3J////wD///8Az7OLWs+zjP/Nso04////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOs4y40LOM19G5iwu/v4AE4tO0luLTtfnk1bUw0LGNMc+zja7Ps4z+z7OM/8+zjNDPs4y/z7OM/8+zjP/Ps4z/z7OM/8+zjP/PtIy70LOM58+zjP/Ps4zrz7KNdNzFohbi07Xi4tO0w////wHItpIOz7OMyc+zjMj///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOtI5Yz7OM/8+zjPTQtI3r2cOh/9vHpJXOs4yyz7OM/8+zjP7Ps4y2zrONQ////wD///8BzrSMbc+zjNDPs4zzz7OMys6yjF3///8Av7+ACM6zjGjPs4zez7OM/8+zjObVvZeW28ak/9G2kOXPtIz6z7OM/82yjHH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQs42Nz7OM/8+zjP/Ps4z/z7OM/8+zjP/Qs4z3z7OMm820jyn///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCqqqoDz7OMW86zjNLPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjbn///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANu2kgfPs4ztz7OM/8+zjP/Ps4z/z7OM/8+zjPjPs4y+zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjPs43rz7OM/8+zjP/Ps4z/z7OM/8+zjP7NsY4k////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP+AgALPs4zgz7OM/8+zjP/Ps4z/z7OM/86yi+zOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjPs4zez7OM/8+zjP/Ps4z/z7OM/8+zjPnTsZAX////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQs4x8z7OM/8+zjP/Ps4z/z7OM/860jU7///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDRsoshz7OM/c+zjP/Ps4z/z7OM/8+0jKb///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDGqo4JzrOMaM+zjNPPs4vAzrOMVP+AgAL///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrWMPs+zjKvPtIzfz7SMesmuhhP///8A////AP///wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA////////AAD///w///8AAP//+B///wAA///wD///AAD///AP//8AAP//8A///wAA///wD///AAD//+AH//8AAP//jBH//wAA//4+fH//AAD/+P5/H/8AAP/j/n/H/wAA58f+f+PnAACBH/4/+IEAAIB//B/+AQAAAP/4D/8AAAAAAAAAAAAAAAD/+A//AAAAgH/8H/4BAACBH/gP+IEAAPPH8m/hzwAA+eHyZ8efAAD9+OZzH78AAPz+Djg/PwAA/v+MEP9/AAD+f4AA/n8AAP9/MAT+fwAA/z9wDn7/AAD/PnAPfP8AAP+84Ac9/wAA/5mAAZn/AAD/wB48A/8AAP+Af/8B/wAA/4AAAAH/AAD/gAAAAf8AAP/D///B/wAA/+f//+f/AAD///////8AAP///////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANCyjUzQs4y8z7OL1tCzi2zMzJkF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDRtokcz7OM/M+zjP/Ps4z/z7OM/860i1j///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM60jX3Ps4z/z7OM/8+zjP/Ps4z/zrOMvf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrOLcs+zjP/Ps4z/z7OM/8+zjP/PtIyp////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A07CNHc+0jJ/Ps43hz7OM5s+zjP/Ps4z/z7ON9dCzjOLQs4yn0rSPIv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0rSHEc+0i4vQs4zn0LSLd8aqjgn/gIAC0LONlM+zjN3RuYsL1aqABs+zjG/Ps4zl0LKMks62khX///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7+ACM+zjHbQs4znz7SLi9K0hxH///8A////AP///wDOtIxt0LOMzP///wD///8A////AMi2kg7PtIuE0LOM5860jX3RuYsL////AP///wD///8A////AP///wD///8A////AP///wDMs4AKzrONXs6zjS////8AqqqqA9C0jWLPs4zgz7SMn9OwjR3///8A////AP///wD///8A////AM+yjXTOs4zS////AP///wD///8A////AP///wDMuI8Zz7KMmc+zjOPOs4xov7+ABP///wDOs40vzrONXsyzgAr///8Az7GKO8+zjO/Ps4z/z7OM/9C0jcTPs4zUz7OMs9G0iyz///8A////AP///wD///8A////AP///wDg07Qp2MKf3tK3kv3i0rWU////AP///wD///8A////AP///wD///8A0bGJJ86yjK3Ps4zYz7OMxs+zjP/Ps4z/z7OM78+xijvOsoytz7OM/8+zjP/Ps4z/z7OM/9W6l3Pfz7cg38+3IN/PtyDfz7cg38+3IN/PtyDfz7cg38+3IOLTtq/Yw6D/0reR/+LTtf/i07ZX38+3IN/PtyDfz7cg38+3IN/PtyDfz7cg38+3INS7lnDPs4z/z7OM/8+zjP/Ps4z/zrKMrc+zjODPs4z/z7OM/8+zjP/Ps4z/2caj5+HTtdjh07XY4dO12OHTtdjh07XY4dO12OHTtdjh07XY4tO19djCn//Rt5H/4tO1/+LUtuXh07XY4dO12OHTtdjh07XY4dO12OHTtdjh07XY2caj58+zjP/Ps4z/z7OM/8+zjP/Ps4zgz7ONf8+zjP/Ps4z/z7OM/9CzjP3Os4y4zrCMKv///wD///8A////AP///wD///8A////AP///wDi07aE2MKf/tG2kP/i07X538+vEP///wD///8A////AP///wD///8A////AM+vjyDPs4yv0LOM/c+zjP/Ps4z/z7OM/8+zjX/StIcRz7OMic+zjO3Ps4z5zrSLY8+yjXvPs4zxz7ONmsy4jxn///8A////AP///wD///8A4Na4GeLStuXSt5Gu0LON8+HTtZvj1LWH////AP///wD///8A////ANG5iwvOs4yDz7OM8dCzjIfNtY1gz7OM+s+zjO3Ps4yJ0rSHEf///wD///8A/4CAAtCzjIfPs4yJ////ANCzjhvPs4ykz7OM88+zi4DRuYsL////AP///wHi07W54tO2c8+zjZrPs4313cy7D+PTteDg0bMy////AP///wHPs4xbz7OM5M+zjL7NtI8p////ANC0jIHPso2P/4CAAv///wD///8A////AP///wD///8A0baJHM+zjOnMs4AK////AP///wDOs4s5z7KLy9CzjOfQtIxm39K1cePUtLz///8Bz7OLoc+zjPv///8A4tW0TuHTtc/Rto04z7OMxtCzjOfPs4xb////AP///wDbtpIHz7OM5tK0jyL///8A////AP///wD///8A////AP///wD///8A0LOMp86zjGj///8A////AP///wD///8B0LOLYc+zjOzRuJL71LmUX8i2kg7Qs43Nz7OM/8+1iUXMuI8Z1byY38+zjP3QsoySzLuID////wD///8A////AM+yjWDPs4yv////AP///wD///8A////AP///wD///8A////AP///wDRtYs3z7OL1v///wH///8A////AP///wDb27YH4dGz1NK6lLHPs4z5z7OM8c+zjP/Ps4z/z7OM/8+zjPjOs43N3syt6t/Ssyj///8A////AP///wD///8Az7OMz86yjj////8A////AP///wD///8A////AP///wD///8A////AP///wDQs4zH0LSMR////wD///8A////AOLStY7i1LWf////ANGzjk3Ps4z/z7OM/8+zjP/Ps4z/zrON4f///wHh1LZe4tO1xP//gAL///8A////AM6yjj/Ps4zP////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANCzjVfPs4y2////AP///wDh07VF4tO02+vYsQ3/gIACzrSMc8+zjP/Ps4z/z7OM/8+zjP/Ps4zo0baJHP///wDi07a449K2bP///wD///8Az7ONrs+0jF////8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7+ABM+zjeHRtIss286qFeLTteHhz7Y7zrONXs+zjNnPs4zpz7SM3M+zjP/Ps4z/z7OM/8+zjNDQs4z3z7KNj9vHozLi07Xn3ta1H9GxkCfPs4zl27aSB////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrOMqM+zjfXRtpD01b2Yxs+zjN3Ps4vWz7OMZNu2kge/v4AE0LONV8+zjJDRtIss////AM20jDPQs4usz7OM8tO6lcfSt5Hz0LOM+M6zjLj///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AL+/gATOs4znz7OM/8+zjP/Ps4z4z7SMn8yxi3nOs4x4zrOMeM6zjHjOs4x4zrOMeM6zjHjOs4x4zrOMeM6zjHjOs4uNz7OL7s+zjP/Ps4z/z7OM+NW4jhL///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////Ac+zjNzPs4z/z7OM/8+zjN7Ps4uAz7OLgM+zi4DPs4uAz7OLgM+zi4DPs4uAz7OLgM+zi4DPs4uAz7OLgM+zi4DPs4zOz7OM/8+zjP/Ps4zx0bmLC////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrKMSc+zjNHPtI3E0LOONv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+ziiXQsou3z7OM286zjV7///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////n////w////8H////B////AP///Oc///n3n//n9+f4H+P4EH/D/gAAAAAIP8P8GM/B8R5jpeZ/eaae/35mPv++AH3/vMG9/93Bu//bAFv/wPcD/8H/g//AAAP/5//n//////////////////////KAAAAEAAAACAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7WKMM+zjKTPs4zZz7OM78+zjL7Qs41yv7+ABP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7OMds+zjPvPs4z/z7OM/8+zjP/Ps4z/z7OM/86zjc3Rtokc////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7GMRc+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OMvv///wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+zjNTPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/QtIxS////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AMy7iA/Ps4z+z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OMiv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDRs4oyz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjKv///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0bmLC8+zjPzPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Os4yD////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANC0jUHPsozzz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/zrOMfP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzbSMM8+zjbnPs4z/z7OM/8+0jf7Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4zDz7GKO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQtY0mz7OMqs+zjP7Ps4z/z7SM386zjV7QtI1cz7OM8c+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7SMtM+1jWDPs4zZz7OM/8+zjP/Ps4y0zbGLLv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANCzjhvPs42az7OM+8+zjP/Ps4zpzrOLbtWqgAb///8A////AMyziB7Qs4yMz7OM9M+zjP/Os4zXzrKMWf///wD///8AqqqqA8+zjGTPs4zjz7OM/8+zjP7Ps42l0rSPIv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A1biOEs+0i4vQs4z3z7OM/8+zjPHOtI191aqVDP///wD///8A////AP///wD///8A////AM+zjNrPs4z/0LKMkv///wD///8A////AP///wD///8Av7+ACM60jHPPs4zsz7OM/8+zjPrPtIyVyrWKGP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDRuYsL0LOMfM+zjPDPs4z/0LOM99CzjIzJroYT////AP///wD///8A////AP///wD///8A////AP///wDPs4zgz7OM/860i5j///8A////AP///wD///8A////AP///wD///8AyLaSDtCzi4LPs4zzz7OM/8+zjfXPs42Gz6+PEP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANWqgAbQs4tsz7OM6M+zjP/Ps4z8z7OMm9G2iRz///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7OM5s+zjP/Ps4ye////AP///wD///8A////AP///wD///8A////AP///wD///8A0a6LFtCzjZHPs4z5z7OM/8+zjO7QtIt3xqqOCf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM61jB/Os41DzrWMH////wD///8A////AP///wD///8A/4CAAs6yjF3Ps4zez7OM/8+zjP7Ps4yr0bGJJ////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+zjO3Ps4z/z7ONpf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrWMH8+zjKDPs4z9z7OM/8+zjObOs4xozMyZBf///wD///8A////AP///wD///8AzrWMH86zjUPOtYwf////AP///wD///8A////AP///wD///8Az7SNVdCzjOfPs4z/z7OM/8+zjP/Ps4zkz7OMUP///wD///8A0bOLTc+zjNPPs4z/z7OM/8+0jLrNtIwz////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AOvYsQ3QtIz3z7OM/9K4k8jf1bUY////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrCMKs+zjK/Ps4z/z7OM/8+zjNzOsoxZ////Af///wDPs4xQz7OM5M+zjP/Ps4z/z7OM/9CzjOfPtI1V////AP///wD///8Az7KMhc+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/QtIydz7OMxc+zjP/Ps4z/z7OMydGyi0L///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AOPStj/i07Xhz7SN/8+zjP/VvZn/4tO18eHUtF////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0bWLN8+zjL7Ps4z/z7OM/8+zjNDQtI2jz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+yjIX///8A0LOMPM+zjP3Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4vW0LOLUf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AODWuBni07Xu4tO1/8+zjP/Ps4z/1LyY/+LTtf/i07X94NO0Ov///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7GMRdCzjMzPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z90LOMPM+zjKrPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4zKqqqqA////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDj1LaI4tO1/+LStP/Ps4z/z7OM/9S7lv/i07X/4tO1/+LTtrj///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Bz7OMxs+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjKrPtIzfz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0baP9OLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO16OLTtf/h0bP/z7OM/8+zjP/TupX/4tO1/+LTtf/h07T54tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qNG2j/TPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/PtIzfz7OM68+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/9C2j//i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4dGy/8+zjP/Ps4z/07qV/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/Qto//z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM69Cyi7fPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/QtI7Q5NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjji0rSP4tO1/+DQsf/Ps4z/z7OM/9O5lP/i07X/4tO1/+PTtbTk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY40LSPz8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/9Cyi7fQs4thz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/c+zjZrOsYka////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzMzMBeLTtOzgz7D/z7OM/8+zjP/SuJL/4tO1/+LTtvrg1rgZ////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPr48QzrOLjc+zjPvPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Qs4th////ANCyi7fPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7KM38+zjPvPs4z/z7ON9c+zjYbMu4gP////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AOPTtFLi07X/3s6u+s+zjP/Ps4z/0beR/+LTtfTi07X/4dO1bv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKqqqgPPsoxqz7OM68+zjP/Ps4z+0LOM4s+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Qsou3////AP///wDRuYsLz7SMlc+zjP7Ps4z/z7OM/8+zjP/Ps4z/zrOMp8aqjgnNtI8p0LKLt8+zjP/Ps4z/z7OM7NCzjXLbtpIH////AP///wD///8A////AP///wD///8A////AP///wD///8A////AOTRthzi07Xu4tO1/9vIpnDPs4z/z7OM/8+zjeTj1bM24tO1/OLTtfXi07Yj////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM21jkjPtIzVz7OM/8+zjP/Ps4zPz7SNOsaqjgnPs4yez7OM/8+zjP/Ps4z/z7OM/8+zjP7PtIyV0bmLC////wD///8A////AP///wDNtI8pz7SMX86zjIPPs4ulz7OM/8+0jKb///8A////AP///wDNtY5Iz7SM1c+zjP/Ps4z/z7OM4M6zjV7/gIAC////AP///wD///8A////AP///wD///8A////AP//gALi07XA4tO1/+HStJrQsY0xz7OM/8+zjP/Ps4zp////AOHStYni07X/4tO1wP///wH///8A////AP///wD///8A////AP///wD///8AzbSPKdCyi7fPs4z/z7OM/8+zjO7PsoxqqqqqA////wD///8Az7KNj8+zjP/Ps4y0zrOMg8+0jF/NtI8p////AP///wD///8A////AP///wD///8A////AP///wD///8A1aqVDM+zjPDPs4z70baJHP///wD///8A////AKqqqgPPsoxqz7OM68+zjP/Ps4z/z7OM0c6yjEn///8A////AP///wD///8A////AP///wDi07R74tO1/+PTtdfmzLMKzbKNOM+zjP/Ps4z/z7OM8P///wDf378I49O11+LTtf/i1LVr////AP///wD///8A////AP///wDJroYTz7OMlM+zjPvPs4z/z7OM/tC0jZzMs4wU////AP///wD///8AzLuID8+zjPLPs4z6zrGJGv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPs4yKz7OM/8+zjIr///8A////AP///wD///8A////AM+vjxDOs4uNz7OM+c+zjP/Ps4z/z7OMv9Czjjb///8A////AP///wDk0bY44tO1++LTtfnk1bUw////AM61jD7Ps4z/z7OM/8+zjPb///8A////AOTRtjji07X94tO19ODRsiH///8A////AMzMmQXQs41yz7OM78+zjP/Ps4z/z7KLy9Czjjb///8A////AP///wD///8A////AM+yjXTPs4z/0LOMov///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0baJHM+zjPvPs4zv1aqVDP///wD///8A////AP///wD///8A////AM2xjiTPs4yvz7OM/8+zjP/Ps4z/z7OMq9C1jSbb27YO4tO13uLTtf/i1LVx////AP///wDOtItEz7OM/8+zjP/Ps4z8////AP///wD///8A4tO2i+LTtf/j0rW9////Ac+yi0/Ps4zbz7OM/8+zjP/Ps4zs0LSMZv+AgAL///8A////AP///wD///8A////AL+/gATOs4ziz7OM/8+1ijD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPs42lz7OM/860jG3///8A////AP///wD///8A////AP///wD///8A////AM+zi0DPs4zPz7OM/8+zjP/Ps4z71byY2uDQsf/i0rW3////Af///wD///8Az7SLS8+zjP/Ps4z/z7OM/6qqqgP///8A////AN/fvwjh07XY3s2t/9G2kdnPs4z/z7OM/8+zjP3OtIuY1biOEv///wD///8A////AP///wD///8A////AP///wDOsoxZz7OM/86zjL3///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzbSMM8+zjP/Ps4zd/4CAAv///wD///8A////AP///wD///8A////AP///wD///8A/4CAAs6yi2PQs4znz7OM/8+zjP/QtY7+0LWQjsi2kg7///8AzrWMH8+zjcnPs4z/z7OM/8+zjP/Ps4zF0bWLN////wDKtYoY0LaRs8+zjP/Ps4z/z7OM/8+zjMjNtIwz////AP///wD///8A////AP///wD///8A////AP///wD///8Az7KLy8+zjP/PtItL////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPsozBz7OM/9Czi1H///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A3MuoLNfCn/nQtI3/z7OM/8+zjP/Ps4zrzrSMks+zjPHPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjPzOs4yxz7OM8s+zjP/Ps4z/0baP/9rEo9X///8B////AP///wD///8A////AP///wD///8A////AP///wD///8A0bSKPc+zjP/Qs4zX////Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrSNTs+zjP/Ps4zD////AP///wD///8A////AP///wD///8A////AP///wD///8A/7+/BOLStcji07X/3sytnNCzjKfPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z80LOMl+HRs9zi07X/49S2Zf///wD///8A////AP///wD///8A////AP///wD///8A////AM+0jbDPs4z/0LSMZv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP+AgALPs4zaz7OM/8+yjDX///8A////AP///wD///8A////AP///wD///8A////AOLTtIXi07X/4dO1z9XVqgb///8AzrOLOc+zjODPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4vqzrONL////wDh1bc84tO1/eLTtfHl07kd////AP///wD///8A////AP///wD///8A////AM2xjiTPs4z9z7OM68aqjgn///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7KMas+zjP/Qs4yn////AP///wD///8A////AP///wD///8A////AOPTs0Di07X94tO19d/Ssyj///8A////AP///wDPs4ykz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OL1v///wD///8A////AOPTtZDi07X/4tO2uP///wD///8A////AP///wD///8A////AP///wDPs4yUz7OM/9Czi4L///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AMyzgArPs4ztz7OM+9OwjR3///8A////AP///wD///8A////AOPVuBLi07Xk4tO1/+PUtmX///8A////AP///wD///8Az7OMtM+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjOX///8A////AP///wDmzLMK4tO13OLTtf/i07Zi////AP///wD///8A////AP///wDVuI4Sz7ON9c+zjPjTsZAX////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7KMhc+zjP/PtIuL////AP///wD///8A////AP///wDi07au4tO1/+HTtaz///8A////AP///wD///8A0bSKPc+0jNzPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/PtI3q0LOONv///wD///8A////AOLStT7i07X+4tO18OPQsxv///8A////AP///wD///8Az7OLec+zjP/Os4yd////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AMy4jxnPs4z5z7OM8NWqlQz///8A////AP///wDh0rVn4tO1/+LTteLh0rQR////AP///wDQtIxHzrONws+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4y0zbSMM////wD///8A4dO2kuLTtf/h07S1////AP///wD///8AzMyZBc+zjOXPs4z/0LKOK////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7OMoM+zjP/Ps4xvv7+ABP///wDg07Qp4tO19uLTtf3i0rU+////AdCzi1HQs4zMz7OM/8+zjP/Ps4z/z7OM2NCzjLLPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM08+zjN7Ps4z/z7OM/8+zjP/Ps4yxz7WKMOjRuQvi07Xd4tO1/+HUtF////8AzMyZBc6zjV7Ps4z/zrOMuP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM2xiy7Ps4z/0LSM99CzjPfPs43v1byY8ODPsP/i1LWDz7OMW8+zjNTPs4z/z7OM/8+zjP/Ps4y7z7ONSv///wH///8Az7OLZc+zjPHPs4z/z7OM/8+zjP/Ps4z80LOMh7+/gAiqqqoDzrSLWM+zjM/Ps4z/z7OM/8+zjP7Osoyt2MOhYuHStP7XwZ74zrOM58+zjP3Qs4z3z7OM/9Czikb///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDRsotC0LSN/s+zjP/Ps4z/z7OM/8+zjP/PtI78z7SN38+zjP/Ps4z/0LOM99C0jZzRtIss////AP///wD///8A////AP///wDMu4gPz7GMRdCzjXLPsotPyrWKGP///wD///8A////AP///wD///8A0LOKRtCzjLzPs4z/z7OM/8+zjP7QtI72z7OM/8+zjP/Ps4z/z7OM/8+zjP/QtIt3////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7OM2s+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Qs4znzrSNfc62khX///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzbSMM8+0jKnPs4z9z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM+cq1ihj///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A07GQF8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z7z7SM2c+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7SM8c+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/PsotP////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM62khXPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LKNTP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7OM1M+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/9CzjJe/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAjOtYxZz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM9s62khX///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM6ziznPs4z5z7OM/8+zjP/Ps4z/z7OM/8+zjeHStIcR////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM6zjLLPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjWv///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0bSLLM+zjLPPs4zsz7OM4NC0jZzJroYT////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wC/v4AEz7KNe8+zjNTPs4zyz7OMxdGzi03///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////////////////////////////////////////////////////////////////////////////////////////////8P/////////gP////////8Af////////gB////////+AD////////4AP////////gA////////+AH////////gAH///////4YCH//////+Hw+H//////h/j+H/////8P+P8H/////D/4/8P////w//j/8P//g8P/+P/8PB4AD//wf/8ABgA//+A//8AEAH//wB//4AAAAAAAAAAAAAAAAAAAAAAAAH//wB//4AIAP//gP//ABgAP/+A//4AHAYf/yJ/+GA/h4f+Ij/h4f/n4f5jP4Pn/+PwfOOfD+f/8/wZ44w/z//z/wHjwH/P//n/wcHB/5//+f/gAAP/n//8/8AAA/8///z/jgA5/z///n+eADj+P//+fz4APP5///4+PgA+fn///z54AA48////POAAAzz///+Ag8Dggf///4AP//gB////AH///gD///8AAAAAAP///wAAAAAA////AP///4D///+B////gf///8P////j///////////////////////////////////////////////////////////////////////////////////////w=='> <script> //these are placed here because the compression compiler does not deal with the eval function properly. //enter the values of the settings object into their input fields @@ -45,8 +46,8 @@ <div id='header'> <div id='logo'> - <a href='http://mistserver.org' target='_blank'> - <span>Mist/</span> Server + <a> + <span>Mist</span>Server Management Interface </a> </div> <div id='status'> @@ -56,20 +57,31 @@ </div> </div> - <div id='menu'> + <div id='menu' class='menu'> <div class='button'>Overview</div> <div class='button'>Protocols</div> <div class='button'>Streams</div> + <div class='button'>Preview</div> <div class='button LTS-only'>Limits</div> - <div class='button'>Conversion</div> + <!--<div class='button'>Conversion</div>--> <div class='button'>Logs</div> + <!--<div class='button'>Statistics</div>--> <div class='button'>Server Stats</div> <br> <div class='button red'>Disconnect</div> - <a class='button' href='http://shop.mistserver.org' target='_blank'>Mist Shop</a> + <br> + <div class='expandbutton'> + Tools <span class=arrowdown></span> + <div class='expandcontainer'> + <a class='button' href='http://mistserver.org/products' target='_blank'>Mist Shop</a> + <a class='button' href='http://mistserver.org/streamtester' target='_blank'>Stream Tester</a> + <a class='button' href='http://mistserver.org/wiki/Category:Guides' target='_blank'>Guides</a> + <div class='button'>Email for Help</div> + </div> + </div> </div> <div id='page'></div> - + <div id='ih-button' title='Activate integrated help'>?</div> </body> </html> \ No newline at end of file diff --git a/lsp/header.html b/lsp/header.html index 50a544d9..5bb1b924 100644 --- a/lsp/header.html +++ b/lsp/header.html @@ -1,4 +1,4 @@ <html> <head> <meta http-equiv='content-type' content='text/html;charset=utf-8' /> - <title>MistServer Manager</title> \ No newline at end of file + <title>MistServer MI</title> \ No newline at end of file diff --git a/lsp/jquery.js b/lsp/jquery.js deleted file mode 100755 index 674c18e2..00000000 --- a/lsp/jquery.js +++ /dev/null @@ -1,5 +0,0 @@ - /*! jQuery v1.7.1 jquery.com | jquery.org/license */ - (function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function cb(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function ca(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bE.test(a)?d(a,e):ca(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)ca(a+"["+e+"]",b[e],c,d);else d(a,b)}function b_(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bT,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bP),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bC(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bx:by,g=0,h=e.length;if(d>0){if(c!=="border")for(;g<h;g++)c||(d-=parseFloat(f.css(a,"padding"+e[g]))||0),c==="margin"?d+=parseFloat(f.css(a,c+e[g]))||0:d-=parseFloat(f.css(a,"border"+e[g]+"Width"))||0;return d+"px"}d=bz(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0;if(c)for(;g<h;g++)d+=parseFloat(f.css(a,"padding"+e[g]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+e[g]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+e[g]))||0);return d+"px"}function bp(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c+(i[c][d].namespace?".":"")+i[c][d].namespace,i[c][d],i[c][d].data)}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?m(g):h==="function"&&(!a.unique||!o.has(g))&&c.push(g)},n=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,l=j||0,j=0,k=c.length;for(;c&&l<k;l++)if(c[l].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}i=!1,c&&(a.once?e===!0?o.disable():c=[]:d&&d.length&&(e=d.shift(),o.fireWith(e[0],e[1])))},o={add:function(){if(c){var a=c.length;m(arguments),i?k=c.length:e&&e!==!0&&(j=a,n(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){i&&f<=k&&(k--,f<=l&&l--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&o.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(i?a.once||d.push([b,c]):(!a.once||!e)&&n(b,c));return this},fire:function(){o.fireWith(this,arguments);return this},fired:function(){return!!e}};return o};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p,q=c.createElement("div"),r=c.documentElement;q.setAttribute("className","t"),q.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="<div "+n+"><div></div></div>"+"<table "+n+" cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="<div style='width:4px;'></div>",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h=null;if(typeof a=="undefined"){if(this.length){h=f.data(this[0]);if(this[0].nodeType===1&&!f._data(this[0],"parsedAttrs")){e=this[0].attributes;for(var i=0,j=e.length;i<j;i++)g=e[i].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),l(this[0],g,h[g]));f._data(this[0],"parsedAttrs",!0)}}return h}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split("."),d[1]=d[1]?"."+d[1]:"";if(c===b){h=this.triggerHandler("getData"+d[1]+"!",[d[0]]),h===b&&this.length&&(h=f.data(this[0],a),h=l(this[0],a,h));return h===b&&d[1]?this.data(d[0]):h}return this.each(function(){var b=f(this),e=[d[0],c];b.triggerHandler("setData"+d[1]+"!",e),f.data(this,a,c),b.triggerHandler("changeData"+d[1]+"!",e)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise()}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h<g;h++)e=d[h],e&&(c=f.propFix[e]||e,f.attr(a,e,""),a.removeAttribute(v?e:c),u.test(e)&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; - f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=[],j,k,l,m,n,o,p,q,r,s,t;g[0]=c,c.delegateTarget=this;if(e&&!c.target.disabled&&(!c.button||c.type!=="click")){m=f(this),m.context=this.ownerDocument||this;for(l=c.target;l!=this;l=l.parentNode||this){o={},q=[],m[0]=l;for(j=0;j<e;j++)r=d[j],s=r.selector,o[s]===b&&(o[s]=r.quick?H(l,r.quick):m.is(s)),o[s]&&q.push(r);q.length&&i.push({elem:l,matches:q})}}d.length>e&&i.push({elem:this,matches:d.slice(e)});for(j=0;j<i.length&&!c.isPropagationStopped();j++){p=i[j],c.currentTarget=p.elem;for(k=0;k<p.matches.length&&!c.isImmediatePropagationStopped();k++){r=p.matches[k];if(h||!c.namespace&&!r.namespace||c.namespace_re&&c.namespace_re.test(r.namespace))c.data=r.data,c.handleObj=r,n=((f.event.special[r.origType]||{}).handle||r.handler).apply(p.elem,g),n!==b&&(c.result=n,n===!1&&(c.preventDefault(),c.stopPropagation()))}}return c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0)}),d._submit_attached=!0)})},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on.call(this,a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.type+"."+e.namespace:e.type,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.POS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() - {for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bp)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bn(k[i]);else bn(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bq=/alpha\([^)]*\)/i,br=/opacity=([^)]*)/,bs=/([A-Z]|^ms)/g,bt=/^-?\d+(?:px)?$/i,bu=/^-?\d/,bv=/^([\-+])=([\-+.\de]+)/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bv.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bC(a,b,d);f.swap(a,bw,function(){e=bC(a,b,d)});return e}},set:function(a,b){if(!bt.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cv(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cu("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cu("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cv(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cn.test(h)?(o=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),o?(f._data(this,"toggle"+i,o==="show"?"hide":"show"),j[o]()):j[h]()):(k=co.exec(h),l=j.cur(),k?(m=parseFloat(k[2]),n=k[3]||(f.cssNumber[i]?"":"px"),n!=="px"&&(f.style(this,i,(m||1)+n),l=(m||1)/j.cur()*l,f.style(this,i,l+n)),k[1]&&(m=(k[1]==="-="?-1:1)*m+l),j.custom(l,m,n)):j.custom(l,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:cu("show",1),slideUp:cu("hide",1),slideToggle:cu("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cr||cs(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){e.options.hide&&f._data(e.elem,"fxshow"+e.prop)===b&&f._data(e.elem,"fxshow"+e.prop,e.start)},h()&&f.timers.push(h)&&!cp&&(cp=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cr||cs(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cp),cp=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(["width","height"],function(a,b){f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cy(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.support.fixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.support.fixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); - diff --git a/lsp/main.css b/lsp/main.css index 8a5805eb..1a203bf7 100755 --- a/lsp/main.css +++ b/lsp/main.css @@ -3,7 +3,7 @@ body { padding: 0px; width: 100%; height: 100%; - font-family: sans-serif; + font-family: FuturaICGLight, sans-serif; } #shield { position: absolute; @@ -31,17 +31,16 @@ body { margin-left: 25px; padding: 3px 0px 0px 60px; height: 66px; - width: 190px; font-family: FuturaICGLight, sans-serif; font-weight: normal; - text-transform: uppercase; + font-size: 2em; background-image: url('data:image/png;base64,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'); background-repeat: no-repeat; } #logo > a { text-decoration: none; - font-size: 1.2em; color: #c7c7c7; + cursor: pointer; } #logo > a > span { color: #9cc1db; @@ -68,15 +67,38 @@ body { } #message { position: absolute; - bottom: 10px; + bottom: 0; + font-family: sans-serif; font-size: 0.8em; - color: #c7c7c7; + color: #777; } #message > a { text-decoration: underline; color: #505050; cursor: pointer; } +#ih-button, #ih-button.active:hover { + position: absolute; + top: 108px; + right: 1em; + width: 1.2em; + line-height: 1.2em; + height: 1.2em; + font-weight: bold; + border: 2px solid #bcbcbc; + border-radius: 2em; + text-align: center; + background-color: #ededed; + color: white; + box-shadow: 2px 2px 5px #ededed; + text-shadow: 1px 1px 2px #6696ce; + cursor: pointer; +} +#ih-button.active, #ih-button:hover { + border-color: #6696ce; + background-color: #9cc1db; + text-shadow: 1px 1px 2px #6696ce; +} #menu { position: fixed; top: 100px; @@ -88,25 +110,54 @@ body { background-color: #ededed; border-right: 1px solid #bcbcbc; } -#menu > .button { +.menu .button, +.menu .expandbutton { color: #333; margin: 5px auto 5px 13px; padding: 0px 15px 0px 15px; height: 31px; line-height: 30px; - width: 167px; cursor: pointer; display: block; text-decoration: none; + background-position: 100% 0; } -#menu > .button:hover { +#menu .button { + width: 167px; +} +.menu .button:hover { color: #000; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMUAAAAfCAYAAABXnQm5AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wFDwkbJKNP/RYAAAkpSURBVHja7ZxNb+PGGYCfGXl3bZGUzBk5lijJLtJNEaCnAi2CXor+iv7J/oOiaHsp2tyTTdqkRdYSKXmXHEqiPrxei9PDULKdBrtXwaAulvVIfDnDeef9mhmRJImdzWbc3t7wlxF0lc80LeienTJJZwx0wJUpGOgW43TJoOOTpAt6nZrX/JB5izhdMjwLiN8WfBb5/PpCiWeixCIR3GGtACmgtAgLWwtCSuR8PufVq1f8/R//JFInJLN3RNq/F5itnMBswYX2GWVLok6r5jU/aD7NFgxVk9HbgoH2+S5Z8P3bjW3YOyR3lLaBFBZKi0UCILHY8g55+27D9ZuU0WjEON8Qnb5gnG/2Ai+0R2wK+irgKltyqXynkTWv+QHzrvKZzNYMlcdVvqGnA778z1verEpbcoQQAqwEShAlJQJEiZQgLRLJHRJLFHpMzZJBeOJMUsdnlC8ZnPok+ZK+9rgyCwYdv+Y1P2ie5CvOQ4+RWdFXJ1xnSwYdjz9/k/L+/XtnLYREAJRbhHTWwlqBFEJQ2gYlgtgsOVd+ZSmcSYpCn/HsXvDu85rX/JB5XwXE2YoL7TNJF5yrFpNsyVn7hL9+v7AlR0hbAiBEA+wDN2pbQkOWCFsy0AETs2YQnjBOl1yoFnG22gsehn71eVDzmh88H6omsSnodVpM8wVd5XOV3/C8AV/HmbVsQTQorUDYEkQDay1SCsu2dGHGeKcQ+YZBx6+CF484WzmfzRRcaJ/XZlXzmh88T/IlUegRp0tnOfIbBuqEcVowXYO5EXZb4gJucEG2BAmlCzqkoB82Gc9W9JRLa9XBXM2fUrA9aD8nSRd0dZtJVvDtdEVZljTsHVZIpLAupqDyo+z2jqlZEoU+bzKXB66DuZo/pWB7nG9cOtfM6Sqff18bvs82diuOACiFBCTSWmc6kA0idUKc39BTXpUHroO5mj+dYPtRnSNb0tchX/43J13dWsEdtmwguHMxxS7qHpk1/fB4X6eog7WaP6Vg+6FrFXUCJumCofL4078Mt9sS2SjZ2gaytIKG2O6zTz+uU7w2Sy60/8Bncz7cOKt5zQ+b93culGpxnS0euVZjs2agjolNQdd/wd++M5btHVLY+zqFFZJxVjyqUyTpgkvlcZUtH5XSX5sVUafmNT9sPs0WRKFHkhVEqvXItRqenhDnG7dGanYDjWO+mW4sorIU8oGleFinqLMbNX9K2adHrlX+jq5uO8VpvyDJ10zWW8xqbaUUltKKn6xT1NmNmj+l7NND12qojpmkM7phmyRfVa7UEnnUeCWhrLJPP1WnqLMbNX9K2SenEL3OKa/zgp72ifM5XR2QpAVfXCrC5/KXH6xTJOmCi+rCfRUwyguGYcCo8uFqXvND5pNq+fgu5rjKVvtl55dhQJytiVSba1Pw6SdtXnZeCPvxOkXts9b86cQUD+sUI7NmqJpMzJKuCnipjmgIyVYcfbhOUfusNX9aMcW9wvS1x2TmYmi/cUf39FhsEUhbfqROUfusNX9CMcWjOkW65Dz0WKxv+c1lKAAE5cfrFHVFtOZPqaL9SKE6Pm/Mit+/bP1RPDt2lWwpKcvyI3WKumJa8ydU0X6oMHG24lcXbU597w+N8p2zEOV7pJR8sE6RpAuG2v9RxfA+rVXzmh8yn2YFXeVzbRach+1HCvPp2Qmfdz0BZTX+Ybda9oN1iqhzf2zIOHPpr53AOF3Q0z6TdO6CnWzNQAeM8iV95XOdzulpp5nDsM04L7hUAVeZC+Jj49Jok0qjx7uNIMZlFeJ0QdQJiLMN/X12wWdk1vS0W8zV7fjE2cplEbIVl8rJ7+rAmczdDLLbiKJ3y+E94mzGIAycT6q9e5P7dlEtFpsR6dN9++NsRaR27av4g/6ZZAXD8P7+YrMi6gQk6bJ6EK7/Rmbtfpe5/Srx/kFVM9nugZs5vU6LSVYQaddPA93iquqHOJvRU22SdE5fOxdhGPr8MFvT0z6JmVffe7d3IYahC0qjjuvfQcdjkq3oa4+xcf2488Wn2Zy+Chin6/uNPGGbcbair9z1o06LUbbkUp9UaVGf2NzL72nX/r5u7tOko4rH1TE1+/FVZZNG+ZKe9plW7R1l7vdxtmGgPcbZip5q78fHpFrTlDzIRg20S7v2qv7t6YA4nz9SmM8+8ZDVvmwhBIKSrZBgt0grJMKC2NoHDSv2GzH6D3Y2jbOCng6YZjMiHTA1K7ra+XQDdUKSFkShC3Y+0S2mpurw2YL+qc8oK+jrE2JTEIVNktylzZLqwUwqzY7NbiAsidQxiVkThX71f5OpKejqgOtsRaSajPMNXdVklDn518b9nRrnayZmTaSaTNLZPviKVJt4Vg1ws3b3mRX0ztxA7Oo2EzPftz9STabZrn3/3z/uvu/vLwqbTLIlPe25rF7Vf5FqugdZTQCRau55bJb7/u2Frf1M59KGbqLqq8DtKFNtpvmCnm6RmA1R6OQPT5tMzZJe2CLJV0TqudtHUHHXj65/42xNVzVJzJp+2CTO1u56ecF5tVaor0+cIqsmEzOnpz2S3F1/khUMlMeVuanataSr7uVPjWt/YjYMlOcm2opHulK8/YTl7cfPtDorIDbu+onZuHGQruhpj2k+34+PrgpIzNrJSavnYRb0ddNNtJ0WSTonUgFJ5hTHe2bpBi+ceaheFknDujOhJIAVEiskw7OAUbXI6tosHp3ukVQz4G6A1rzmh8x/MHd7CzzQbSbmhnPtMS/WfHHRfqQQP35JYd2hBYI7RumcS91yM1roVTOwcwn62t1AtNvBVPOaHzC/1MdOIZTHVb5iqE6Isxt+97nm6IMqAdJagRXOt+pXpfC+Cpxg1dy7COPMnYbgTPJJzWt+2Dxd0uu09y7Y62zGb3/WRHtNUcqPKEVDgq18qsQUXGiPkdnsNc8Fgxsud7545dvXvOaHzAcdj+nbXexW8Nl5m5fdlrB2S8mHtUJ8+83X9quvvmKSzjmyN1iOsAKwEiEE1lpElaESFqrslTuktuY1P1RuBaJRctbp8Yuff8p590w0jp6771r54Hv2Ptiu3oskGdv5vOD23YbSCiQCK0pA7oXZ6r3liH0KtyqL17zmh8gtkgaW581jWn4gXjx7TomtzpDd7tf7/ZRS/A+du06fZm0R/gAAAABJRU5ErkJggg==); } -#menu > .button.current { +.menu .button.current { color: #fff; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMUAAAAfCAYAAABXnQm5AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wFDwkeFY/mCWkAAALeSURBVHja7Zy/TttQFMa/a6Kq6gRSO3SliHZruxoCLwHiEfIGJTZCiA322slDMAIbQgjm2KkoqZirdOmCOvTPkK9DguU4dpw0HZL48/JL8jl2dHRPzj3n3Gu0223e3d0xDBsEwLW1tYjGmOj9+vq6dOkzpce5s7NDjHq0Wi2enJzQq/mZFxTFeeDh4eFojtEMG/T8OquuI8OJc8+zs7N8xwjDkDX/I123GoUk27b7QpRt232hKhnCpEufVj3JpaUl3t/fLw+PFM0mPa8WRYq4Q4jiPDDpOOVyeXi0aARN1mseHWd37ORFnIx59pU+mR4/LzmuDw4OOHT65HkeHUc5hVgcbmxs8PLykhlO0aDv+3T2XBlMLBS3t7eZGSl832e1+kGGElWmBYAgCLpO4TrKJcRC8vT0lOpTiGKMi4uL/WXaIAj6qk8ylFhE9pVpuzmFIoVYzP5FnPv7+0ztU8hgYlG5ubnJi4sLRtWneJ9i1Ja5KM5TA7VUKvHm5obqU4hij8fHx1SfQlSk6EWKra0tqk8hij2Wy2Wen59TfQpREaLHSqXCgY62qk9iUbmyssKMtU+TR4q8fRhF18Xpo2VZvLq6Yu5+ikkbIeMOkOT30srBk+Y4j9d9/F1Jpu04FOefR0dHw1fJ/q8+RdbA+ldHmXSDSfL+WY4Qv944T5EQ52zZeHI/Rd5A0xRJ+qzpaX9gA9WmgaXjzZA1z6dbdWRQ6YXQB6pNaU7h+3U6zl7unFy69FnWbdtOrzalTZ9qnk/X2R37cSGiOGvVpuvr63yn6Ha063Rj1adkbpGVhEqXPq16WhElWtuUd3xqBvT8up7mIRa32pQ4zJfWZ97e3uLb9weU+AtECTQAaMEYA5Iw6HRPJroaAEK69CnWaWAWOnjx/CVWXy3j7ft3ZmSnaLe/8uHhB/78/okODSwY0HQAWNHN2HtNlIDeZwCkS59anbCwAOLJs6d4s/p6ZIcAgL81egTU3KTFZQAAAABJRU5ErkJggg==); } +.arrowdown { + width: 0; + height: 0; + display: inline-block; + border: 4px solid transparent; + border-top-color: #333; + border-bottom-width: 2px; +} +.menu .expandbutton .expandcontainer { + display: none; +} +.menu .expandbutton.active .expandcontainer, +.menu .expandbutton:hover .expandcontainer { + display: block; +} +.menu .expandbutton { + margin-bottom: 0; +} +.menu .expandcontainer .button { + margin-top: 0; + width: 149px; +} +.menu .expandcontainer { + margin-left: -10px; +} #page { position: fixed; top: 100px; @@ -114,17 +165,20 @@ body { left: 230px; right: 0px; overflow: auto; - padding: 10px 20px 10px 20px; + padding: 1em; } .description { font-size: 0.9em; color: #777; clear: both; text-align: justify; + width: 40em; + clear: none; } .input_container { margin-top: 25px; - width: 75%; + width: 40em; + overflow: hidden; } label, .pretend-label { width: 100%; @@ -137,13 +191,18 @@ label, .pretend-label { label > input, label > select, label > textarea, .pretend-label > input, .pretend-label > select, .pretend-label > textarea { float: right; + clear: right; width: 60%; - padding: 5px; - margin: 2px 0px 2px -8px; + padding: 0.3em; + margin: 0 0 0 -0.5em; color: #505050; border: 1px solid #b4b4b4; - height: 28px; + height: 2.3em; background-color: #fff; + font-size: 0.8em; +} +input[disabled], label > select[disabled], label > textarea[disabled] { + opacity: 0.5; } label > textarea, .pretend-label > textarea { height: 100px; @@ -166,24 +225,26 @@ label > span, .pretend-label > span { line-height: 16px; font-size: 0.8em; } -button { +button, .fakebutton { font-weight: bold; font-size: 1em; - height: 30px; + height: 1.8em; + line-height: 1.8em; background-color: #505050; color: #fff; border: none; - margin: 5px 0px 5px 5px; - padding: 0px 10px 0px 10px; + margin: 0.3em 0 0.3em 0.3em; + padding: 0 0.5em 0px 0.5em; cursor: pointer; float: right; + text-decoration: none; } -label > span > button, .pretend-label > span > button { - margin-top: -8px; - margin-bottom: -8px; +label > span > button, .pretend-label > span > button, +label > span > .fakebutton, .pretend-label > span > .fakebutton { + margin-top: -0.5em; + margin-bottom: -0.5em; } -table button { - height: 25px; +table button, table .fakebutton { float: none; } .red { @@ -221,6 +282,7 @@ td,th { padding: 5px; } td { + font-family: sans-serif; font-size: 0.8em; } tr:nth-child(even) { @@ -242,14 +304,83 @@ tr:nth-child(odd) { .sortable th.sortdesc { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAB3RJTUUH1wsGDC4gqCc4egAAAAlwSFlzAAAewQAAHsEBw2lUUwAAAARnQU1BAACxjwv8YQUAAADDSURBVHjaY2AgDdSyoYswI3MaGBT/KjMcR1HAhGA2MTBEMHxkiEhDUcAIY7Qw/BNSfMrL8fLHWenv7xZjMYFfaAsXx7s/3BwSWxj4Maxo4xfeLmX5/P45i2f3FSyltmOxYtGHL+9u2X59/VdU4TCLUJUAVk8WREz+mBSBKsaIYFYJqQAd+QLoyAXvsHizhV9qCz/H5z+CHHJbYvmxKBDfLmf57v51i9f3VSzltmNRwKf1/v5T2+6zj21f3xfRIjGGKAMAccw+PBxJau0AAAAASUVORK5CYII=); } +/* negate the tables in the flot legend.. */ +.legend table { + width: auto; + margin: auto; +} +.legend tr { + background-color: rgba(255,255,255,0.5) +} + label > table, .pretend-label > table { float: right; width: 60%; } -label > .unit, .pretend-label > unit { +label > .unit, .pretend-label > .unit { width: auto; position: absolute; - left: 75%; + left: 41em; + padding-left: 0.2em; + font-size: 1em; +} +.ih-balloon { + position: absolute; + background-color: #ededed; + border-radius: 0.5em; + border: 0.1em solid #bcbcbc; + padding: 0 0.5em; + text-align: justify; + line-height: normal; +} +.ih-balloon.pageinfo { + margin: 2em 0 1em 1em; + float: right; + position: relative; + width: 20em; +} +.ih-balloon.inputinfo { + left: 45em; + margin-right: 1em; +} +.ih-balloon:before, .ih-balloon:after { + content: '.'; + color: transparent; + width: 0; + height: 0; + position: absolute; + border: 0.6em solid transparent; +} +.ih-balloon.pageinfo:before, .ih-balloon.pageinfo:after { + right: 0.5em; +} +.ih-balloon.inputinfo:before, .ih-balloon.inputinfo:after { + top: 0.5em; +} +.ih-balloon.pageinfo:before { + border-bottom-width: 0.7em; + border-bottom-color: #bcbcbc; + top: -1.25em; +} +.ih-balloon.pageinfo:after { + border-bottom-color: #ededed; + top: -1.1em; +} +.ih-balloon.inputinfo:before { + border-right-width: 0.7em; + border-right-color: #bcbcbc; + left: -1.25em; +} +.ih-balloon.inputinfo:after { + border-right-color: #ededed; + left: -1.1em; +} + +.ih-balloon p { + font-weight: normal; +} +.ih-balloon * { + font-size: 0.9em; } #input-validation-info { font-size: 0.8em; @@ -257,4 +388,106 @@ label > .unit, .pretend-label > unit { float: right; width: 60%; margin-right: -5px; +} +#tooltip { + display: none; + position: absolute; + z-index: 1337; + border: 1px solid #bcbcbc; + padding: 0.5em; + background-color: #ededed; +} +.checklist label { + width: auto; + height: auto; + min-height: 0; + padding: 0; +} +.checklist input[type=radio], +.checklist input[type=checkbox] { + width: 1em; + height: 1em; + float: none; + margin: 0; + margin-right: 0.5em; + vertical-align: baseline; +} +.LTS-only.LTSstuff_done, +#menu .LTS-only.LTSstuff_done, +.LTS-only.LTSstuff_done p, +.LTS-only.LTSstuff_done label { + color: #b4b4b4; +} +button[disabled=disabled] { + background-color: #a0a0a0; +} +.LTS-only.LTSstuff_done table { + opacity: 0.5; +} +.table { + display: table; +} +.row { + display: table-row; +} +.cell { + display: table-cell; +} +#graphcontainer { + width: 100%; + background-color: white; + display: -webkit-flex; + display: -moz-flex; + display: -ms-flex; + display: flex; + -webkit-flex-flow: row wrap; + -moz-flex-flow: row wrap; + -ms-flex-flow: row wrap; + flex-flow: row wrap; +} +#graphcontainer .graph-item { + -webkit-flex: 1 1 auto; + -moz-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + width: 400px; + border: 1px solid #ccc; + margin: 0.1em; + padding: 0 0.5em; +} +#graphcontainer .graph { + height: 250px; +} +.legend { + font-size: 0.8em; +} +.legend-list { + -webkit-column-width: 15em; + -moz-column-width: 15em; + column-width: 15em; +} +.series-color { + width: 1em; + height: 1em; + display: inline-block; + margin: 0 0.5em; +} + +@font-face { + font-family: FuturaICGLight; + src: url(data:font/ttf;base64,AAEAAAARAQAABAAQRkZUTULheo8AAAEcAAAAHEdERUYA+wAEAAABOAAAACBPUy8ylj1AXwAAAVgAAABgY21hcFgHjPwAAAG4AAACCmN2dCAN2AtLAAADxAAAADhmcGdtU7QvpwAAA/wAAAJlZ2FzcAAAABAAAAZkAAAACGdseWYK2LXwAAAGbAAAekhoZWFk/5ZRsgAAgLQAAAA2aGhlYREWB7YAAIDsAAAAJGhtdHhnJ0VYAACBEAAAAzhsb2Nhi+Rt1gAAhEgAAAGebWF4cAHrAaAAAIXoAAAAIG5hbWV3QTKKAACGCAAAAftwb3N0dLwSjwAAiAQAAAJncHJlcJzpK2wAAIpsAAABJXdlYmbS0k+bAACLlAAAAAYAAAABAAAAAMmJbzEAaXdgrS0QrAAAAADLwYNRAAEAAAAOAAAAGAAAAAAAAgABAAEAzQABAAQAAAACAAAABAQ2AZAABQAEBZkFMwAAASUFmQUzAAADoABmAhIAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQWx0cwDAACDgAAZm/mYAAAgzAkQAAAABAAAAAAPGBggAAAAgAAEAAAADAAAAAwAAABwAAQAAAAABBAADAAEAAAAcAAQA6AAAADYAIAAEABYAfgCjAKUArgCxALQAtgC7AM8A1gDcAO8A/AD/AVMBeCAKIBQgGiAeICIgJiAvIF8hIuAA//8AAAAgAKAApQCnALAAtAC2ALoAvwDRANgA3wDxAP8BUgF4IAAgECAYIBwgIiAmIC8gXyEi4AD////j/8L/wf/A/7//vf+8/7n/tv+1/7T/sv+x/6//Xf854LLgreCq4KngpuCj4JvgbN+qIM0AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQYAAAEAAAAAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAAAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGEAent9f4aLkJOSlJaVl5mbmpydn56goaKko6WnpquqrK0Ab2RlZ8hykW5pzHFoAHyMAHAAAGYAAAAAAABqcwCYqXVjbAAAAABrdMlidnmKr7DAwcXGwsOoAK6xAAAAAAAAAADExwB4gHeBfoOEhYKIiQCHjo+NAAAAAAAAAAAAAAAAAAAAA8YGCAChAJcAjwCUAIcAmwClAOgAqADsAJ0ApACoAK0AsQC4ALwAlgCfAKoAegB9AEkAcABtsAAssAATS7BMUFiwSnZZsAAjPxiwBitYPVlLsExQWH1ZINSwARMuGC2wASwg2rAMKy2wAixLUlhFI1khLbADLGkYILBAUFghsEBZLbAELLAGK1ghIyF6WN0bzVkbS1JYWP0b7VkbIyGwBStYsEZ2WVjdG81ZWVkYLbAFLA1cWi2wBiyxIgGIUFiwIIhcXBuwAFktsAcssSQBiFBYsECIXFwbsABZLbAILBIRIDkvLbAJLCB9sAYrWMQbzVkgsAMlSSMgsAQmSrAAUFiKZYphILAAUFg4GyEhWRuKimEgsABSWDgbISFZWRgtsAossAYrWCEQGxAhWS2wCywg0rAMKy2wDCwgL7AHK1xYICBHI0ZhaiBYIGRiOBshIVkbIVktsA0sEhEgIDkvIIogR4pGYSOKIIojSrAAUFgjsABSWLBAOBshWRsjsABQWLBAZTgbIVlZLbAOLLAGK1g91hghIRsg1opLUlggiiNJILAAVVg4GyEhWRshIVlZLbAPLCMg1iAvsAcrXFgjIFhLUxshsAFZWIqwBCZJI4ojIIpJiiNhOBshISEhWRshISEhIVktsBAsINqwEistsBEsINKwEistsBIsIC+wBytcWCAgRyNGYWqKIEcjRiNhamAgWCBkYjgbISFZGyEhWS2wEywgiiCKhyCwAyVKZCOKB7AgUFg8G8BZLbAULLMAQAFAQkIBS7gQAGMAS7gQAGMgiiCKVVggiiCKUlgjYiCwACNCG2IgsAEjQlkgsEBSWLIAIABDY0KyASABQ2NCsCBjsBllHCFZGyEhWS2wFSywAUNjI7AAQ2MjLQAAAAABAAH//wAPAAIAXP/0ATsGCAALAA8ANwCyCAAAK7ECCumyDQIAKwGwEC+wANaxBgzpsQYM6bMMBgAIK7EPFOmxEQErALENAhESsAw5MDE3NDc2FxYVFgcGJyYTETMRXGY4Ih8BZjgiHyCSZmYIBSUhMGYJBSYhAREEwfs/AAAAAAIAZgQXAd0GBwADAAcAKACyAAIAK7AEM7QDCgAJBCuwBjIBsAgvsAfWtAYUABIEK7EJASsAMDETMwMjEzMDI2aLHVbTjBxWBgf+EAHw/hAAAgBmAAoEnAYLABsAHwFOALIHAgArsggLDDMzM7IfAQArsgMQHjMzM7EJCOmyBgoNMjIysAAvsxQXGBskFzOxAQPpswIRHB0kFzKyAAEKK7NAABkJK7IVFhoyMjIBsCAvsBrWsRkN6bAZELEHASuxCA7psgcICiuzQAcECSuzFggHCCuxFQ3pshUWCiuzQBUTCSuwCBCxCwErsQwO6bEhASuwNhq6PxL1IgAVKwq6PyD1dAAVKwqwGhCzAhoHEyuzAxoHEyuzBhoHEyuwGRCzCRkIEyuwFhCzChYLEyuwFRCzDRUMEyuzEBUMEyuzERUMEyuzFBUMEyuwFhCzFxYLEyuwGRCzGBkIEyuwGhCzGxoHEyuwGRCzHBkIEyuwFhCzHRYLEyuzHhYLEyuwGRCzHxkIEysDQBACAwYJCg0QERQXGBscHR4fLi4uLi4uLi4uLi4uLi4uLrBAGgAwMRM1MxMjNTMTMwMzEzMDMxUjAzMVIwMjEyEDIxM3MxMjZuc91u5JoU3+RaJJwds7zedYm1j+/1WeWbn6PfoB0aEBYpYBof5fAZ3+Y53+pan+QQHH/jkBx6UBWgADAGD/CwQZBtYALwA4AEAAPAABsEEvsAfWsg8qNTIyMrE5FOmyER0oMjIysDkQsT0BK7EjEOmxQgErsT05ERKwGDmwIxGxIBc5OQAwMRM3FxYXFh8BESckNz4BPwE1MxUXFhcWFwcnJicmJxEXBBcWFRQHBg8BFSM1JyYnJhMWFxYfAREHBgE3PgEnJi8BYKQGF0VNSxlH/twFA8KUFIMVQVVaPYkQFjYoNV8BBBQBk3ZoDYgRkneF6gUyHVQRELYBTxtJawQHsxEBcy8Zcz1FCwQCbyKK9o7SEwPKygMINzxuUhUhOh8L/ggvf/ESFLqKbwoC4doBDGl0A6xEMB40CgHBB0z7eQgVsVepTQkAAAUAbP/sBUAGMAAPAB8AIwAzAEMAlACyMAAAK7AjM7E4B+myIQIAK7IEAgArsRwH6bRAKDAEDSuxQAfptBQMMAQNK7EUB+kBsEQvsADWtBAUACEEK7AQELEYASu0CBQAIQQrsAgQsSQBK7Q0FAAhBCuwNBCxPAErtCwUACEEK7FFASuxGBARErEgIzk5sTw0ERKxISI5OQCxQDgRErAkObEcFBESsAA5MDETNDc2NzYXFgcGBwYHBicmNxQXFhcWNzYnJicmBwYHBhMBFwkBNDc2NzYXFgcGBwYHBicmNxQXFhcWNzYnJicmBwYHBmxJT3eDUkkBAUdPfoFQRnUjK1BSLSYBASUsUkkuJwsDfFX8ewHcSU93g1JJAQFHT3+BT0Z1IytPUi4lAQEkLFNJLScEo5Z1gAIDg3aemHN+AgGCdZ9sTFsBAVtMa2tJWgEBWE37GQX3Kfn7AZOWdYACA4R2nphzfgIBgnWfbEtbAgFbTWtrSFoBAVhNAAAAAwBb/+UFfgYiAB4AKgA3AFsAshcAACuyGwAAK7EjBOmyCwIAK7E1BukBsDgvsADWsR8O6bAfELExASuxDg7psTkBK7ExHxEStwcLEQMjJywuJBc5sA4RsCY5ALE1IxEStQMRFRgnLiQXOTAxEyYlNycmJyY3NDYzMhYXFg8BATcXBwEjJwcGJyYnJjcWFxYzMj8BAQcOARMGHwE3Nic0JyYHDgFbBQEcdSFAFyEBvI2PugQFvlgBP7B2vQEC1adT4721e4ChDMsZHol6iv6jfVlr9gQ1UGhmAQESmkJbAY3+uk0sVzNGU4e6sYWgk0X+XsVj4/6v0kGxBQV1eL7WOgdmcgHGVDutAwhFSWxQTl4LDZMFA2AAAAEAWgQYAOYGCAADACIAsgACACu0AwoACQQrAbAEL7AD1rQCFAASBCuxBQErADAxEzMDI1qMHFYGCP4QAAEAYv7OAbIG/AAVABMAAbAWL7AA1rELDumxFwErADAxEzQTEj8BFwcGAwIHFBcSHwEHJyYDJmIkK0whlCg9KR8BHCRGHIseVSsbAs32AQUBL7RRQV2R/p3+8IWt2f7bxk1JQbkBb/AAAQBt/tMBvAcCABUAEwABsBYvsBHWsQYO6bEXASsAMDETNxcWExIVFAcCDwEnNzYTNjU0AwInbZIjTSojIypNH4oaRCYeIis7BsBCUrL+0P8A+vL8/s6gQUlNygEi46R+ARUBY5EAAAABAGwEcgKfBpAADgArALAKL7AMM7QECgAIBCsBsA8vsADWtAcMAAgEK7EQASsAsQQKERKwCzkwMRM3FycXBzcXBxcHJwcnN2wjyQlwDMclzoFec3ZZgAWYbErWAtVLbTmnRbKxRacAAAEAYP//BDoD2AALAEoAsgoAACuyAwEAK7QBAAoDDSuwBzOxAQPpsAUyAbAML7AK1rACMrEJDemwBDKyCQoKK7NACQcJK7IKCQors0AKAAkrsQ0BKwAwMRM1IREzESEVIREjEWABoKEBmf5hmwGbogGb/mGe/mQBnAAAAAABAGD/NgFjAPkAAwAdALADL7QBCgAKBCsBsAQvsADWsQIM6bEFASsAMDEXExcDYI51tqQBnS/+bAAAAAABAGABuwH8AlQAAwAiALAAL7EBBOmxAQTpAbAEL7EAASu0AwwACgQrsQUBKwAwMRM1IRVgAZwBu5mZAAABAF///wERALAABwAxALIGAAArsQIJ6bIGAAArsQIJ6QGwCC+wANaxBBHpsQQR6bEJASsAsQIGERKwADkwMTc0FxYHFCciX1pZAVxWVlsBAVhZAQABAE3+4gSLBrAAAwAAFwEXAU0Dtoj8PuAHkD34bwAAAAIAa//jBMMGIQAPAB8AOACyDAAAK7EUA+myBAIAK7EcA+kBsCAvsADWsRAR6bAQELEYASuxCBHpsSEBKwCxHBQRErAAOTAxExA3EjckExYDAgcGBwQDJhMGFxYXFjc2JyYnJgcGBwZrk5/rAQijkwMDjZ79/wCfjrEDVWa8wWlZAQFYacCuaFsDCAEq7AD/BAX++ez+xf7O4/4CAQEE6AE++rTWAwTVtf36rtIDA86yAAAAAAEByAAAA3AGCAAFACEAsgQAACuyAQIAK7EABOkBsAYvsATWsQMQ6bEHASsAMDEBNyERIxEByGABSK0FcZf5+AVxAAAAAQCnAAAElQYmABgAOwCyAAAAK7EWA+myEAIAK7EGA+myBhAKK7NABgwJKwGwGS+wA9axFw7psBMysRoBK7EXAxESsBg5ADAxMwE2Jy4BJyYHBg8BIzc2JBcWAAcGBwEhFacC4mIDA6GEg1ZPFAStBRcBCcLFAR0HBWP92QKFA2RzgH2rBgVVToMaKb/1AQH+77+0df10oAABAM7/1gR2BhgALwB2ALIrAAArsQYI6bIGKwors0AGAAkrsh8CACuxFgPpshYfCiuzQBYbCSu0Dg0rHw0rsQ4D6QGwMC+wG9axGhDpsBoQsQkBK7EoEOmwEyDWEbEiEOmxMQErsRobERKxAwE5ObATEbIODSQ5OTkAsQ4NERKwJDkwMRMzFxYXFhcWNicuAS8BNTc2NzYnLgEnJgYPASM3PgEXHgEHBgcWFxYXFgAnJicmJ86sAQReVWV9twIDknBAPG1DSAMDiXJnlwcDrQYZ8Ka49wQFwW8zNQME/uTCsoOJDAGEImZGQQIEu358pwICogIDQEh6bYwFBXpiHSukzAUF863hdERTWIq+/u4BAWZspwACAGMAAATaBlkACAALAEcAsgcAACuyAQIAK7QACQcBDSuwAjOxAAPpsAQyAbAML7AK1rAHMrECC+mwBTKyAgoKK7NAAgQJK7ENASsAsQEJERKwCzkwMTcBETMVIxUjNSUhEWMDvrm1rf4YAej+BVv7RqX6/qECuQABAJj/4QSqBggAHQB6ALIbAAArsQQJ6bIOAgArsRED6bQUChsODSuxFATpsBIyAbAeL7AH1rEYEOmxHwErsDYauj0d7P8AFSsKsA4usBIusA4QsREO+Q6wEhCxDQ75ALANLgGzDQ4REi4uLi6wQBoBsRgHERKxDxA5OQCxCgQRErEAATk5MDE/ARcWFxY2NzYmJyYPARMhFSEDNjMyFxYXFgAjICeYiRd9vaTnAQHaq1qLN94CYv4XZjAn6ZGJAQH+tuT+57D6XyGuBQXioqPiBAFFGwLao/64CKOY4+H+u/MAAAACAJ//8ASlBf4AEAAgAEYAsg4AACuxFQPpsgcBACuxHQjpAbAhL7AA1rERC+mwERCxGQErsQsL6bEiASuxGRERErIDBAU5OTkAsR0VERKxERk5OTAxEyY3ARcBNjc2FxYXFgAnJgA3FBcWFxY3NjU0JyYnJgcGnwOZAeB9/qtCPMWSjgUH/snW0v7WpVNcoqhjWVZfpKFiWQHxqdICklv+LBUDCpaTztT+yQEBASzJimFpAwNvY5GNYWwDA3JmAAABAGL/6QToBggABQAaALIFAAArsgMCACuxAgPpAbAGL7EHASsAMDE3ASE1IQFiA2H8wgRj+/U3BTKf+eEAAAAAAwDH/+EEdwYhABMAIwAzAH8AshEAACuxGAPpsgcCACuxMAPptCggEQcNK7EoBOkBsDQvsADWsRQR6bAUELAkINYRsQQL6bAEL7EkC+mwFBCxHAErsQ4Q6bAsINYRsQoO6bE1ASuxLCQRErIHAgw5OTkAsSAYERKxFBw5ObAoEbECDDk5sDASsgokLDk5OTAxEzY3JicmJDMyFhUUBxYXFgAnJgA3FBcWNzY3NjU0JyYHBgcGExQXFjc2NzY1NCcmBwYHBscF2rQBAQEBs7H8tdYFBP7jwr/+5rVNVY6KT0lNVY2KUEkhRUyAekhBRU1/ekhBAar+fXzZrvn5rtt+ffq9/vICAQELv3pVXgQDXVJ2elVeBANcUgJUb0tVBANSSmptTVMDA1FKAAAAAgCN/98EtAYeAA8AHwBLALIJAAArsgMCACuxHAPpAbAgL7AA1rEQEemwEBCxGAErsQYR6bEhASuxEAARErAPObAYEbIJCgs5OTkAsRwJERKzCw4QFSQXOTAxEyYAFxYAFxYHAScBBgcGADcUFxYXFjc2NTQnJicmBwaNCQFB39kBNAMDov4TgQFpPUDO/tGoWGKnpWRbVl+lq2VbBAPcAUECAf7K2ajf/VhfAfIVBAwBJ+ORYnADA3NolI9ibQMDcWYAAAIAZP//AUgD/gANABwAQQCyCwAAK7EDCumyEQEAK7EZCukBsB0vsADWsA4ysQcM6bAVMrEHDOmxHgErALEDCxESsQAHOTmxERkRErAVOTAxNzQ2FxYXFhUUBwYnLgERNDYXFhcWFRQHBicmJyZkPTc1IBsdITY1Oz03NSAbHSE2NR8bcTBFAQEjIS0wISQBAUQDRy9FAQEjHzAvICQBASMfAAAAAgBg/sIBuAP/AAMAEQAwALIHAQArsQ8K6QGwEi+wBNaxCwzpsRMBK7ELBBESsQECOTkAsQcPERKxBAs5OTAxGwEXAxM0NhcWFxYVFAcGJy4BYLWb6w89NzUgGx0hNjU7/vQCCzX9+ATLL0QBASMhLS8hJAEBRAAAAQBiAEEEPwQjAAYAABM1ARUJARViA9n9FQLvAfSGAamu/rz+va0AAAIAZgEhBDIC8wADAAcAGgCwAC+xAQTpsAQvsQUE6QGwCC+xCQErADAxEzUhFQE1IRVmA8z8NAPMASGWlgE7l5cAAAAAAQBfAEEEOwQhAAYAADc1CQE1ARVfAu79FgPYQa0BQwFDrf5ZhgAAAAIAWf/ZAzMGFgApADYAjQCyMwAAK7EtCumyBQIAK7ElCOm0GhAzBQ0rsRoD6bIQGgors0AQFQkrAbA3L7Ad1rENC+mwACDWEbEpDumwDRCxKgErsTAM6bAwELEiASuxCA7psTgBK7ENHRESsCc5sTAqERKyHyALOTk5sCIRsQoUOTkAsS0zERKxKjA5ObElEBESswgAHyAkFzkwMRM1NDc2MzYWFRAPAQYHBhYXFjY/ATMVFAcGJy4BNzYlNzY1LgEHDgEfARM0Njc2FhUUBgcGJyZZbWmRnNfkt4ECAV5CQF4KCJ9sZn2HxAMEAQaRhwF7VlN5BQZIOjY3Pjs1NyAeBFGIh11ZAdWY/vRMPStzQmQBAVBAMTF+WlYBAcKG3VkyLppWfAEBdU9m+/wuQwEBRDAuRAEBJCEAAAACAEr/7gYVBfEAOgBGAJYAsjgAACu0LwcAEwQrsAsvsBAztCIHABMEK7A9MrBDL7QYBwATBCuwKS+0AwcAIAQrAbBHL7AA1rQsFAASBCuwLBCxFAErsTsT6bA7ELElASu0BxQAEgQrsUgBK7ElOxESQAkDDQsbKS8zOEEkFzmwBxGwNDkAsSILERKwDzmwQxG2GyANJSwzNCQXObAYErEcHTk5MDETEgAhIBcWAwYHBiMiJwcGBwYnJic0NzY3Mh8BPwEDBhUUMzISNTQnJiMgABEQACEgNzY3MwYHBiEgACUUMzI3NjU0IyIHBkoBAb4BOgEg1N8BAZOXyoIZP056elJOAaSUm4EhFSuyoggqgNq+rPf+9P6EAYQBFAEbp0UoijtZ1/6t/sD+OgHYjmhdU3xnYmAC9AE8AcGnsf716bW6qkdaAQFlYITCkIMBYTqBBP2mGxxGAVW68ZGD/oX+9v7p/nCWQFd2VcsBxnOsvauLtq+pAAAAAgAqAAAFpgZJAAYACQAsALIAAAArsAIzsgECACu0BQcAAQ0rsQUD6QGwCi+xCwErALEBBxESsAk5MDEzCQEjAyEDEyEDKgLMArC8sf1guf4CEv8GSfm3Abf+SQJYAmwAAwCFAAAECgYIAAwAFQAeAGQAsgwAACuxDQPpsgICACuxHgPptBYVDAINK7EWBekBsB8vsADWsQ0O6bAWMrANELEZASuxBAvpsxIEGQgrsQgQ6bEgASuxEhkRErAGOQCxFQ0RErAIObAWEbAGObAeErAEOTAxMxEhIBEUBwQRFAcGIyczMjc2NRAhIzUzIBE0NSYhI4UBDwHqjQEZuJT0oMmaXnL+aZyNAR4E/uaNBgj+bsF5bP7a5W5XoTtIlAEcjgETBQXoAAABAGP/8gU4BhkAIgA9ALIgAAArsRcJ6bIDAgArsQ4I6QGwIy+wANaxFAvpsSQBKwCxFyARErAcObAOEbIAChs5OTmwAxKwCTkwMRMQACUyMxYXFhcVJiEiIwYHBhcUFRYAFzIzIDcVBiMiIyAAYwG5AT4QEH5gbHS3/vQJCf+uqwEEAVr5CwsBB7TK+QIB/sL+MwMBAUIBzQkBHCBT08gEvrn6BgX6/pgGu8+QAc8AAAIAdgAABOwGCAAMABgAOACyAAAAK7ENA+myAQIAK7EYA+kBsBkvsADWsQ0U6bANELESASuxCAvpsRoBKwCxGA0RErAIOTAxMxEhIBcWExQVEAcGISczNjc2NTQ1AgArAXYBdQFD2uED5+X+uc3f/bCrBf6i/dcGCNPa/qoEBP663tmhAbax+gcHAQYBUAAAAAEAlAAAA7EGCAALAEcAsgAAACuxCQPpsgECACuxBAPptAUIAAENK7EFCOkBsAwvsADWsQkL6bAEMrIJAAors0AJCwkrsAIys0AJBwkrsQ0BKwAwMTMRIRUhESEVIREhFZQDHf2UAln9oAJvBgih/jmb/ZyhAAAAAAEAlAAAA00GCAAJAEAAsgAAACuyAQIAK7EEA+m0BQgAAQ0rsQUI6QGwCi+wANaxCQvpsAQysgkACiuzQAkDCSuzQAkHCSuxCwErADAxMxEhFSERIRUhEZQCuf34AfH+CAYIof45m/z7AAAAAAEAWv/oBlAGIwAqAGUAsiYAACuxFwjpsgICACuxDAjptB0eJgINK7EdBOkBsCsvsCrWsRAQ6bAQELEbASuxIBPpshsgCiuzQBsdCSuxLAErsRsQERKyBwQkOTk5sCARsAY5ALEMHhESswYHECokFzkwMRIAJTYzIBMHJwIhIiMGBwYRFBUSAAUyMzI3Nj8BITchBwIHBiMiJyQnJhFaAcoBOxEQAZjvdxji/uIICf+xsAEBZQECBgWip8cIAv43AgKDAgTu0PsPD/655+sEQwHVCgH+r3AdAQYIvbz/AAIB/v7+jgdvgrk5l3D+zceuAQ3j6AFBAAAAAQBmAAAE0QYIAAsAPwCyAAAAK7AHM7IBAgArsAUztAMKAAENK7EDA+kBsAwvsADWsQsR6bACMrALELEIASuwBDKxBwvpsQ0BKwAwMTMRMxEhETMRIxEhEWapAxWtqfzvBgj9iQJ3+fgC7/0RAAAAAAEApwAAAVgGCAADACEAsgMAACuyAAIAKwGwBC+wA9axAhHpsQIR6bEFASsAMDETMxEjp7GwBgj5+AAAAAEANP9fAqIGCAATAC4AsgQAACuxEQjpsgAAACuyCQIAKwGwFC+wCNaxCxHpsRUBKwCxCQQRErABOTAxFzcXFjMyNzYREzMRFAcGIyIjJic0Wx9SPgwLnAOuUF2tBQVvdS+AGD0BFAEHBPD7E7p4igNUAAAAAQCNAAAEugYIAAsAMACyAAAAK7AHM7IBAgArsAQzAbAML7AA1rELEumwAjKxDQErALEBABESsQMJOTkwMTMRMxEBMwkBIwEHEY24ApHV/TAC39v9kCoGCP1mApr9LPzMArkX/V4AAAABAI0AAALZBggABQAsALIAAAArsQMD6bIBAgArAbAGL7AA1rEDEOmyAwAKK7NAAwUJK7EHASsAMDEzETMRIRWNrQGfBgj6maEAAAAAAQAx/9UG/AZeAAkAfwCyAAAAK7MEBQcJJBczsgECACuwAzMBsAovsADWsQkQ6bAJELEFASuxBBHpsQsBK7A2Gro+1PPRABUrCrAAELABwA6wCRCwCMC6wQ70bwAVKwoOsAUQsAbABbAEELADwAMAsQYILi4BswEDBgguLi4usEAaALEBABESsAI5MDEzCQMjAwkBAzEBOgImAjsBMLDA/gX+Gc0GXvr8BQT5ogQV+8AETPvfAAAAAAEAY/+vBcEGSQAHADgAsgAAACuwBTOyAwIAK7ABMwGwCC+wANaxBxHpsAcQsQIBK7EFEOmxCQErALEDABESsQIGOTkwMTMRAREzEQERYwSwrvtTBkn7BgS5+acE6/tmAAIAYP/dBqIGGgAWAC0ARACyEQAAK7EdA+myAwIAK7ErA+kBsC4vsBXWsRkL6bAZELEjASuxCwvpsS8BK7EjGRESsQ8FOTkAsSsdERKxCxU5OTAxExIAJTIzIBcWExQVEAcGISIjJCcmETQ3FBUQFxYFMjMgNzYRNDUCJyYhIiMGAGABAdUBRwECAUbt7gHs7v64AQL+t+vpqra5AQQBAgEEvLkDvLb+/ggJ//6ZAwEBRwHRAejp/rgEBP686+0B7esBRQMDBAT+/7q9Ab68AQAFBgEIubMH/pAAAAACAGIAAAOJBggADAAVAEoAsgAAACuyAgIAK7EVA+m0Cw0AAg0rsQsD6QGwFi+wANaxDAvpsA0ysAwQsRIBK7EGEOmxFwErsRIMERKwCjkAsRUNERKwBjkwMTMRITIXFhUUBwYHIxETMzI3NjUQISNiASPfiZyPgcapBKl9Tlr+uYcGCGJw3tF3bQH9XgNFQEqGARIAAwBZ/9oGpwYgAB0AOAA5AFcAshgAACuwETOxJAPpsjkAACuyAwIAK7EzA+kBsDovsBzWsR4Q6bAeELEtASuxCxDpsTsBK7EtHhESswUSGBAkFzkAsSQYERKwEzmwMxGyEAscOTk5MDETEgAlMjMgFxYTFBUUBwYPARcjJwcGByIjICcmETQzFhcWFxYzMj8BATMBNzY1ECcmISIjBAcGFRQBWQQB0gFGBAQBROvtBDMwTDHq24I0ytcBAv6+7eqrBJaS1D89oZUh/pTVAQose7u4/v8EBP78ubQEvwMDAUgB0wLk6P65AwOTioZQNPWNH3kC7usBQQbjsq4uDVsVAXz+6TeX3gEFvbsBvbn7B/zVAAAAAgBgAAAD2wYIABEAGgBdALIAAAArsA0zsgICACuxGgPptBASAAINK7EQBekBsBsvsADWsREL6bASMrARELEXASuxCBDpsRwBK7EXERESsQ8MOTmwCBGwDjkAsRIQERKxCww5ObAaEbAGOTAxMxEhMhcWFRQVBgcGBwEjAQcREzMyNzY1ECUjYAEl34OSBW9mrgHqz/4uMARvhmBp/smHBghgbdMICcNtYw79SgKpAf1YAzhIUIUBEQEAAAABAF7/4QQZBiYAMgBaALIvAAArsQQJ6bIVAgArsSAD6QGwMy+wEdaxJAvpsCQQsQoBK7ErEOmxNAErsSQRERKwATmwChGzDxcbJyQXObArErEaKDk5ALEgBBEStQABERobKyQXOTAxEzcXFhcyMzY3Nic0NSYvASQ1NDc2NzIzMh8BBycmIyIjBgcGFRQXBQQTFhUUBwYnIiYnXp0TU9ABAnxeXQEEnvP+2H58qwUF45MRhSVWhAcIZkhMqwECAQQRAYaSyan7KwF3LDPmAwFWVnYDArhIconypXNvA9UZWTJ1BUBBYJBOdnj++hAQsoiUAcOlAAAAAAEAXAAAA+sGCAAHADoAsgYAACuyAQIAK7EAA+mwAzIBsAgvsAbWsQUQ6bIFBgors0AFAwkrsgYFCiuzQAYACSuxCQErADAxEzUhFSERIxFcA4/+iasFZ6Gh+pkFZwAAAAEAXP/fBKoGCAAWADcAshIAACuxBgnpsgACACuwCjMBsBcvsBbWsQIL6bACELEJASuxDBLpsRgBK7EJAhESsBA5ADAxEzMRFBcWIDc2NREzERAHBiEiIyQnJhFcrEBZAcJWPLWJlP76Bwf+6It4Bgj8XdxumJtt4QOc/DH+/qWzBbieAQUAAAABADz/pwUXBggABQAhALIFAAArsgACACuwAzMBsAYvsQcBKwCxAAURErACOTAxEzMJATMBPLkBrwG2vf2NBgj7bgSS+Z8AAAABAEL/swjGBk0ACQAqALIHAAArsAkzsgACACuxAwUzMwGwCi+xCwErALEABxESsgIECDk5OTAxEzMJAzMJAkK8AYkB+AHsAZ69/aX+FP4IBgj7gQTE+zwEf/mvBMz7MAABADgAAASFBggACwAmALIAAAArsAgzsgICACuwBTMBsAwvsQ0BKwCxAgARErEECjk5MDEzCQEzCQEzCQEjCQE4Acr+bbwBOAE7wP5pAb7A/p7+oQMdAuv9ugJG/RX84wJ0/YwAAQA4AAAEzgYIAAgAMACyBwAAK7IAAgArsAMzAbAJL7AH1rEGEOmxCgErsQYHERKwAjkAsQAHERKwAjkwMRMzCQEzAREjETjBAYkBjMD+CK0GCP1SAq78mv1eAp8AAAAAAQBiAAAEvQYIAAcAHgCyAAAAK7EFA+myAwIAK7ECA+kBsAgvsQkBKwAwMTMBITUhASEVYgNC/SgD8fy2AzcFZ6H6maEAAAAAAQBk/moB7AakAAcANwCyBAIAK7EBCOmwAC+xBQTpAbAIL7AB1rQCDAALBCuwBzKxBQvptAMMAAsEK7AGMrEJASsAMDETESEVIxEzFWQBhNre/moIOpv4+pkAAAABAF//kASVBnAAAwAAEzcBB1+CA7SDBi5C+WFBAAAAAAEAVv5qAd8GpAAHAEAAsgMCACuxBAjpsAcvsQAE6QGwCC+wA9awADK0BQwACwQrsAUQsQEL6bABL7AFELQDDAALBCuwAy+xCQErADAxFzMRIzUhESFW4NwBhf53/QcGm/fGAAABAGIEpgLYBggABQAtALIBAgArtAUKAAwEK7ADMgGwBi+wANa0AgwABwQrsQcBKwCxAQURErAEOTAxEwkBBycHYgE7ATtvzM0E/wEJ/vNVqakAAAABABH+owWU/uwAAwAdALADL7QABwATBCu0AAcAEwQrAbAEL7EFASsAMDETIRUhEQWD+n3+7EkAAAEAZgR7Af0GBAADACAAsAMvtAEKAAsEKwGwBC+wANa0AgwACwQrsQUBKwAwMRM3EwdmpPNfBbZO/rQ9AAIAPv/jA/kD2wAUACoAVQCyDAAAK7IRAAArsRsE6bIJAQArsgQBACuxJwXpAbArL7AA1rEXDumwFxCxHwErsQgMMjKxCw3psSwBK7EfFxESsQQROTkAsScbERKyAA0IOTk5MDETNDc2MzIzFhc1MxEjNQYHIiMiJyY3FBUUFxYzMjc2NTQ1JicmIyIjBgcGPnh8xgkJzIGioIPQBAO8hIGlVVyMkVpSA1JVjwkJglZTAd7UkpcHm438OoOeApmX3ggHiWl0cGaOBQWXY2gFaWMAAAAAAgCK/+EEUwabABQAKgBeALIAAAArshEAACuxGwjpsgUBACuxJwTpsAEvAbArL7AA1rEDDumyExUXMjIysAMQsR8BK7ELC+mxLAErsQMAERKwKjmwHxGzBw8bJSQXOQCxJxsRErILEwM5OTkwMTMRMxE2NzIzMhcWFRQHBiMiIyYnFQMUFRQXFjMyNzY1NCcmJyYjIiMGBwaKpIHTCQnHfXuDhLwHB8uFClRbkpZcUgEHVVuOAQKLWlUGm/yhpwaXktjQmp0FnYMB7QUFkGZxdWiJCwuSYWcBbWYAAAEAWv/qA4sD4QAhAD0Ash4AACuxGATpsgQBACuxDQXpAbAiL7AA1rETC+mxIwErALEYHhESsBw5sA0RsgAJGzk5ObAEErAIOTAxEzQ3NjcyMzIXFSYnJiMGBwYVFBUeARcWMzI/ARUGIyInJlqKjOIJCJ2CL2hWPZBkYgGwhgsLr20giq3WkpIB5tKRkgZX20wwJwFsao8DA43PBwGGKd5plpMAAgAz/+ED4QabABQAKgBYALIMAAArshEAACuxFwPpsgQBACuxJQbpsAkvAbArL7AA1rEpDumwKRCxDAErsggdHzIyMrELDemxLAErsQwpERKzBBEXIyQXOQCxJRcRErIADQg5OTkwMRM0NzYzMjMWFxEzESM1BgciIyInJhcWMzIzNjc2NTQ1JicmIyIjBgcGFRQzdnzBBwjLgKGdgdIEA7mBffJUjgECm1dMA1JXjQMCi1dRAd7Tk5kFnANf+WWHowOblydsAXBhkAUGkWJpAW9mjpUAAAAAAgBe/+ED9QPfAB4AKABOALIbAAArsRAI6bIEAQArsSUI6bQfCxsEDSuxHwfpAbApL7AA1rEMC+mwHzKyDAAKK7NADAoJK7EqASsAsQsQERKxExQ5ObAfEbAAOTAxEzQ3NjMyMxYEHwEhFRQXFjMyPwEXBwYHBiMiIyYnJhMhLgEnIiMiBwZeeIDQBQa0AQMIBf0TX1tzqFoshiZAgGljBATSgn2xAjkOmGkHCGZQVgHf1JGbBPCvdyh1WVOEQlA+aTsvBZeRAT5pigVCRwAAAAEAbwAAAlwGsAAaAFcAshkAACuyDwIAK7EHA+myFAEAK7ABM7EXCOmwADIBsBsvsBnWsAIysRgL6bATMrIYGQors0AYFgkrsAoyshkYCiuzQBkACSuxHAErALEPFBESsAs5MDETNTMRNDc2MzIfARUmJyYjIgcGFxEzFSMRIxFvU2lFciwqIxk4DwwhFU0B7++oAymaAaXBUjUIBrAWBQEHG4P+XZ381wMpAAACADT95QPxA9sAJQA7AHIAsiMAACuxLAjpsgkBACuyBAEAK7E4BumwES+xGgTpshoRCiuzQBoVCSsBsDwvsADWsBUysSgL6bAWMrAoELEeASuxCDIyMrELC+mxPQErsSgAERKwFDmwHhG0BA8hLDYkFzkAsTgRERKyAAgfOTk5MDETNDc2MzIzFhc1MxEUBwYjIiMmJyYnMxYXFjMyMyQRNQYjIiMmAjcUFRQXFjMyMzY3NjU0JyYjIiMGBwY0iILBCAi9gKV4f+cDA9B+dAukBVdUcwUGATaJvgYGyvenUFmLAwSXV05TWZQCAYNZVQHg2JSPB5iK/AfUhY8BgXjCglNPCAFKsZ8FASHmBwaMZ3EDa2OWk2ZsAWpkAAEAZgAAA2oGmwAVAEcAsgAAACuwDDOyBwEAK7ESBumwAS8BsBYvsADWsRUO6bACMrAVELENASuxDBDpsRcBK7ENFRESsQcQOTkAsQcSERKwAzkwMTMRMxE2NzYzMhcWGQEjETQjIiMGExFmpCw+TWdpUImswAID8AEGm/y2QCMrM1n+1P3ZAmXlA/7Q/ekAAAIAnAAAAYgFpQAOABIAQQCyDwAAK7IQAQArsAsvsQQK6QGwEy+wANaxBwzpsQcM6bMSBwAIK7EPC+mwDy+xEgvpsRQBK7ESDxESsAQ5ADAxEzQ3NjMyFhUUBwYHIicmExEzEZwmITAwRSMhMDAiJiWpBS04Ih49ODcgGwEdH/sHA8b8OgAAAAACAJ794gGJBZkAEAAUAD4AshIBACuwDS+xBArpAbAVL7AA1rEJDOmxCQzpsxQJAAgrsREL6bARL7EUC+mxFgErsRQRERKxBA05OQAwMRM0NzYzMjMyFhUUBwYjIicmExEzEZ4lIC4BAjBFIyEwMCImJakFIjgiHT04NyAbHB/49QXk+hwAAAAAAQCUAAADowabAAsAMACyAAAAK7AHM7IEAQArsAEvAbAML7AA1rEDDemwCjKxDQErALEEABESsQMJOTkwMTMRMxEBMwkBIwEHEZSdAVHT/ncB19T+jSUGm/u1AXb+Vv3kAasj/ngAAAABALIAAAFaBpsAAwAfALIAAAArsAEvAbAEL7AA1rEDC+mxAwvpsQUBKwAwMTMRMxGyqAab+WUAAAEAZAAABVgD5AAoAGwAsgAAACuxFB8zM7IBAQArsgUBACuwDzOxIwTpsBoyAbApL7AA1rEoC+mwAjKwKBCxIAErsR8L6bAfELEVASuxFAvpsSoBK7EgKBESsAc5sB8RsA05sBUSsA85ALEjABESsA05sAERsAM5MDEzETMVNjMyMzIXFhcWFzYzMhcWFREjETQnJiMiIwYZASMRECMiBwYVEWSoVpQBAUhMHiskCFy/dVtsqSApaAUFwaiucTcqA8ZxjCQOLycQm0lW6v2lAjqCPk4H/tH97gI2ARNsU3f97QAAAAEAYwAAA2QD4wAZAEcAsgAAACuwDjOyAQEAK7IJAQArsRQI6QGwGi+wANaxGQvpsAIysBkQsQ8BK7EOC+mxGwErsQ8ZERKwCTkAsQEUERKwAzkwMTMRMxU2NzY3NjMyFxYVESMRECciIyIHBhURY6kUFTAkTk56WWyowgUFbD07A8Z1GRYxECJOYOH9rAJCAQAHVVGE/eEAAgBk/9kEWQPWABAAJABGALIOAAArsRME6bIEAQArsR8I6QGwJS+wANaxIwvpsCMQsRkBK7EKC+mxJgErsRkjERKxBA45OQCxHxMRErIKACM5OTkwMRM0NzYzMjMWFxYVFAcGIyIABRY3MjM2NzY3NDU2JyYnIgcGFRRkmpnNAQLPkpGJj+TQ/tcBDmKLBASKYF4EAVxglZNiYgHS05mYAZaVzdqSmAEnKmcBAWhljgYGjmhsAWtqkZQAAAACAIb94ARHA+AAFgAsAFgAshEAACuxHQTpsgEBACuyBwEAK7EpCOmwAC8BsC0vsADWsRYL6bICFxkyMjKwFhCxIwErsQ0L6bEuASuxIxYRErMHER0pJBc5ALEpHRESsg0VAzk5OTAxExEzFTY3NjMyFxYXFBUUBwYjIicmJxEDFBUUFxYzMjM2NzY1NCcmJyYjIgcGhqhzrRoZwoJ9BXSA1R0eoHUIWVyNBgaLV0oBB0xWko5aUv3gBeaQlBMDl5HOCAnLjp8CEYj9ZgQIBgaPaW4EdmWEDQ2PXGRoYgAAAAIAW/3gBBwD3wAYAC4AVwCyEwAAK7EhBOmyCQEAK7IEAQArsSsI6bALLwGwLy+wF9axGwvpsBsQsQwBK7IIJScyMjKxCwvpsTABK7EMGxESsgQTKzk5OQCxKyERErINFwg5OTkwMRM2NzYzMhcWFzUzEScRBgcGBwYjIicmNTQ3BhUUFxYXMjMyNzY1NDUmJyYjIgcGWwV9gsMZGa5yqKguSlJKHRzXgXSoAUtWjQUFjF9XBFJajpJWTAHqzpCXAxSSkPoaAgKaNyswCAOfkMsJEg0NhGV2A25pjgcGkWFoZlsAAAEAhQAAApQD4wATADkAsgAAACuyAQEAK7IHAQArsQwG6QGwFC+wANaxEwvpsAIysRUBKwCxDAARErAKObABEbEDCTk5MDEzETMVNjc2MzIXByYjIgcGBwYVEYWoTWUREVNATzgvCgxOKiMDxmJzCgIumDECDllMaf3QAAABAGf/4QMDA98AOwBdALI4AAArsQcE6bIaAQArsSUI6QGwPC+wFtaxKQ3psCkQsQsBK7EzEOmxPQErsSkWERKxEQE5ObALEbQHEB8uOCQXObAzErEeLzk5ALElBxEStQABFh4fMyQXOTAxPwEWFxYXFjMyNzY3NDU0LwEmJyYnJjU0NzY3MhcWFwcmJyYnJiMiBwYHFBcWHwEWFx4BFRQGBwYjIicmZ5AFJDA0Hx89KCwDf3QmGEAZJ0NPiIBMKhKJAxgfHBIZLyEoAQcMWWxVIzc4ZlRETo1kItY/Ei9BEQokKEcDA181NREPJiU8SGRRYAFXLy1ICR8mCwgaHjMUFCsiMCYYJnVBWp0lHlseAAEAYAAAAhgFHgALAE4AsgoAACuyAQEAK7AFM7EABumwBzKyAQAKK7NAAQMJKwGwDC+wCtawAjKxCQvpsAQysgkKCiuzQAkHCSuyCgkKK7NACgAJK7ENASsAMDETNTMRMxEzFSMRIxFgY6qrq6oDMpQBWP6olPzOAzIAAAABAGL/4gNaA8YAFQA3ALITAAArsQYE6bIAAQArsAszAbAWL7AV1rECDumwAhCxCgErsQ0O6bEXASuxCgIRErATOQAwMRMzERQXFjMyNzY1ETMRFAcGByIjIBFipScvhoUvH6REWdoEBP6IA8b9+axEVWVEnAIH/ezWbIsDAcMAAAEAPP+0A+wDxgAFACEAsgUAACuyAAEAK7ADMwGwBi+xBwErALEABRESsAI5MDETMwkBMwE8vQEcAR26/icDxv12Aor77gAAAAEAQf+3BjwEBQAJACoAsgkAACuwBzOyAAEAK7EDBTMzAbAKL7ELASsAsQAJERKyAgQIOTk5MDETMwkDMwkCQbYBDgEzASQBJrr+Hf7Z/tUDxv10Asv9MwKO+/ICwv09AAEAPgAABDADxgALACYAsgAAACuwCDOyAgEAK7AFMwGwDC+xDQErALECABESsQQKOTkwMTMJATMbATMJASMJAT4Bl/6lyfn40f6hAYrJ/tX+zgH+Acj+tQFL/jf+AwGD/n0AAAABADz95QQ+A8YABwAUALIAAQArsAMzAbAIL7EJASsAMDETMwkBMwEjATy/AVABObr9OrsBMgPG/VECr/ofAnYAAQBaAAAEXgPGAAcAHgCyAAAAK7EFBOmyAwEAK7ECCOkBsAgvsQkBKwAwMTcBITUhASEHWgK4/csDgf1HApYBAQMpnPzRlwAAAQBk/moCPQakACwAUACyDQIAK7EKCOmyBwIAK7AkL7EhCOkBsC0vsCjWsAUysR0Q6bARMrIdKAors0AdIwkrsAsysS4BK7EdKBESsBY5ALENIRESsgYdKDk5OTAxEzU2NzY1ETQ3NjsBFSMiBwYVERQHBgcWFxYXFhcRFBcWNzMVIyInJjURNCcmZE0qH2AqX1pNJw4UOB9EByMqEDUBEw4oTVpfKmAgKAJFihdFM14BnMtaJ5wPFTv+QHNzQDsDKDAibXn+PzsVEAGbJlnLAZ9ZN0YAAAAAAQBa/eIA7gYJAAMAHACyAQIAKwGwBC+wANaxAxTpsQMU6bEFASsAMDETETMRWpT94ggn99kAAAABAGT+agI+BqQAKwBLALIAAgArsQEI6bAWL7EXCOkBsCwvsBvWsCYysREQ6bAGMrIbEQors0AbFgkrsAAysS0BK7ERGxESsCI5ALEAFxESsgYRJzk5OTAxEzUzMhcWFREUFxYXBwYHBgcRFAcGKwE1MzI2NRE0NzY3NjcmJyY1ETQnJgdkW14rYB4rTQFOJx8BXypeW0woITcRKiMGRR83Ew4pBgicJ1nM/mRgMUUXihtHOFj+Yc9VJpsjPAHBdnAiMCgDOUJwdgHAOxUQAQAAAQBKBQwC/wYIABkANgCyDQIAK7ADM7ARL7AZM7QIBwAgBCsBsBovsRsBKwCxCBERErEAFDk5sA0RswYFDhUkFzkwMRM3Njc2HwEWNzI3Nj8BFwcGJyIvASYHBg8BShVBYkI8dSwXDg0ZJBBfDUZuQENeMSEuIw4FNzGaBgQnTR0BBxBRIyshsAEuQiIGCF0iAAAAAAIAV/3iAUIEEgALAA8AMgCwCS+xAwrpAbAQL7AA1rEGDOmxBgzpswwGAAgrsQ8U6bERASuxDwwRErEJAzk5ADAxEzQ2MzIWFRQGIyImExEzEVdEMTBGRDIxRCySA5wxRUYwMURE+ncEwPtAAAIAXgBZA48GCAAdACQAcgCyBAIAK7AbL7AYM7EhCOmwDzKyGyEKK7NAGxoJK7AJL7AiL7AOM7EDBukBsCUvsADWsR4L6bAeELEaASuxAyEyMrEZFOmxBQ4yMrEmASsAsSEbERKwFDmwCRGxEx45ObEDIhESsQcIOTmwBBGwBjkwMRMmEjc1MxUWFxUnJicmJxE+AT8BFQYHBgcVIzUmAjcUFhcRDgFeA/G+kmmAESNGBGs3bSolPDtBO5K78KiQc3iLAy/CAR0d3dgJTdgWLigDKf1ACT0zLtwsGBkI5+cdARTHgMQaAr4kvgAAAQBr/+IEvQYlAE0AtgCyRAAAK7BNM7E6CemyFAIAK7EgCem0CwxEFA0rsCwzsQsF6bAuMgGwTi+wEdaxJhLpshEmCiuzQBELCSuwJhCxCAErsTMR6bIzCAors0AzLgkrsDMQsRwBK7EaDemxTwErsSYRERKzBg8NTSQXObAIEbBLObAzErQoLDdISSQXObAcEbMUIDpEJBc5sBoSsD85ALE6RBESsQBLOTmwCxGzBjE/QCQXObEgDBESshAbHDk5OTAxNzY3Njc2NzYnNCchNTMmJyY3NgAzMhcWFxYVFwcSJyYjIgcGBwYVFBc0FzAXIRUjFBcUFRYVFAcXFjMyNjc2NxcGBwYjIicmJzImIyIHawcqOS5KUDkBCf77ziwtVgQHAQyxGxvPbUUQrgPKISNiT1UQBXAlIAEf7wEESJF0KCZOKiICiyNXXF9EbDMzCZw4dEBADjBBGioHZloZOYxTU7hstAD/BBqeYjl/KgEYOwk7Pl4bHqWpCkc+igMFBQMjHWt2NCojOy4OVV9GTSUSEjN8AAAAAQA4AAAEzgYIABgAdACyDwAAK7IAAgArsAMztBESDwANK7AKM7ERBumwDDK0FhcPAA0rsAUzsRYE6bAHMgGwGS+wD9awEzKxDhDpsAkysg4PCiuzQA4HCSuwCzKyDw4KK7NADxYJK7ARMrEaASuxDg8RErACOQCxABcRErACOTAxEzMJATMBIRUhBxUhFSERIxEhNSE1JyE1ITjBAYkBjMD+agEy/ncLAZT+bK3+dQGLDf6CASgGCP1SAq79RJcTk5X+hgF6lZAWlwAAAgBT/nIDawYdADMAPwBaALIIAgArsRED6bIRCAors0ARDgkrsCAvsSkD6bIpIAors0ApJQkrAbBAL7A61rAOMrEYEOmwDTKxQQErsRg6ERK0CwwZGx0kFzkAsREpERKzAho3PSQXOTAxEzY3JicmNzY3NhcWHwEjJyYjIgcGHwEEAwYHFhcWBwYnJicmPQEzFxY3Njc2JyYnJi8BJDcGFjMyNjU0JiMiBlMJxqQIB3VvoKRnXwsDpAgkoccSEc9fATYKCcypCAd+c6inYVOhBBDDvQ8HNCZBHRx9/vmzBYJkXIODXFqCAj/9YF6woWtkAwNvZqEjLMy4p0QfZv706F9gyqxkXAQEdWR+NzLFBwW5T0EsIAwMLHn0Yo+HXlyFewAAAAIAawUfAt0GAAAOABwAQwCwDC+wGTOxBArpsBMysQQK6QGwHS+wANaxCAzpsAgQsQ8BK7EWDOmxHgErsQgAERKxBAw5OQCxBAwRErEPFjk5MDETNjc2MzIXFgcGBwYjIiYlNDc2FzIWFRQGIyInJmsBIx4sLyEjAQEkHiwvRAGUIx8uLkJCLi4fIwWUNB4aHCE3NB4aPjE1IB0BOzU1PB0eAAAAAwBkAAUGTgXvAA8AHwA+AJIAsAwvtBQHABMEK7AvL7QkBwATBCuyLyQKK7NALyoJK7AcL7QEBwATBCsBsD8vsADWtBAUABIEK7AQELEgASu0MxQAEgQrsDMQsSoBK7QpFAASBCuwKRCxGAErtAgUABIEK7FAASuxKjMRErQMFBwENSQXObApEbA4ObAYErA5OQCxLxQRErUIEAAYPjwkFzkwMRMQNzYhIBcWERAHBiEgJyYTEBcWISA3NhEQJyYhIAcGASY3NhcWFxYfASMnJicmBwYHBhUQITI2NzMGBwYnJGS90wFlAWbRvr7R/pr+m9O9Uqi8AT8BP7upqbv+wf7BvKgBFwFkcL6HX2gTBVoGFU5IW6FTRQE9X58VWRtrb5b+jQL6ATXT7e3U/sz+zdXt7dQBNP7tvtPTvgETARS+09O+/ubGipgFBD9FeCMbVzMuAwR7aab+c5BolllcCBcAAAADAGwCFwNGBhQAAwAUACMAcwCyCAIAK7QhBwATBCuwAC+0AQcAIAQrsBEvtBkHACAEKwGwJC+wBNawADK0FRQAIQQrshUECiuzQBUDCSuwFRCxCgErsQ4dMjK0DBQAIQQrsSUBKwCxGRERErEODTk5sCERsgQPCjk5ObAIErELDDk5MDETNSEVATQ3NhcWFzUzESM1BgcGJyY3BhcWFzI3NicmJyYHDgFsAtr9Jllgn5pgeXhinJFkYHoEREZpbEM/AQE+RHVifgIXcXECfp9tdgMEdWX9L2J0AwF0cKZtU1cBVU9vc0lTBQSYAAIAWwCYBFUEHwAGAA0AABM1ARUNARUTNQEVDQEVWwHE/sEBOHkBxP7AATgB894BTtvl3+gBW94BTtvl3+gAAAABAFwAAAZnApMABQAwALIEAAArsAAvtAEHACAEKwGwBi+wBNa0AxQATwQrsgQDCiuzQAQACSuxBwErADAxEzUhESMRXAYLfwIcd/1tAhwAAAEAYAG7AfwCVAADAAATNSEVYAGcAbuZmQAAAAAEAFcACgZABfUADwAfAD4ARQC8ALAML7QUBwATBCuwPS+0QAcAEwQrsj1ACiuzQD0gCSuwMzKwPy+0IQcAEwQrsBwvtAQHABMEKwGwRi+wANa0EBQAEgQrsBAQsSABK7Q+FAASBCuwPzKwPhCxNgErtC8UABIEK7QxFAASBCuwLxCxGAErtAgUABIEK7FHASuxNj4RErQMFBwEJiQXObAvEbMpKEJDJBc5sRgxERKyJCUzOTk5ALE9FBESsSk3OTmwQBG1CBAYACgmJBc5MDETEDc2ISAXFhEQBwYhICcmExAXFiEgNzYRECcmISAHBgERISAXFgcWFxYXFhUUBhUUFxQXIyI1JicmJyYHIxkBEzMyJyYjV73TAWUBZdK9vdL+m/6b071RqbwBPwE/u6mpu/7B/sG8qQFyAUwBPhAKoTECFw8bAhIVXh4BAw08JGbyAf7yBQXjAv8BNdTt7dT+y/7N1e3t1AE0/u2+09O+ARMBFL7T0779GwOO7ptGHQERHDVFEEESPjYBIlWWFGchFAH+ZgNB/qetrAAAAgBfBEcCEwX8AA8AHgBOALAML7QUBwATBCuwGy+0BAcAEwQrAbAfL7AA1rQQFAASBCuwEBCxGAErtAgUABIEK7EgASuxGBARErEMBDk5ALEbFBESsgAQGDk5OTAxEzQ3NjMyFxYHFAcGIyInJjcUFxYXFjc2NTQmJyYHBl83PWdnPDcBNjxnZz03TCAmQUUoJkZBRSokBSJZPEVFPVhZPkREPlU3JysDAy4oOzhSAwMuKAAAAgBeAAADogRTAAMADwBkALIAAAArsQEH6bAEL7ALM7EFB+mwCTKyBAUKK7NABA4JK7IFBAors0AFBwkrAbAQL7AO1rAGMrQNFABBBCuwCDKyDQ4KK7NADQMJK7AKMrIODQors0AOAAkrsAQysREBKwAwMTM1IRUBNSERMxEhFSERIxFeA0T8vAFjfQFk/px9fX0Ccn0BZP6cff6eAWIAAAEAWQR/AfAGCAADACIAsgECACu0AwoACwQrAbAEL7AA1rQCDAALBCuxBQErADAxGwEXAVn0o/7JBLwBTE7+xQABAGL+wQQtBggAEgBQALIEAgArsQsH6bAGMrILBAors0ALCQkrsAwyAbATL7AN1rEMFOmwDBC0AAwACAQrsAAvsAwQsQkBK7EIFOmyCAkKK7NACAUJK7EUASsAMDETNjc2MyEVIwMjESMRIxEnJicmYgF4ivUB03MBi5+KMppjdQQYyYififlCBr35QwN7BhVrgAAAAwBeAhcDVgYVAAwAEAAbAGwAsgMCACu0GgcAIAQrsA0vtA4HACAEK7AKL7QUBwAgBCsBsBwvsADWtBEUAEEEK7ARELEXASu0BhQAQQQrsR0BK7ERABESsQ0OOTmwFxGyAwoZOTk5sAYSsQ8QOTkAsRoUERKyBhEAOTk5MDETNDYzMhYVFAcGIyImEzUhFQEUFjc+ATc2JiIGXueam9xnbKqc3w4C2v2WmGtojwMDkN6SBJKe5eCcoW5x3f4hcXECe2yeAQGaa3CioAAAAgBmAJgEYAQfAAYADQAAEzUBFQE1JTc1ARUBNSVmAcP+RAE69QHE/kIBOgNE2/6y3v6l6N/l2/6y3v6l6N8AAAIAVv3gAzAEFwApADUAewCyAAAAK7AlL7EFCOmwGi+xEAPpshoQCiuzQBoVCSuwMy+xLQrpAbA2L7AN1rEdC+mwACDWEbEBDumwHRCxKgErsTAM6bAwELEIASuxIg7psTcBK7EdDRESsAM5sTAqERKyCgsfOTk5sAgRsRYgOTkAsQAFERKwIjkwMRczBwYWFxY2NzYvASQnJjY3NhcWHQEjJy4BBw4BFRYfARYRFAYnJicmNRM0NjMyFhUUBiMiJlajCAd8U1Z7AQOJk/78BgPFiH1lbJ8HCl8/Ql8Bg7bl1p2RaW3mRDIwRUQxMUVaZU91AQF6VpwuMVffhsICAVZafjIyQFABAWVCcSw/Tv74mNgCAVlchwSFMEVFMDFFRAAAAAADACoAAAWmCAgABgAJAA0ALACyAAAAK7ACM7IBAgArtAUHAAENK7EFA+kBsA4vsQ8BKwCxAQcRErAJOTAxMwkBIwMhAxMhCwE3EwcqAswCsLyx/WC5/gIS/5uk818GSfm3Abf+SQJYAmwC9k7+tD0AAwAqAAAFpggMAAYACQANACwAsgAAACuwAjOyAQIAK7QFBwABDSuxBQPpAbAOL7EPASsAsQEHERKwCTkwMTMJASMDIQMTIQsBExcBKgLMArC8sf1guf4CEv+49aL+yQZJ+bcBt/5JAlgCbAH8AUxO/sUAAAADACoAAAWmB9EABgAMAA8ALACyAAAAK7ACM7IBAgArtAUNAAENK7EFA+kBsBAvsREBKwCxAQ0RErAPOTAxMwkBIwMhAxMJAQcnBwMhAyoCzAKwvLH9YLntATsBO2/MzF4CEv8GSfm3Abf+SQbIAQn+81WpqfvpAmwAAAAAAwAqAAAFpgd5AAYAIAAjAEMAsgAAACuwAjOyAQIAK7QFIQABDSuxBQPpsBgvsCAztA8HACAEKwGwJC+xJQErALEBIRESsCM5sQ8YERKxBxs5OTAxMwkBIwMhAxM3Njc2HwEWMzI3Nj8BFwcGIyIvASYHBg8BAyEDKgLMArC8sf1gub8VQWJCPHUsFw4NGSQQXw1GbkBDXjEhLiMOIwIS/wZJ+bcBt/5JBqcxmgcEKEwdCBBQIysgsC9BIgUIXiL71wJsAAQALwAFBawHYAAGABUAGAAnAGUAsgECACuwBS+xFgPpsBMvsCQzsQsK6bAdMgGwKC+wB9axDwzpsA8QsRkBK7EhDOmxKQErsQ8HERKyCxMWOTk5sBkRsQEYOTmwIRKxBBc5OQCxARYRErAYObELExESsRkhOTkwMTcJASMDIQMTNjc2MzIXFgcGBwYjIiYTIQMTNDc2MzIXFhUUBiMiJyYvAswCsb2x/WC58AEjHiwvISMBASQeLC9EEAIS/3EjHy4uISFCLi4fIwUGSfm3Abf+SQbvNB4aHSA3NB4aPvufAmwCJjUfHR0eNTU8HB4AAAQAKgAABaYIMwAGAAkAGQApAG8AsgAAACuwAjOyAQIAK7QFBwABDSuxBQPpsBYvtB4HABMEK7AmL7QOBwATBCsBsCovsArWtBoUABIEK7AaELEiASu0EhQAEgQrsSsBK7EiGhESswkBFg4kFzkAsQEHERKwCTmxJh4RErESIjk5MDEzCQEjAyEDEyELATQ3NjMyFxYVFAcGIyInJjcUFxYXFjc2NTQnJicmBwYqAswCsLyx/WC5/gIS/8I2PWdnPDc3PGdnPTdMISZARSkmJCNBRSkkBkn5twG3/kkCWAJsApRZPUVFPVlZPkREPlU3JysCAy4oOzgpKAMDLigAAAACAGb//QiBBggADwASAF8AsgsAACuwADOxCgPpsgECACuxBAPptA4QCwENK7EOA+m0BQgLAQ0rsQUI6QGwEy+wEdawDDKxCBLpsAQysggRCiuzQAgHCSuxFAErsQgRERKwATkAsQQFERKwEjkwMTMBIRUhESEVIREhFQURIQkBIRFmBSUC9v2UAlj9oAJw/OP9YP6TAfICEgYIn/43m/2coQMBuv5JAlgCbAAAAgBi/k0FTQYiABYAGgBCALIYAAArshQAACuxDwPpsgMCACuxCQjpAbAbL7AA1rEMEOmxHAErALEPFBESsRASOTmwCRGxBxE5ObADErAGOTAxEwIAJTYWFxUmBQQAExYAFwQ3FQYnIAABExcDYgEBwgFEk9t4wf7j/v7+ngQEAV/9ARjC0vz+vf4sAh7DmOoC/gFEAdcJBEJV19MGB/6A/vz//pMICcrRkgEB0/zRAS8//tQAAgCUAAADsQgIAAsADwBHALIAAAArsQkD6bIBAgArsQQD6bQFCAABDSuxBQjpAbAQL7AA1rEJC+mwBDKyCQAKK7NACQsJK7ACMrNACQcJK7ERASsAMDEzESEVIREhFSERIRUBNxMHlAMd/ZQCWf2gAm/96aT0XwYIof45m/2coQe6Tv60PQAAAAIAeQAAA5YIBQALAA8ARwCyAAAAK7EJA+myAQIAK7EEA+m0BQgAAQ0rsQUI6QGwEC+wANaxCQvpsAQysgkACiuzQAkLCSuwAjKzQAkHCSuxEQErADAxMxEhFSERIRUhESEVARMXAXkDHf2UAlj9oAJw/eT0o/7JBgih/jmb/ZyhBrkBTE7+xQACAJQAAAOxB90ACwARAE8AsgAAACuxCQPpsgECACuxBAPptAUIAAENK7EFCOkBsBIvsADWsQkL6bAEMrIJAAors0AJCwkrsAIys0AJBwkrsRMBK7EJABESsAw5ADAxMxEhFSERIRUhESEVCQIHJweUAx39lAJZ/aACb/1ZATsBO2/MzQYIof45m/2coQbTAQr+8lSpqQAAAAMAlAAAA7EHYAALABoAKQBzALIAAAArsQkD6bIBAgArsQQD6bQFCAABDSuxBQjpsBgvsCYzsRAK6bAfMgGwKi+wANaxCQvpsAQysgkACiuzQAkLCSuwAjKzQAkHCSuzDAkACCuxFAzpsAkQsRsBK7EjDOmxKwErsRQJERKxEBg5OQAwMTMRIRUhESEVIREhFQE2NzYzMhcWBwYHBiMiJiU0NzYzMhcWFRQGIyInJpQDHf2UAln9oAJv/WIBIx4sLyEjAQEkHiwvRAGUIx8uLiEhQi4uHyMGCKH+OZv9nKEG9DQeGh0gNzQeGj4xNR8dHR41NTwcHgAAAgBOAAAB5ggIAAMABwAlALIHAAArsgQCACsBsAgvsAfWsQYR6bEJASuxBgcRErABOQAwMRM3Ew8BMxEjTqT0X+CxsAe6Tv60PXf5+AAAAAACAE0AAAHkCAwAAwAHACcAsgcAACuyBAIAKwGwCC+wB9axBhHpsQkBK7EGBxESsQMBOTkAMDEbARcBBzMRI031ov7JBrGwBsABTE7+xXv5+AAAAAAC/8kAAAI/B90ABQAJACcAsgkAACuyBgIAKwGwCi+wCdaxCBHpsQsBK7EICRESsQQBOTkAMDEDCQEHJwcXMxEjNwE7ATtuzcxvsbAG0wEK/vJUqalz+fgAA//SAAACRQdgAA4AEgAiAFoAshIAACuyDwIAK7AML7AfM7EECumwFzIBsCMvsBLWsRER6bMIERIIK7EADOmwAC+xCAzpsBEQsRMBK7EbDOmxJAErsRIAERKxBAw5OQCxBAwRErETGzk5MDEDNjc2MzIXFgcGBwYjIiYXMxEjEzQ3NjMyFxYVFAcGIyInJi4BJB4sLyEiAQEjHiwvRNaxsL0jHy4uICIiIS0uHyMG9DQeGh0gNzQeGj62+fgG7zUfHR0eNTUeHhweAAAAAgBI/68FpQeCAAcAIQBZALIAAAArsAUzsgMCACuwATOwGS+wITO0EAcAIAQrAbAiL7AA1rEHEemwBxCxAgErsQUQ6bEjASuxAgcRErEIFjk5ALEDABESsQIGOTmxEBkRErEIHDk5MDEzEQERMxEBERM3Njc2HwEWMzI3Nj8BFwcGIyIvASYHBg8BSASwrftUxBVBYkI8dSwXDg0ZJBBfDUZuQENeMSEuIw4GSfsGBLn5pwTr+2YGsDKaBgQnTR0IEFAjKyCwL0EiBQheIgAAAAMAYP/dBqIICAAOAB0AIQA6ALIMAAArsRID6bIEAgArsRoD6QGwIi+wANaxDwvpsA8QsRYBK7EIC+mxIwErsRYPERKxHiA5OQAwMRMSNzYlJBcWExIHBiUkABMCAAUENzYDAicmBQYHBgE3EwdgAevqAUcBSO7uAQPv7v61/rf+KqwDAXIBBAEGvb0EBLy7/vP/s7QByKTzXwMBAUfp6AEB6Or+uP637u4BAQHaAUn++v6GAQG/wAEHAQi6uQcHuLgDuE7+tD0AAwBg/90GoggMAA4AHQAhADoAsgwAACuxEgPpsgQCACuxGgPpAbAiL7AA1rEPC+mwDxCxFgErsQgL6bEjASuxFg8RErEeIDk5ADAxExI3NiUkFxYTEgcGJSQAEwIABQQ3NgMCJyYFBgcGARMXAWAB6+oBRwFI7u4BA+/u/rX+t/4qrAMBcgEEAQa9vQQEvLv+8/+ztAHS9aP+yAMBAUfp6AEB6Or+uP637u4BAQHaAUn++v6GAQG/wAEHAQi6uQcHuLgCvgFMTv7FAAAAAwBg/90GogfdAA4AHQAjADoAsgwAACuxEgPpsgQCACuxGgPpAbAkL7AA1rEPC+mwDxCxFgErsQgL6bElASuxFg8RErEeIDk5ADAxExI3NiUkFxYTEgcGJSQAEwIABQQ3NgMCJyYFBgcGCQIHJwdgAevqAUcBSO7uAQPv7v61/rf+KqwDAXIBBAEGvb0EBLy7/vP/s7QBQwE7ATtvzM0DAQFH6egBAejq/rj+t+7uAQEB2gFJ/vr+hgEBv8ABBwEIurkHB7i4AtEBCv7yVKmpAAMAUf/dBpIHgAAOABwANgBRALILAAArsRID6bIEAgArsRoD6bAuL7A2M7QlBwAgBCsBsDcvsADWsQ8L6bAPELEWASuxBwvpsTgBK7EWDxESsR0rOTkAsSUuERKxHTE5OTAxExI3NiQXFhMSBwYlJCcmEwIABQQ3NgMCJyYFBgABNzY3Nh8BFjcyNzY/ARcHBiMiLwEmBwYPAVEB6uoCkO3uAQPv7v62/rfs66wDAXIBBQEGvL0EA7y7/vP//pgBLBVBYUI9dSwWDg4ZJA9fDUZtQEReMSEuIg8DAQFH6egC6Or+uP637u4BAe3tAUn++v6GAQG/wAEHAQi6uQcH/pACrTGaBgQnTR0BCBBQIysgsC9BIgYIXSIAAAAABABV/90GlgdiAA4AHAAsADsAZQCyDAAAK7ESA+myBAIAK7EaA+mwKS+wODOxIQrpsDEyAbA8L7AA1rEPC+mwDxCxHQErsSUM6bAlELEtASuxNQzpsDUQsRYBK7EIC+mxPQErsSUdERKxISk5OQCxISkRErA1OTAxExI3NiUkFxYTEgcGJSQAEwIABQQ3NgMCJyYFBgABNjc2MzIXFgcGBwYjIicmJTQ3NhcyFxYVFAYjIicmVQHq6gFHAUju7gED7+7+tf63/iqsAwFyAQQBBr29BAS8u/70//6YAUwBJB4sLyAjAQEjHi0vISIBkyQfLS4hIUIuLh4jAwEBR+noAQHo6v64/rfu7gEBAdoBSf76/oYBAb/AAQcBCLq5Bwf+kAL0NB4aHCA3NB4aHh8xNSAdAR0eNTU8HR4AAAMAYP+HBqIGYQAWAB4AJgBtALINAAArsSED6bIRAAArsgMCACuxHAPpsgcCACsBsCcvsADWsRcL6bAXELEkASuxCgvpsSgBK7EkFxESQAkGBQ8RCBMZGh8kFzmwChGwBzkAsSENERKwDzmwHBG0EwgaGSYkFzmwAxKwBTkwMRMSACU2FzcXBwQTEgAlJicHJzY3JicmNwIXASYHBgABFhcEAAMCJ2ABAdUBR+jDiFaRASoDAf4l/rXcvYxNRkWRUVWrA+4C15vA//6ZATeUpwEGAXkDBNwDAQFHAdEBAXvBKs7u/ob+t/4kAQFzyjdkZHGkqb3+z8IEDWIEBf6P/NRWAQEBfwEHASK9AAACAFz/3wSqCAgAFAAYADkAshAAACuxBgnpsgACACuwCjMBsBkvsBTWsQIL6bACELEJASuxDBLpsRoBK7EJAhESsRUXOTkAMDETMxEUFxYgNzY1ETMREAcGJSQnJhEBNxMHXKxAWQHCVjy1iZj+8P7oi3gBW6T0XwYI/F3cbpibbeEDnPwx/v6luAUFuJ4BBQV7Tv60PQAAAAIAXP/fBKoIDAAUABgAOQCyEAAAK7EGCemyAAIAK7AKMwGwGS+wFNaxAgvpsAIQsQkBK7EMEumxGgErsQkCERKxFRc5OQAwMRMzERQXFiA3NjURMxEQBwYlJCcmEQETFwFcrEBZAcJWPLWJmP7w/uiLeAFm9aL+yQYI/F3cbpibbeEDnPwx/v6luAUFuJ4BBQSBAUxO/sUAAgBc/98EqgfdABQAGgA5ALIQAAArsQYJ6bIAAgArsAozAbAbL7AU1rECC+mwAhCxCQErsQwS6bEcASuxCQIRErEVFzk5ADAxEzMRFBcWIDc2NREzERAHBiUkJyYREwkBBycHXKxAWQHCVjy1iZj+8P7oi3j6ATsBO2/MzQYI/F3cbpibbeEDnPwx/v6luAUFuJ4BBQSUAQr+8lSpqQAAAAMARf/fBJIHXQAVACUANABqALIRAAArsQYJ6bIAAgArsAszsCIvsDEzsRoK6bAqMgGwNS+wFdaxAhDpsAIQsRYBK7EeDOmwHhCxJgErsS4M6bAuELEKASuxDRLpsTYBK7EeFhESsRoiOTmwJhGwBjkAsRoiERKwLjkwMRMzERQXFjMyNzYnETMREAcGJSQnJhEBNjc2MzIXFgcGBwYjIicmJTQ3NhcyFxYVFAYjIicmRaxAWeDhVzwBtYiY/u/+6It4AQ8BJB4sLyAjAQEjHi0vISIBkyQfLS4hIUIuLh4jBgj8XdxumJtt4QOc/DH+/qW4BQW4ngEFBLI0HhocITc0HhofHzE1IB0BHR41NTwdHgAAAAEAVf/iBBkGKgAvAIsAsi4AACuyFgAAK7EXBemyBwIAK7EoB+myIAEAK7EfBemzASAfCCuxAAXpAbAwL7Au1rACMrEtDemwLRCxAAzpsAAvsC0QsRsBK7ESDemzDBIbCCuxJBTpsCQvsQwU6bIkDAors0AkFgkrsTEBK7EbABESsgcQHzk5OQCxIB8RErAQObAoEbAMOTAxEzUzERA3NjMyFx4BFRQHBgcEExYHBic1Fjc2JzQnJgc1Fjc2JzQnJiMiBwYVESMRVVCbXnpsXEliGihVASEEA8aosYt3gAFzb55bRlYBKz5xaT00mwLqjwEDARFiOy8mr2JCO1s3iv7F8pyGBo0FbXWnn2hjA40EOEZtSDlSSUCT+3sC6gAAAAMATP/jBAYF+QAQACAAJABbALIKAAArsg0AACuxFQTpsgcBACuyBAEAK7EdBekBsCUvsADWsREO6bARELEGASuxChkyMrEIDumxJgErsQYRERKyFSEjOTk5sAgRsBg5ALEdFRESsQsGOTkwMRM0NzYXFhc1MxEjNQYHBicmNwYXFjMyNzYnJicmBwYHBhM3EwdMd4HTzIGioIPQwYaBpQVaXIyRWlUDA1JanIJWU4yk9F8B3tSSngcHm438OoOeAgOcl96TbnRwapSXY28HBWljAy9P/rQ+AAMAPv/jA/kF/AAQACAAJABdALIKAAArsg0AACuxFQTpsgcBACuyBAEAK7EdBekBsCUvsADWsREO6bARELEGASuxChkyMrEIDumxJgErsQYRERKyFSEjOTk5sAgRsBg5ALEdFRESsgALBjk5OTAxEzQ3NhcWFzUzESM1BgcGJyY3BhcWMzI3NicmJyYHBgcGGwEXAT54gdPMgaKgg9DBhoGlBVpcjJFaVQMDUlqcglZTrvWi/skB3tSSngcHm438OoOeAgOcl96TbnRwapSXY28HBWljAjUBTE7+xQADAD7/4wP5Bc4AEAAgACYAYACyCgAAK7INAAArsRUE6bIHAQArsgQBACuxHQXpAbAnL7AA1rERDumwERCxBgErsQoZMjKxCA7psSgBK7EGERESsxUhIiQkFzmwCBGxGCM5OQCxHRURErIACwY5OTkwMRM0NzYXFhc1MxEjNQYHBicmNwYXFjMyNzYnJicmBwYHBhMJAQcnBz54gdPMgaKgg9DBhoGlBVpcjJFaVQMDUlqcglZTKgE7ATxvzcwB3tSSngcHm438OoOeAgOcl96TbnRwapSXY28HBWljAkkBCv7yVaqqAAAAAwA+/+MD+QVoABAAIAA6AG0AsgoAACuyDQAAK7EVBOmyBwEAK7IEAQArsR0F6bAyL7A6M7QpBwAgBCsBsDsvsADWsREO6bARELEGASuxChkyMrEIDumxPAErsQYRERKzFSEuMiQXObAIEbEYLzk5ALEdChESsgAGCzk5OTAxEzQ3NhcWFzUzESM1BgcGJyY3BhcWMzI3NicmJyYHBgcGAzc2NzYfARYzMjc2PwEXBwYjIi8BJgcGDwE+eIHTzIGioIPQwYaBpQVaXIyRWlUDA1JanIJWUwIVQWJCPHUsFw4NGSQQXw1GbkBDXjEhLiMOAd7Ukp4HB5uN/DqDngIDnJfek250cGqUl2NvBwVpYwIbMpoGBCdNHQgQUSMrIbAvQSIFCF4hAAAEAHb/4wQxBVEAEAAgADAAQACKALIKAAArsg0AACuxFQTpsgcBACuyBAEAK7EdBemwLS+wPTOxJQrpsDUyAbBBL7AA1rERDumwERCxIQErsSkM6bApELEGASuxChkyMrEIDumzOQgGCCuxMQzpsDEvsTkM6bFCASuxKSERErElLTk5sDERsBU5sDkSsBg5ALEdChESsgAGCzk5OTAxEzQ3NhcWFzUzESM1BgcGJyY3BhcWMzI3NicmJyYHBgcGEzY3NjMyFxYHBgcGIyInJiU0NzYzMhcWFRQHBiMiJyZ2eIHTzIGioIPQwYaBpQVaXIyRWlUDA1JanIJWUygBIx4sLyEjAQEkHiwvIiIBlCMfLi4hISEhLi4fIwHe1JKeBwebjfw6g54CA5yX3pNudHBqlJdjbwcFaWMCaTQfGh0gNzQeGh4fMTUgHR4dNTUfHh0eAAAABABM/+MEBgYpABAAIAAwAEAAlwCyCgAAK7INAAArsRUE6bIlAgArtD0HABMEK7IHAQArsgQBACuxHQXptC01HSUNK7QtBwATBCsBsEEvsADWsREO6bARELEhASu0MRQAEgQrsDEQsTkBK7QpFAASBCuwKRCxBgErsQoZMjKxCA7psUIBK7E5MRESshUtJTk5OQCxHQoRErEGCzk5sT01ERKyKTEhOTk5MDETNDc2FxYXNTMRIzUGBwYnJjcGFxYzMjc2JyYnJgcGBwYTNDc2MzIXFhUUBwYjIicmNxQXFhcWNzYnNCcmJyYHBkx3gdPMgaKgg9DBhoGlBVpcjJFaVQMDUlqcglZTjTc9Z2c8Nzc8Z2c9N0wgJkFFKSYBIyNBRSokAd7Ukp4HB5uN/DqDngIDnJfek250cGqUl2NvBwVpYwLTWT1FRT1ZWT5ERD5VNycrAgMuKDs4KSgDAy4oAAMAPv/aBvAD2AAoADcAPgCEALIiAAArsiUAACuwHjOxLQTpsBUysgQBACuwCzOxNAXpsDwytDgQJQsNK7E4B+myOBAKK7NAOAgJKwGwPy+wANaxKQ7psCkQsSIBK7EGMDIysSEN6bEIEDIysUABK7EiKRESsC05ALE4JREStQAYGSAjMCQXObA0EbAGObAEErAJOTAxEzQ3NhcWFzUzFTYXFgQfASEVFBcWFzI/ARcHBgcGJyYnFSM1BgcGJyY3BhcWMzI2NTQnJgciBwYFIS4BJyYGPniB08yBony3tAEDCAX9E19bc6haLIYmQIBsaLR9oIPQwYaBpQVaXIyLr1FZnoFXUwMjAjkOmGlwqwHX1JKeBweajVh0BAPwsHYpdVhTAYVBTz9pOzECBHRZhJ4CA5uX35NvdMe0k11mAWtkMGmJBQWNAAACAGD+OgOSA+EAGQAdAD0AshYAACuxEATpsgMBACuxCgXpAbAeL7AA1rENC+mxHwErALEQFhESsREUOTmwChGxBhM5ObADErAFOTAxEyYANzYXFSYnJiMOARceARcWPwEVBiMiJyYBExcDYAEBF+Kliy9oVj6RxgEBsIe/cyGLrdaTjwEZw5jqAePWASIGBFvbTDAnAdiTjc8HCY4p3mmWkv1kAS9A/tUAAAAAAwBe/+ED9QX5ABsAIwAnAEgAshgAACuxDwjpsgQBACuxIQjptBwKGAQNK7EcB+kBsCgvsADWsQsL6bAcMrILAAors0ALCQkrsSkBKwCxCg8RErESEzk5MDETJjc2FxYXFh8BIRUUFxYzMj8BFwcGBwYnJicmEyEmJyYnJgYTNxMHXgF5g9i0gYIIBf0TX1tzqFoshiZAgG1n0oJ8sAI5DkxMaXCqYKTzXwHb1pOfBAR4eK93KHVZU4RCUD5pOzECBZePAUBpRUQGBY4C70/+tD4AAAADAF7/4QP1BfwAGwAjACcASACyGAAAK7EPCOmyBAEAK7EhCOm0HAoYBA0rsRwH6QGwKC+wANaxCwvpsBwysgsACiuzQAsJCSuxKQErALEKDxESsRITOTkwMRMmNzYXFhcWHwEhFRQXFjMyPwEXBwYHBicmJyYTISYnJicmBhsBFwFeAXmD2LSBgggF/RNfW3OoWiyGJkCAbWfSgnywAjkOTExpcKqN9aP+yQHb1pOfBAR4eK93KHVZU4RCUD5pOzECBZePAUBpRUQGBY4B9QFMTv7FAAMAXv/hA/UFzgAbACMAKQBIALIYAAArsQ8I6bIEAQArsSEI6bQcChgEDSuxHAfpAbAqL7AA1rELC+mwHDKyCwAKK7NACwkJK7ErASsAsQoPERKxEhM5OTAxEyY3NhcWFxYfASEVFBcWMzI/ARcHBgcGJyYnJhMhJicmJyYGAwkBBycHXgF5g9i0gYIIBf0TX1tzqFoshiZAgG1n0oJ8sAI5DkxMaXCqAwE7ATtvzM0B29aTnwQEeHivdyh1WVOEQlA+aTsxAgWXjwFAaUVEBgWOAgkBCv7yVaqqAAAEAF7/4QP1BU0AGwAjADMAQwCDALIYAAArsQ8I6bIEAQArsSEI6bQcChgEDSuxHAfpsDAvsEAzsSgK6bA4MgGwRC+wANaxCwvpsBwysgsACiuzQAsJCSuwCxCxJAErsSwM6bAsELE0ASuxPAzpsUUBK7EsJBESsSgwOTmwNBGwDzmwPBKxEh05OQCxCg8RErESEzk5MDETJjc2FxYXFh8BIRUUFxYzMj8BFwcGBwYnJicmEyEmJyYnJgYDNjc2MzIXFgcGBwYjIicmJTQ3NjMyFxYVFAcGIyInJl4BeYPYtIGCCAX9E19bc6haLIYmQIBtZ9KCfLACOQ5MTGlwqgcBIx4tLyAjAQEkHiwvIiIBlCMfLi4hISEhLi4fIwHb1pOfBAR4eK93KHVZU4RCUD5pOzECBZePAUBpRUQGBY4CJjQeGh0gNzQeGh4fMTUgHR4dNTUfHh0eAAIAUwAAAesF+QADAAcAIACyBAAAKwGwCC+wBNaxBwvpsQkBK7EHBBESsAE5ADAxEzcTBwMRMxFTpPRf1KkFqk/+tD77kQPD/D0AAAACAF4AAAH1BggAAwAHAC8AsgQAACuyAQIAKwGwCC+wBNaxBwvpsQkBK7EHBBESsAE5ALEBBBESsQMFOTkwMRsBFwETETMRXvWi/skEqAS8AUxO/sX7gQPD/D0AAAL/6wAAAmEFzgAFAAkAIgCyBgAAKwGwCi+wBtaxCQvpsQsBK7EJBhESsQQBOTkAMDEDCQEHJwcTERcRFQE7ATtuzcxkqATEAQr+8lWqqvuVA8MF/EIAAAAD/9EAAAJDBVcADgASACAAXACyDwAAK7AML7AdM7EECumwFzIBsCEvsADWsQgM6bAIELEPASuxEgvpsxMSDwgrsRoM6bEiASuxCAARErEEDDk5sRoSERKxFx05OQCxDA8RErAQObAEEbAaOTAxAzY3NjMyFxYHBgcGIyImExEzEQM0NzYzMhYVFAYjIicmLwEkHiwvICMBASMeLS9D8agGJB8tLkJCLi4eIwTrNB4aHSA3NB4aPftMA8P8PQTmNR8dOzU1PBweAAIAYwAAA2QFbQAXADEAdACyAAAAK7AOM7IBAQArsgkBACuxEgjpsCkvsDEztCAHACAEKwGwMi+wANaxFwvpsAIysBcQsQ8BK7EOC+mxMwErsRcAERKxGDE5ObAPEbQJHR8rLSQXObAOErElJjk5ALEBEhESsAM5sSApERKxGCw5OTAxMxEzFTY3Njc2MzIXFhURIxEQJyYHBhURAzc2NzYfARY3Mjc2PwEXBwYnIi8BJgcGDwFjqRQVMCROTnpZbKjCc0A7hhVBYkI8dSwXDg0ZJBBfDUZuQENeMSEuIw4DxnUZFjEQIk5g4f2sAkIBAAcEWVGE/eEEnDGaBgQnTR0BBxBRIyshsAEuQiIGCF0iAAAAAAMAZP/ZBFkF+QANABwAIAA9ALILAAArsREE6bIEAQArsRkI6QGwIS+wANaxDgvpsA4QsRUBK7EHC+mxIgErsRUOERKzCxkdHyQXOQAwMRM0NzYXFgAHBgcGIyIANx4BNzY3Njc2JyYjIgcGEzcTB2Sbms7PASQBAYiP5ND+2KcBypCKYF4EBGBflZNjYm+k818B0tOZmQEB/tTP2JGYASjRktICAWhljpZsbWtrA0VP/rQ+AAMAZP/ZBFkF/AANABwAIAA9ALILAAArsREE6bIEAQArsRkI6QGwIS+wANaxDgvpsA4QsRUBK7EHC+mxIgErsRUOERKzCxkdHyQXOQAwMRM0NzYXFgAHBgcGIyIANx4BNzY3Njc2JyYjIgcGGwEXAWSbms7PASQBAYiP5ND+2KcBypCKYF4EBGBflZNjYqH1o/7JAdLTmZkBAf7Uz9iRmAEo0ZLSAgFoZY6WbG1rawJLAUxO/sUAAAADAGT/2QRZBc4ADQAcACIAPQCyCwAAK7ERBOmyBAEAK7EZCOkBsCMvsADWsQ4L6bAOELEVASuxBwvpsSQBK7EVDhESswsZHR8kFzkAMDETNDc2FxYABwYHBiMiADceATc2NzY3NicmIyIHBhMJAQcnB2Sbms7PASQBAYiP5ND+2KcBypCKYF4EBGBflZNjYioBOwE7b8zNAdLTmZkBAf7Uz9iRmAEo0ZLSAgFoZY6WbG1rawJfAQr+8lWqqgAAAAADAGT/2QRZBWgADQAcADYAWwCyCwAAK7ERBOmyBAEAK7EZCOmwLi+wNjO0JQcAIAQrAbA3L7AA1rEOC+mwDhCxFQErsQcL6bE4ASuxFQ4RErQLGR0qLiQXObAHEbArOQCxJS4RErEdMTk5MDETNDc2FxYABwYHBiMiADceATc2NzY3NicmIyIHBhM3Njc2HwEWMzI3Nj8BFwcGIyIvASYHBg8BZJuazs8BJAEBiI/k0P7YpwHKkIpgXgQEYF+Vk2NiDRVBYkI8dSwWDg4ZJBBfDUZuQERdMSEuIw4B0tOZmQEB/tTP2JGYASjRktICAWhljpZsbWtrAjEymgYEJ00dCBBRIyshsC9BIgUIXiEAAAQAZP/ZBFkFUQANABwALAA8AG0AsgsAACuxEQTpsgQBACuxGQjpsCkvsDkzsSEK6bAxMgGwPS+wANaxDgvpsA4QsR0BK7ElDOmwJRCxLQErsTUM6bA1ELEVASuxBwvpsT4BK7ElHRESsSEpOTmwLRGxCxk5OQCxISkRErA1OTAxEzQ3NhcWAAcGBwYjIgA3HgE3Njc2NzYnJiMiBwYTNjc2MzIXFgcGBwYjIicmJTQ3NjMyFxYVFAcGIyInJmSbms7PASQBAYiP5ND+2KcBypCKYF4EBGBflZNjYh8BJB4sLyAjAQEjHi0vISIBkyQfLS4hISEhLi4eIwHS05mZAQH+1M/YkZgBKNGS0gIBaGWOlmxta2sCfzQfGh0gNzQeGh4fMTUgHR4dNTUfHh0eAAADAGsAkgQXA4IAAwAPABsAMQCwDS+xBwrpsAAvsQEH6bAZL7ETCukBsBwvsATWsBAytAoTAB4EK7AWMrEdASsAMDETNSEVBTQ2MzIWFRQGIyImETQ2MzIWFRQGIyImawOs/cA/Kyo+PykrPz8rKj4/KSs/Acx9fdIrPj4rKj4+AkoqPj4qKz4+AAAAAAMAZP+HBFkEDgAVAB0AJgB4ALIPAAArsSAE6bITAAArsgMBACuxGwjpsgcBACsBsCcvsADWsRYL6bAWELEjASuxCwvpsSgBK7EWABESsRIUOTmwIxG2BQMRDxkeJSQXObALErIGBwk5OTkAsSAPERKxERQ5ObAbEbQJABgeJSQXObADErAFOTAxEzQAMzIXNxcGBxYHBgcGIyInByc3JjcUFwEmJyIGExY3PgE3NicGZAE0z5N/cGcbYYYBAYmO5IpzeFlznqhbAbFRZJPFt0xZir4EA0h0AdLTATFWjkMheJPF2JGYRZdLkZrVh2cCITUB1v41KwEDy46DYpEAAAACAGL/4gNaBfkAEwAXAEEAshEAACuxBgTpsgABACuwCzMBsBgvsBPWsQIO6bACELEKASuxDQ7psRkBK7EKAhESshQVFzk5ObANEbAWOQAwMRMzERQXFjMyNzY1ETMRFAcGBwQREzcTB2KlJy+GhS8fpERZ2v6AvqT0XwPG/fmsRFVlRJwCB/3s1myLAwUByAQFT/60PgAAAAIAYv/iA1oF/AATABcAQQCyEQAAK7EGBOmyAAEAK7ALMwGwGC+wE9axAg7psAIQsQoBK7ENDumxGQErsQoCERKyFBUXOTk5sA0RsBY5ADAxEzMRFBcWMzI3NjURMxEUBwYHBBEbARcBYqUnL4aFLx+kRFna/oD39aP+yQPG/fmsRFVlRJwCB/3s1myLAwUByAMLAUxO/sUAAgBi/+IDWgXOABMAGQBJALIRAAArsQYE6bIAAQArsAszAbAaL7AT1rECDumwAhCxCgErsQ0O6bEbASuxAhMRErAUObAKEbIVGBk5OTmwDRKxFhc5OQAwMRMzERQXFjMyNzY1ETMRFAcGBwQREwkBBycHYqUnL4aFLx+kRFna/oBoATsBO27NzAPG/fmsRFVlRJwCB/3s1myLAwUByAMfAQr+8lWqqgAAAwBi/+IDWgVRABMAIwAzAHAAshEAACuxBgTpsgABACuwCzOwIC+wMDOxGArpsCgyAbA0L7AT1rECDumwFCDWEbEcDOmwAhCxCgErsQ0O6bANELAsINYRsSQM6bAkL7EsDOmxNQErsRwUERKxGCA5ObAkEbAGOQCxGCARErAsOTAxEzMRFBcWMzI3NjURMxEUBwYHBBETNjc2MzIXFgcGBwYjIicmJTQ3NjMyFxYVFAcGIyInJmKlJy+GhS8fpERZ2v6ATgEkHiwvICMBASMeLS8hIgGTJB8tLiEhISEuLh4jA8b9+axEVWVEnAIH/ezWbIsDBQHIAz80HxodIDc0HhoeHzE1IB0eHTU1Hx4dHgADADz95QQ+BVQABwAXACcAVgCyAAEAK7ADM7AUL7AkM7EMCumwHDIBsCgvsAjWsRAM6bAQELEYASuxIAzpsSkBK7EQCBESswUHDBQkFzmwGBGwAjmwIBKwAzkAsQwUERKxGCA5OTAxEzMJATMBIwEDNjc2MzIXFgcGBwYjIicmJTQ3NhcyFxYVFAcGIyInJjy/AVABObr9OrsBMqIBJB4sLyAjAQEjHiwvIiIBlCMfLi4gIiIhLS4fIwPG/VECr/ofAnYEjTQeGhwgNzQeGh4fMTUgHQEdHjU1Hh4dHgAAAAIAYP/dCRcGGgAaACYAgQCyEwAAK7EQA+myGAAAK7EeA+myCAIAK7ELA+myAwIAK7EkA+m0DA8YAw0rsQwI6QGwJy+wANaxGwvpsBsQsRQBK7EHITIysRAL6bALMrIQFAors0AQEgkrsAkys0AQDgkrsSgBK7EUGxESsAM5ALEPEBESsBQ5sQsMERKwBzkwMRMSACEyFxYXESEVIREhFSERIRUhEQYHBickABMCAAUEAAMCAAUGAGABAdcBRb6ppHIDHf2UAlj9oAJw/Odxpau8/rf+KqwDAXIBBAEGAXoEBP6J/vP//pkDAQFHAdJTUpIBJaH+OZv9nKEBF5JUVgIBAdoBSf76/oYBAQF/AQcBCAFyBgf+kAADAE3/2Qc1A98AIAAtADQAjACyHgAAK7AaM7EkBOmwETKyAwEAK7AHM7ErCOmwMjK0LgweBw0rsS4H6QGwNS+wANaxIQvpsCEQsSgBK7ENEOmwLjKyDSgKK7NADQsJK7E2ASuxKCERErEeKzk5sA0RsQUcOTkAsSQeERKwIzmwDBGzFBUcJSQXObAuErAAObArEbAFObADErAzOTAxEzQAFwQXNgUWBB8BIRUUFxYzMj8BFwcGBwYnJicGISIANx4BNzY3Njc2JiMiBgUhLgEnJgZNATTOAQKUgwEJtAEDCAX9E19bc6haLIYmQIBtZ/uEkf7n0P7XqAHKj4pgXgQEvpaTxQNcAjkOmGlwqwHS0wEyAQHW5QUE8K93KHVZU4RCUD5pOzECBdDdASfSktMDAWhljpbZ1hlpigUFjgAAAAMAOAAABM4HYAAIABgAJwBrALIHAAArsgACACuwAzOwFS+wJDOxDQrpsB0yAbAoL7AH1rEGEOmzEQYHCCuxCQzpsAkvsREM6bAGELEZASuxIQzpsSkBK7EHCRESsQ0VOTmxBhERErACOQCxAAcRErACObENFRESsCE5MDETMwkBMwERIxEDNjc2MzIXFgcGBwYjIicmJTQ3NjMyFxYVFAYjIicmOMEBiQGMwP4IrdQBJB4sLyAjAQEjHi0vISIBkyQfLS4hIUIuLh4jBgj9UgKu/Jr9XgKfBFU0HhodIDc0HhofHzE1Hx0dHjU1PBweAAEAYAG7AfwCVAADAAATNSEVYAGcAbuZmQAAAAABAGABuwH8AlQAAwAAEzUhFWABnAG7mZkAAAAAAQBgAbsB/AJUAAMAABM1IRVgAZwBu5mZAAAAAAEAWgG3BVwCVAADABcAsAMvsQAI6bEACOkBsAQvsQUBKwAwMRMhFSFaBQL6/wJUnQAAAAABAFsBtweKAlQAAwAXALAAL7EBCOmxAQjpAbAEL7EFASsAMDETNSEXWwctAgG3nZ0AAAAAAQBoA8kBwQYIAAMAIgCyAQIAK7QDCgAIBCsBsAQvsADWtAIMAAwEK7EFASsAMDEbARcDaOlwwQQDAgUq/esAAAEAaAPKAcAGCAADACIAsgECACu0AwoACAQrAbAEL7AA1rQCDAAMBCuxBQErADAxGwEXA2jAmOkD9AIUOf37AAABAGj+fAHAALoAAwAgALADL7QBCgAIBCsBsAQvsADWtAIMAAwEK7EFASsAMDEbARcDaMCY6f6mAhQ5/fsAAAAAAgBnA8UDFQYIAAMABwAoALIBAgArsAUztAMKAAgEK7AHMgGwCC+xCQErALEBAxESsQQGOTkwMRsBFwM3ExcDZ+lwwb3pcMEEAwIFKv3sNAIFKv3sAAAAAAIAZAPBAxEF/wADAAcAHACwAy+wBzO0AQoACAQrsAUyAbAIL7EJASsAMDEbARcDNxMXA2TAmOnmwJjpA+sCFDn9+yoCFDn9+wAAAAACAGT+fAMRALoAAwAHABwAsAMvsAcztAEKAAgEK7AFMgGwCC+xCQErADAxGwEXAzcTFwNkwJjp5sCY6f6mAhQ5/fsqAhQ5/fsAAAAAAQBeAgYCbQQUAA8AMQCwDC+0BAoACAQrtAQKAAgEKwGwEC+wANa0CAwACAQrsREBK7EIABESsQQMOTkAMDETJjc2MzIXFhcWBwYjIicmXgRTTG1pSVEEBFNMbWlKUQMFgEtEQEZ5gEtEQEYAAwBW//sEqwDqAAsAFwAjAEUAsgkAACuxFSEzM7EDCumxDxsyMrIJAAArsQMK6QGwJC+wANaxBgzpsAYQsQwBK7ESDOmwEhCxGAErsR4M6bElASsAMDE3NDYzMhYVFAYjIiYlNDYzMhYVFAYjIiYlNDYzMhYVFAYjIiZWRjIxRkYxMUcBs0YyMUZGMTFHAbNGMTFHRjIxRnMxRkYxMUdGMjFGRjExR0YyMUZGMTFHRgACAGMDFQV+BgsABwAUAHgAsgECACuxCQwzM7QABwATBCuwAzKyAAEKK7NAAAYJK7IIDhEyMjIBsBUvsAbWtAUUABIEK7IFBgors0AFAwkrsgYFCiuzQAYACSuwBRCxCAErtBQUABIEK7AUELEPASu0DhQAEgQrsRYBK7EPFBESsQoMOTkAMDETNSEVIxEjEQETMxsBMxEjEQMjAxFjAiHsSwFqBXTt9WxJ+kLxBcw/P/1JArf9SQLy/WkClv0PAp/9YgKg/V8AAAEAAAAAA8MDwwADADUAsgAAACu0AQoABwQrsgAAACu0AQoABwQrAbAEL7AA1rQDDAAHBCu0AwwABwQrsQUBKwAwMTERIREDwwPD/D0AAAAAAQAAAAEAAP8cmJlfDzz1AB8IAAAAAADLwYNRAAAAAMvBg1H/yf3gCRcIMwAAAAgAAgAAAAAAAAABAAAIM/28AAAJhf/J/6YJFwABAAAAAAAAAAAAAAAAAAAAzggAAAAAAAAACAAAAAGjAAABlABcAjcAZgTyAGYEcwBgBbQAbAXNAFsBMQBaAgQAYgIcAG0C7QBsBI8AYAHBAGACWgBgAWYAXwTlAE0FOQBrBTkByAU5AKcFOQDOBTkAYwU5AJgFOQCfBTkAYgU5AMcFOQCNAbAAZAIbAGAEjwBiBI8AZgSYAF8DkABZBlwASgXOACoEQQCFBY8AYwUyAHYD2ACUA3UAlAawAFoFMgBmAeoApwM1ADQE4wCNAwsAjQcDADEGHABjBvYAYAPmAGIG9gBZBCUAYAR5AF4EOwBcBQ8AXAU6ADwI6wBCBLoAOAT5ADgFBwBiAj4AZATkAF8CPwBWAzUAYgU6ABECYQBmBHsAPgSJAIoD7gBaBG4AMwQ8AF4CrwBvBHsANAPJAGYCGwCcAhMAngPJAJQB6QCyBbIAZAPDAGMEswBkBHsAhgSJAFsCvQCFA2AAZwJ3AGADtgBiBCYAPAZ4AEEEVwA+BHQAPAS5AFoCkgBkAUAAWgKaAGQDOgBKAz0AAAGhAFcD7gBeBQ8AawUIADgDwgBTAzUAawalAGQDmQBsBKsAWwbHAFwCWgBgBp0AVwJwAF8D9wBeAkwAWQSJAGIDrQBeBLgAZgOQAFYFyQAqBecAKgXnACoF7AAqBdYALwXXACoI0QBmBZ0AYgQqAJQD2gB5BCoAlAQeAJQB9wBOAgMATQID/8kCA//SBhYASAb1AGAG9QBgBukAYAbyAFEG+QBVBvYAYATxAFwFCABcBP0AXAUKAEUEbgBVBHUATARvAD4EhAA+BG8APgSEAHYEfQBMBzoAPgPmAGAETABeBFIAXgRSAF4ETABeAh0AUwIkAF4CHv/rAiL/0QO+AGMEswBkBL4AZAS6AGQEqwBkBMIAZARzAGsEswBkA8QAYgO8AGIDvABiA7cAYgSTADwJhQBgB5sATQUVADgEGQAACDMAAAQZAAAIMwAAArsAAAIMAAABXQAAAV0AAAEGAAABowAAAHQAAAJaAGACWgBgAloAYAWrAFoH4ABbAiIAaAIbAGgCGwBoA1gAZwN1AGQDdQBkAtkAXgUIAFYBowAAAgwAAAXXAGMDwwAAAAAAAAAAAAAAAAA8AGQBQAHIAoYDEgMwA2QDmAPMBAoEKARGBHAEgATaBPwFSAXSBg4GgAbgBwAHlgf4CEoIhgiaCLwI0AluCioKWgq+CxYLXguaC9AMTAyEDKIM3A0QDTYNkg3CDjAOeg8ED2IP3hAOEFIQdhCmENgRCBEsEVoRahGcEcYR4hIAEmoS2BMqE5YT/hRUFOIVKhVuFbIV5hYCFnQWwBceF44YABg+GMYZBBlEGWgZmBnKGeoaDhp6GpYa/htIG0gbfhv0HMAdJh24HgoevB8yH1Ifeh+IIFYgsCEAISAhaiHQIfAigiK6IvQjMiOUJAwkjCTiJTwlgCXEJhAmjCa0Jt4nCidwJ9goPCiiKQopmCo2KrwrCCtUK6QsLiy+LSotmC4MLqAvSi/6MJ4w9DFeMcgyNjLiMwgzNjNiM8Y0UDSqNQY1ZjXuNoY2zDdMN5g35Dg4OMA5MDm8Olw61DrUOtQ61DrUOtQ61DrUOtQ61DrUOtQ64jrwOv47GDsyO1I7cjuSO7475DwKPEA8mDyYPJg8/D0kAAAAAQAAAM4ATgAFAAAAAAACAAEAAgAWAAABAAFOAAAAAAAAAAwAlgABAAAAAAABAAoAFgABAAAAAAACAAQAKwABAAAAAAADACcAgAABAAAAAAAEAA8AyAABAAAAAAAFACABGgABAAAAAAAGAA0BVwADAAEECQABABQAAAADAAEECQACAAgAIQADAAEECQADAE4AMAADAAEECQAEAB4AqAADAAEECQAFAEAA2AADAAEECQAGABoBOwBGAHUAdAB1AHIAYQAgAEkAQwBHAABGdXR1cmEgSUNHAABCAG8AbwBrAABCb29rAABBAGwAdABzAHkAcwAgAEYAbwBuAHQAbwBnAHIAYQBwAGgAZQByACAANAAuADEAIABGAHUAdAB1AHIAYQAgAEkAQwBHACAAQgBvAG8AawAAQWx0c3lzIEZvbnRvZ3JhcGhlciA0LjEgRnV0dXJhIElDRyBCb29rAABGAHUAdAB1AHIAYQAgAEkAQwBHACAAQgBvAG8AawAARnV0dXJhIElDRyBCb29rAABBAGwAdABzAHkAcwAgAEYAbwBuAHQAbwBnAHIAYQBwAGgAZQByACAANAAuADEAIAAyADUALwAwADEALwA5ADYAAEFsdHN5cyBGb250b2dyYXBoZXIgNC4xIDI1LzAxLzk2AABGAHUAdAB1AHIAYQBJAEMARwBCAG8AbwBrAABGdXR1cmFJQ0dCb29rAAAAAgAAAAAAAP8oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADOAAABAgEDAAMABAAFAAYABwAIAAkACgALAAwADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEAIgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQA+AD8AQABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0AXgBfAGAAYQEEAKMAhACFAJYAhgCOAIsAnQCpAKQBBQCKAIMAkwCNAIgAngCqAKIArQDJAMcArgBiAGMAkABkAMsAZQDIAMoAzwDMAM0AzgBmANMA0ADRAK8AZwCRANYA1ADVAGgAiQBqAGkAawBtAGwAbgCgAG8AcQBwAHIAcwB1AHQAdgB3AHgAegB5AHsAfQB8ALgAoQB/AH4AgACBALoAsACxALsBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETALIAswC2ALcAxAC0ALUAxQCHAKsBFAEVAIwBFgZnbHlwaDEGZ2x5cGgyB3VuaTAwQTAHdW5pMDBBRAd1bmkyMDAwB3VuaTIwMDEHdW5pMjAwMgd1bmkyMDAzB3VuaTIwMDQHdW5pMjAwNQd1bmkyMDA2B3VuaTIwMDcHdW5pMjAwOAd1bmkyMDA5B3VuaTIwMEEHdW5pMjAxMAd1bmkyMDExCmZpZ3VyZWRhc2gHdW5pMjAyRgd1bmkyMDVGB3VuaUUwMDAAuAH/hbABjQBLsAhQWLEBAY5ZsUYGK1ghsBBZS7AUUlghsIBZHbAGK1xYALADIEWwAytEsAggRbIDuQIrsAMrRLAHIEWyCC0CK7ADK0SwBiBFsgdTAiuwAytEsAUgRbIG5QIrsAMrRLAEIEWyBUQCK7ADK0SwCSBFugADAREAAiuwAytEsAogRbIJEgIrsAMrRAGwCyBFsAMrRLAMIEWyCxICK7EDRnYrRLANIEWyDBgCK7EDRnYrRLAOIEWyDWQCK7EDRnYrRLAPIEWyDj0CK7EDRnYrRLAQIEWyDzACK7EDRnYrRLARIEWyECMCK7EDRnYrRLASIEWyESICK7EDRnYrRLATIEWyEiECK7EDRnYrRLAUIEWyEykCK7EDRnYrRFmwFCsAAAAAAU+b0tEAAA==) format('truetype'); +} +@font-face { + font-family: FuturaICGLight; + font-weight: bold; + src: url(data:font/ttf;base64,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BxEAfQciAGkCKgBuAtYAdwLPAHADewBiBNwAbgMeAFcECgBqAqoAZwUAAFsF7QB2Be0BxgXtAJQF7QCUBe0AbgXtANMF7QB7Be0AdAXtALcF7QCFAqEAYANfAF0EqgBuBOQAYQSqAGkFDgBzBpoAZwa3ADYFXQCUBWwAcQYDAJAEPQBlBEUAoQcDAG0GPQBtAv4AvgRbACgGUgCZBDcAjwf0ADIGzQBpB1AAbgVeAIoHiQBrBbsAjwVBAG8E+QByBhEAdgbFAD0JgAA5BuMAPAZwAEEGPQBuAsgAawUGAG4CzwBvBEUAZAUOAAkC1gB3BbMAZQWIAI0EGgB2BZAAOwVIAGUD2gBuBYgAQAUjAHMC3ACdAuEApAXYAKECswCyB7sAcwUrAG8FwgByBZgAogWQADsD6ACZBGMAawODAGYFIwBpBacAPQivAFcGWgBGBe8ARQUdAGcDCABpAfcAZgMIAGsD1QBOBTUAAAKdAGQEDABwBf0AdgZwAEEFXQBzBGkAagbAAG4DowB5BLwAaQa/AHcECgBqBr8AbgKTAFYEFgBfAsoAXwWBAG4DwgBtBMIAbQUPAG4GxAA2BrEANga8ADYGygA2BrwANgbYAD0IOQB4BWwAbgQtAGUEQwBlBCEAZAQhAEgC/wClAwEAuQMI/+gDBv/JBtgAaQddAG4HXQBuB10AbgdfAG4HbwBuB5UAbQYbAHYGEAB2BhsAdgYIAFkFpgBzBaMAZQWrAHMFowBlBZ0AZgWjAGUFngBlCNIAZQQMAGoFRgBlBUYAZQVMAGUFOABlAusAnALdAH4C6v/vAuj/wQUpAH0FsQBXBasAVwW5AGUFwQBlBbkAVwRqAHMFwgBzBSIAaQUaAGkFIgBpBSIAaQYAAGEJLgBiCKkAXAZ3AEEESAAACJAAAARIAAAIkAAAAtoAAAIkAAABbQAAAW0AAAESAAABtgAAAHkAAAQKAGoECgBqBAoAagYEAG4IWwBtAywAdgM7AHIDOwByBToAbQUzAHsFMwB7A9IAbwdyAGcBtgAAAiQAAAXrAGsEHgAAAAAAAAAAAAAAAABIAIABaAHgAowDHgM+A2oDmAPOBBAEMARKBIYElgTqBRAFYAXOBhoGfgbkBwQHbAfACBwIXghyCJQIqAkmCgQKNgqiCwALRguGC7wMIAxYDHYMsgzmDQwNZg2iDfoOTA7O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) format('truetype'); +} + +#header { + background-image: linear-gradient(to right, rgba(255,255,255,0.8) 40%, rgba(255,255,255,0)), + url(data:image/png;base64,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); + background-color: #9ec2da; + background-position: 0 0, 100% 0; + background-size: auto, 1060px; + background-repeat: no-repeat; } \ No newline at end of file diff --git a/lsp/main.js b/lsp/main.js index 8ebebe90..3f7b7630 100755 --- a/lsp/main.js +++ b/lsp/main.js @@ -14,6 +14,8 @@ function consolelog() { } } +var ih = false; + function confirmDelete(question){ return confirm(question); } @@ -56,6 +58,151 @@ function formatTime(date){ ('00' + d.getSeconds()).slice(-2) ].join(':'); } +/** + * Format a time duration to something like "2 days, 00:00:00.000" + * @param ms the duration to format in miliseconds + */ +function formatDuration(ms) { + var secs = Math.floor(ms / 1000), mins = 0; + ms = ms % 1000; + if (secs >= 60) { + mins = Math.floor(secs / 60); + secs = secs % 60; + } + if (mins >= 60) { + var hours = Math.floor(mins / 60); + mins = mins % 60; + } + var string = ('00'+mins).slice(-2)+':'+('00'+secs).slice(-2)+'.'+('000'+ms).slice(-3); + if (hours >= 24) { + var days = Math.floor(hours / 24); + hours = hours % 24; + } + if (hours > 0) { + string = ('00'+hours).slice(-2)+':'+string; + } + if (days > 0) { + string = days+' day'+(days > 1 ? 's' : '')+', '+string + } + return string; +} +/** + * Capitalize the first letter + * @param string the string + */ +function capFirstChar(string) { + if (string.length <= 0) { return ''; } + return string[0].toUpperCase() + string.slice(1); +} +/** + * Flot tick generator for bandwidth + * @param axis the axis + */ +function flotTicksBandwidthAxis(axis) { + var range = axis.max - axis.min; + var delta = range / 4; + var start = axis.min; + if (axis.max < 1024) { // unit: bytes/s + if (delta > 100) { delta = Math.floor(delta/100)*100; start = Math.floor(start/100)*100; } // to lowest 100 bytes/s + else if (delta > 10) { delta = Math.floor(delta/10)*10; start = Math.floor(start/10)*10; } // to lowest 10 bytes/s + } + else if (axis.max < 1048576) { //unit: kiB/s + if (delta > 102400) { delta = Math.floor(delta/102400)*102400; start = Math.floor(start/102400)*102400; } //to lowest 100 kiB/s + else if (delta > 10240) { delta = Math.floor(delta/10240)*10240; start = Math.floor(start/10240)*10240; } //to lowest 10 kiB/s + else if (delta > 1024) { delta = Math.floor(delta/1024)*1024; start = Math.floor(start/1024)*1024; } //to lowest 1 kiB/s + else { delta = Math.floor(delta/102.4)*102.4; start = Math.floor(start/102.4)*102.4; } //to lowest 0.1 kiB/s + } + else { //unit: miB/s + if (delta > 104857600) { delta = Math.floor(delta/104857600)*104857600; start = Math.floor(start/104857600)*104857600; } //to lowest 100 miB/s + else if (delta > 10485760) { delta = Math.floor(delta/10485760)*10485760; start = Math.floor(start/10485760)*10485760; } //to lowest 10 miB/s + else if (delta > 1048576) { delta = Math.floor(delta/1048576)*1048576; start = Math.floor(start/1048576)*1048576; } //to lowest 1 miB/s + else { delta = Math.floor(delta/104857.6)*104857.6; start = Math.floor(start/104857.6)*104857.6; } //to lowest 0.1 miB/s + } + var out = []; + for (var i = start; i <= axis.max; i += delta) { + out.push(i); + } + return out; +} +/** + * Flot axis formatter for bandwidth + * @param val the valuea + * @param axis the axis + */ +function flotFormatBandwidthAxis(val,axis) { + if (val < 0) { var sign = '-'; } + else { var sign = ''; } + val = Math.abs(val); + + if (val < 1024) { return sign+Math.round(val)+' bytes/s'; } // 0 bytes/s through 1023 bytes/s + if (val < 10235) { return sign+(val/1024).toFixed(2)+' kiB/s'; } // 1.00 kiB/s through 9.99 kiB/s + if (val < 102449) { return sign+(val/1024).toFixed(1)+' kiB/s'; } // 10.0 kiB/s through 99.9 kiB/s + if (val < 1048064) { return sign+Math.round(val/1024)+' kiB/s'; } // 100 kiB/s through 1023 kiB/s + if (val < 10480518) { return sign+(val/1048576).toFixed(2)+' miB/s'; } // 1.00 miB/s through 9.99 miB/s + if (val < 104805172) { return sign+(val/1048576).toFixed(1)+' miB/s'; } // 10.0 miB/s through 99.9 miB/s + return sign+Math.round(val/1048576)+' miB/s'; // 100 miB/s and up +} +/** + * Converts the statistics data into something flot understands + * @param stats the statistics.totals object + * @param cumulative cumulative mode if true + */ +function convertStatisticsToFlotFormat(stats,islive) { + var plotdata = [ + { label: 'Viewers', data: []}, + { label: 'Bandwidth (Up)', data: [], yaxis: 2}, + { label: 'Bandwidth (Down)', data: [], yaxis: 2} + ]; + + var oldtimestamp = 0; + var i = 0, up = 0, down = 0; + for (var timestamp in stats) { + if (islive) { + i++; + up += stats[timestamp].up; + down += stats[timestamp].down; + //average over 5 seconds to prevent super spiky unreadable graph + if ((i % 5) == 0) { + plotdata[0].data.push([Number(timestamp)*1000,stats[timestamp].count]); + plotdata[1].data.push([Number(timestamp)*1000,up/5]); + plotdata[2].data.push([Number(timestamp)*1000,down/5]); + up = 0; + down = 0; + } + } + else { + var dt = timestamp - oldtimestamp; + if (stats[oldtimestamp]) { + var up = (stats[timestamp].up - stats[oldtimestamp].up)/dt; + var down = (stats[timestamp].down - stats[oldtimestamp].down)/dt; + } + else { + var up = stats[timestamp].up; + var down = stats[timestamp].down; + } + plotdata[0].data.push([Number(timestamp)*1000,stats[timestamp].count]); + plotdata[1].data.push([Number(timestamp)*1000,up]); + plotdata[2].data.push([Number(timestamp)*1000,down]); + oldtimestamp = timestamp; + } + } + for (var timestamp in stats) { + var dt = timestamp - oldtimestamp; + plotdata[0].data.push([Number(timestamp)*1000,stats[timestamp].count]); + if (stats[oldtimestamp]) { + var up = (stats[timestamp].up - stats[oldtimestamp].up)/dt; + var down = (stats[timestamp].down - stats[oldtimestamp].down)/dt; + } + else { + var up = stats[timestamp].up; + var down = stats[timestamp].down; + } + plotdata[1].data.push([Number(timestamp)*1000,up]); + plotdata[2].data.push([Number(timestamp)*1000,down]); + oldtimestamp = timestamp; + } + return plotdata; +} /** * Check if an URL points to a live datastream or a recorded file * @param url the url in question @@ -136,9 +283,10 @@ function applyInput(){ //apply the inputs $('input.isSetting,select.isSetting').each(function(){ + var objpath = findObjPath($(this)); - if ($(this).val() == '') { + if (($(this).val() == '') || ($(this).val() == 0)) { eval('delete '+objpath+';'); } else { @@ -163,6 +311,37 @@ function findObjPath($element) { } } +function ihAddBalloons() { + var page = settings.ih.pages[settings.currentpage]; + if (!page) { return; } + + //something with pageinfo + if (page.pageinfo) { + $('#page').prepend( + $('<div>').addClass('ih-balloon').addClass('pageinfo').html(page.pageinfo) + ); + } + + for (inputid in page.inputs) { + $('#'+inputid).parent().prepend( + $('<div>').addClass('ih-balloon').addClass('inputinfo').attr('data-for',inputid).html(page.inputs[inputid]).hide() + ); + $('#'+inputid).focus(function(){ + $('.ih-balloon[data-for='+$(this).attr('id')+']').show(); + $('.ih-balloon.pageinfo').hide(); + }).blur(function(){ + $('.ih-balloon[data-for='+$(this).attr('id')+']').hide(); + $('.ih-balloon.pageinfo').show(); + }); + } + $('#page label').each(function(){ + $(this) + }); +} +function ihMakeBalloon(contents,forid) { + return $('<div>').addClass('ih-balloon').attr('data-for',forid).html(contents).hide(); +} + function getData(callBack,sendData,timeOut,doShield){ timeOut = timeOut | 30000; var data = {}; @@ -283,6 +462,81 @@ function getData(callBack,sendData,timeOut,doShield){ var jqxhr = $.ajax(obj); } + +function getWikiData(url,callBack) { + var wikiHost = 'http://rework.mistserver.org'; //must be changed when rework goes live + + $('#message').removeClass('red').text('Connecting to the MistServer wiki..').append( + $('<br>') + ).append( + $('<a>').text('Cancel request').click(function(){ + jqxhr.abort(); + }) + ); + + var obj = { + 'url': wikiHost+url, + 'type': 'GET', + 'crossDomain': true, + 'data': { + 'skin': 'plain' + }, + 'error':function(jqXHR,textStatus,errorThrown){ + switch (textStatus) { + case 'timeout': + textStatus = $('<i>').text('The connection timed out. '); + break; + case 'abort': + textStatus = $('<i>').text('The connection was aborted. '); + break; + default: + textStatus = $('<i>').text(textStatus+'. ').css('text-transform','capitalize'); + } + $('#message').addClass('red').text('An error occurred while attempting to communicate with the MistServer wiki:').append( + $('<br>') + ).append( + textStatus + ).append( + $('<a>').text('Send server request again').click(function(){ + getWikiData(url,callback); + }) + ); + }, + 'success': function(returnedData){ + $('#message').text('Wiki data received'); + + //convert to DOM elements + //returnedData = $.parseHTML(returnedData); + returnedData = $(returnedData); + + //fix broken slash-links in the imported data + returnedData.find('a[href]').each(function(){ + if ((this.hostname == '') || (this.hostname == undefined)) { + $(this).attr('href',wikiHost+$(this).attr('href')); + } + if (!$(this).attr('target')) { + $(this).attr('target','_blank'); + } + }).find('img[src]').each(function(){ + var a = $('<a>').attr('href',$(this).attr('src')); + if ((a.hostname == '') || (a.hostname == undefined)) { + $(this).attr('src',wikiHost+$(this).attr('src')); + } + }); + + consolelog('['+(new Date).toTimeString().split(' ')[0]+']','Received wiki data:',returnedData); + + if (callBack) { + callBack(returnedData); + } + $('#message').text('Last communication with the MistServer wiki at '+formatTime((new Date).getTime()/1000)); + + } + }; + + var jqxhr = $.ajax(obj); +} + function saveAndReload(tabName){ var sendData = $.extend(true,{},settings.settings); delete sendData.logs; @@ -345,12 +599,8 @@ function updateOverview() { var streams = 0; var streamsOnline = 0; - for (var index in data.statistics) { - if (data.statistics[index].curr) { - for (viewer in data.statistics[index].curr) { - viewers++; - } - } + if (data.clients && data.clients.data) { + viewers = data.clients.data.length; } for (var index in data.streams) { @@ -363,7 +613,9 @@ function updateOverview() { $('#cur_streams_online').text(streamsOnline+'/'+streams+' online'); $('#cur_num_viewers').text(seperateThousands(viewers,' ')); $('#settings-config-time').text(formatDateLong(data.config.time)); - }); + + settings.settings.statistics = data.statistics; + },{clients: {}}); } function updateProtocols() { getData(function(data){ @@ -380,6 +632,9 @@ function updateProtocols() { function displayProtocolSettings(theProtocol) { var capabilities = settings.settings.capabilities.connectors[theProtocol.connector]; + if (!capabilities) { + return ''; + } var settingsList = []; for (var index in capabilities.required) { if ((theProtocol[index]) && (theProtocol[index] != '')) { @@ -478,20 +733,31 @@ function buildProtocolParameterFields(data,required,objpath) { return $container.html(); } function updateStreams() { + var streamlist = []; + for (var stream in settings.settings.streams) { + streamlist.push(stream); + } getData(function(data){ + var datafields = {}; + for (var index in data.clients.fields) { + datafields[data.clients.fields[index]] = index; + } + var viewers = {}; + for (var index in data.clients.data) { + if (viewers[data.clients.data[index][datafields['stream']]]) { + viewers[data.clients.data[index][datafields['stream']]]++; + } + else { + viewers[data.clients.data[index][datafields['stream']]] = 1; + } + } for (var index in data.streams) { $('#status-of-'+index).html(formatStatus(data.streams[index])) + $('#viewers-of-'+index).text(seperateThousands(viewers[index],' ')); } - for (var index in data.statistics) { - var viewers = 0; - if (data.statistics[index].curr) { - for (var jndex in data.statistics[index].curr) { - viewers++; - } - } - $('#viewers-of-'+index).text(seperateThousands(viewers,' ')); - } - }); + + settings.settings.statistics = data.statistics; + },{clients:{}}); } function filterTable() { var displayRecorded = $('#stream-filter-recorded').is(':checked'); @@ -886,12 +1152,19 @@ function conversionSelectInput(theFiles) { applyInput(); + var extension = settings.settings.conversion.convert._new_.output.split('.'); + if (extension[extension.length-1] != 'dtsc') { + extension.push('dtsc'); + settings.settings.conversion.convert._new_.output = extension.join('.'); + } settings.settings.conversion.convert._new_.output = settings.settings.conversion.convert._new_.outputdir.replace(/\/$/,'')+'/'+settings.settings.conversion.convert._new_.output; delete settings.settings.conversion.convert._new_.outputdir; if ((settings.settings.conversion.convert._new_.video) && (settings.settings.conversion.convert._new_.video.fps)) { settings.settings.conversion.convert._new_.fpks = Math.floor(settings.settings.conversion.convert._new_.fps * 1000); } + + settings.settings.conversion.convert['c_'+(new Date).getTime()] = settings.settings.conversion.convert._new_; delete settings.settings.conversion.convert._new_; saveAndReload('conversion'); @@ -1019,10 +1292,85 @@ function updateServerstats() { },{capabilities:true}); } +function buildstreamembed(streamName,embedbase) { + $('#liststreams .button.current').removeClass('current') + $('#liststreams .button').filter(function(){ + return $(this).text() == streamName; + }).addClass('current'); + + $('#subpage').append( + $('<div>').addClass('input_container').html( + $('<label>').text('The info embed URL is:').append( + $('<input>').attr('type','text').attr('readonly','readonly').val(embedbase+'info_'+streamName+'.js') + ) + ).append( + $('<label>').text('The embed URL is:').append( + $('<input>').attr('type','text').attr('readonly','readonly').val(embedbase+'embed_'+streamName+'.js') + ) + ).append( + $('<label>').text('The embed code is:').css('overflow','hidden').append( + $('<textarea>').val('<div>\n <script src="'+embedbase+'embed_'+streamName+'.js"></' + 'script>\n</div>') + ) + ) + ).append( + $('<span>').attr('id','listprotocols').text('Loading..') + ).append( + $('<p>').text('Preview:') + ).append( + $('<div>').attr('id','preview-container') + ); + + // jQuery doesn't work -> use DOM magic + var script = document.createElement('script'); + script.src = embedbase+'embed_'+streamName+'.js'; + script.onload = function(){ + var priority = mistvideo[streamName].source; + if (priority.length > 0) { + priority.sort(function(a,b){ + return b.priority - a.priority; + }); + var $table = $('<table>').html( + $('<tr>').html( + $('<th>').text('URL') + ).append( + $('<th>').text('Type') + ).append( + $('<th>').text('Priority') + ) + ); + for (var i in priority) { + $table.append( + $('<tr>').html( + $('<td>').text(priority[i].url) + ).append( + $('<td>').text(priority[i].type) + ).append( + $('<td>').addClass('align-center').text(priority[i].priority) + ) + ); + } + $('#listprotocols').html($table); + } + else { + $('#listprotocols').html('No data in info embed file.'); + } + } + document.getElementById('preview-container').appendChild( script ); +} + $(function(){ - $('#menu div.button').click(function(){ - if ((settings.settings.LTS != 1) && ($(this).hasClass('LTS-only'))) { return; } + $('#logo > a').click(function(){ + if ($.isEmptyObject(settings.settings)) { + showTab('login') + } + else { + showTab('overview'); + } + }); + $('#menu div.button').click(function(e){ + //if ((settings.settings.LTS != 1) && ($(this).hasClass('LTS-only'))) { return; } showTab($(this).text().toLowerCase()); + e.stopPropagation(); }) $('body').on('keydown',function(e){ switch (e.which) { @@ -1101,6 +1449,41 @@ $(function(){ $(this).val(v); this.setSelectionRange(curpos,curpos); }); + + $('.expandbutton').click(function(){ + $(this).toggleClass('active'); + }); + + + $('#ih-button').click(function(){ + if (ih) { + $('.ih-balloon').remove(); + } + else { + getWikiData('/wiki/Integrated_Help',function(data){ + settings.ih = { + raw: data.find('#mw-content-text').contents(), + pages: {} + } + settings.ih.raw.filter('.page[data-pagename]').each(function(){ + var pagename = $(this).attr('data-pagename').replace(' ','_'); + settings.ih.pages[pagename] = { + raw: $(this).contents(), + pageinfo: $(this).find('.page-description').html(), + inputs: {} + } + $(this).children('.input-description[data-inputid]').each(function(){ + settings.ih.pages[pagename].inputs[$(this).attr('data-inputid')] = $(this).html(); + }); + }); + consolelog('New integrated help data:',settings.ih); + ihAddBalloons(); + }); + } + ih = !ih; + $(this).toggleClass('active'); + }); + }); $(window).on('hashchange', function(e) { diff --git a/lsp/pages.js b/lsp/pages.js index b43c4296..424a1469 100755 --- a/lsp/pages.js +++ b/lsp/pages.js @@ -6,6 +6,7 @@ var defaults = { }; function showTab(tabName,streamName) { + settings.currentpage = tabName.replace(' ','_'); ignoreHashChange = true; location.hash = location.hash.split('&')[0]+'&'+tabName+(streamName ? '@'+streamName : ''); @@ -14,9 +15,10 @@ function showTab(tabName,streamName) { $('#menu .button').removeClass('current').filter(function(i){ return $(this).text().toLowerCase() == tabName; - }).addClass('current'); + }).addClass('current').parents('.expandbutton').addClass('active'); $('#page').html(''); + $('#tooltip').remove(); clearInterval(theInterval); $('#menu').css('visibility', 'visible'); @@ -121,6 +123,10 @@ function showTab(tabName,streamName) { saveAndReload('overview'); }) + ).append( + $('<button>').text('Cancel').addClass('escape-to-cancel').click(function(){ + showTab('login'); + }) ) ); break; @@ -434,8 +440,12 @@ function showTab(tabName,streamName) { $('<td>').attr('id','status-of-'+index).html(formatStatus(theStream)) ).append( $('<td>').html( - $('<button>').text('Embed').click(function(){ - showTab('embed',$(this).parent().parent().data('stream')) + $('<button>').text('Preview').click(function(){ + showTab('preview',$(this).parent().parent().data('stream')) + }) + ).append( + $('<button>').text('Info').click(function(){ + showTab('streaminfo',$(this).parent().parent().data('stream')) }) ) ).append( @@ -497,7 +507,7 @@ function showTab(tabName,streamName) { $('<label>').text('Buffer time:').addClass('live-only').attr('for','settings-streams-'+streamName+'-DVR').append( $('<span>').addClass('unit').text('[ms]') ).append( - $('<input>').attr('type','text').attr('id','settings-streams-'+streamName+'-DVR').attr('placeholder','2 keyframes').addClass('isSetting').addClass('').addClass('validate-positive-integer') + $('<input>').attr('type','text').attr('id','settings-streams-'+streamName+'-DVR').attr('placeholder','30000').addClass('isSetting').addClass('').addClass('validate-positive-integer') ) ).append( $('<label>').text('Record to:').addClass('live-only').addClass('LTS-only').attr('for','settings-streams-'+streamName+'-record').attr('title','The path to the file to record to. Leave this field blank if you do not wish to record to file.').append( @@ -516,7 +526,7 @@ function showTab(tabName,streamName) { $('<p>').text('Encrypt this stream') ).append( $('<div>').addClass('description').text( - 'To enable encryption, the Licene Acquisition URL must be entered, as well as either the content key or the key ID and seed.' + 'To enable encryption, the Licence Acquisition URL must be entered, as well as either the content key or the key ID and seed.' ) ).append( $('<label>').text('Licence Acquisition URL:').attr('for','settings-streams-'+streamName+'-la_url').append( @@ -578,7 +588,131 @@ function showTab(tabName,streamName) { }) break; - case 'embed': + case 'streaminfo': + var meta = settings.settings.streams[streamName].meta; + if (!meta) { + $('#page').html('No info available for stream "'+streamName+'".'); + } + else { + $meta = $('<table>').css('width','auto'); + if (meta.live) { + $meta.html( + $('<tr>').html( + $('<td>').text('Type:') + ).append( + $('<td>').text('Live') + ) + ); + } + else { + $meta.html( + $('<tr>').html( + $('<td>').text('Type:') + ).append( + $('<td>').text('Pre-recorded (VoD)') + ) + ); + } + for (var index in meta.tracks) { + var track = meta.tracks[index]; + if (track.type == '') { continue; } + var $table = $('<table>').html( + $('<tr>').html( + $('<td>').text('Type:') + ).append( + $('<td>').text(capFirstChar(track.type)) + ) + ).append( + $('<tr>').html( + $('<td>').text('Codec:') + ).append( + $('<td>').text(track.codec) + ) + ).append( + $('<tr>').html( + $('<td>').text('Duration:') + ).append( + $('<td>').html( + formatDuration(track.lastms-track.firstms)+'<br>(from '+formatDuration(track.firstms)+' to '+formatDuration(track.lastms)+')' + ) + ) + ).append( + $('<tr>').html( + $('<td>').text('Average bitrate:') + ).append( + $('<td>').text(Math.round(track.bps/1024)+' KiB/s') + ) + ); + + if (track.height) { + $table.append( + $('<tr>').html( + $('<td>').text('Size:') + ).append( + $('<td>').text(track.width+'x'+track.height+' px') + ) + ); + } + if (track.fpks) { + $table.append( + $('<tr>').html( + $('<td>').text('Framerate:') + ).append( + $('<td>').text(track.fpks/1000+' fps') + ) + ); + } + if (track.channels) { + $table.append( + $('<tr>').html( + $('<td>').text('Channels:') + ).append( + $('<td>').text(track.channels) + ) + ); + } + if (track.rate) { + $table.append( + $('<tr>').html( + $('<td>').text('Samplerate:') + ).append( + $('<td>').text(seperateThousands(track.rate,' ')+' Hz') + ) + ); + } + + $meta.append( + $('<tr>').html( + $('<td>').text(capFirstChar(index)+':') + ).append( + $('<td>').html( + $table + ) + ) + ); + } + + $('#page').html( + $('<p>').text('Detailed information about stream "'+streamName+'"') + ).append( + $('<div>').css({'width':'100%','display':'table','table-layout':'fixed','min-height':'300px'}).html( + $('<div>').css('display','table-row').html( + $('<div>').attr('id','info-stream-meta').css({'display':'table-cell','max-width':'50%','overflow':'auto'}).html( + $meta + ) + ).append( + $('<div>').attr('id','info-stream-statistics').css({'display':'table-cell','text-align':'center','min-height':'200px'}) + ) + ) + ); + } + $('#page').append( + $('<button>').text('Back').addClass('escape-to-cancel').click(function(){ + showTab('streams'); + }) + ); + break; + case 'preview': var httpConnector = false; for (var index in settings.settings.config.protocols) { if ((settings.settings.config.protocols[index].connector == 'HTTP') || (settings.settings.config.protocols[index].connector == 'HTTP.exe')) { @@ -586,37 +720,29 @@ function showTab(tabName,streamName) { } } if (httpConnector) { - var embedbase = 'http://'+parseURL(settings.server).host+':'+(httpConnector.port ? httpConnector.port : 8080)+'/'; $('#page').html( - $('<div>').addClass('input_container').html( - $('<p>').text('Embed info for stream "'+streamName+'"') - ).append( - $('<label>').text('The info embed URL is:').append( - $('<input>').attr('type','text').attr('readonly','readonly').val(embedbase+'info_'+streamName+'.js') + $('<div>').addClass('table').html( + $('<div>').addClass('row').html( + $('<div>').addClass('cell').attr('id','liststreams').addClass('menu') + ).append( + $('<div>').addClass('cell').attr('id','subpage').css('padding-left','1em') ) - ).append( - $('<label>').text('The embed URL is:').append( - $('<input>').attr('type','text').attr('readonly','readonly').val(embedbase+'embed_'+streamName+'.js') - ) - ).append( - $('<label>').text('The embed code is:').css('overflow','hidden').append( - $('<textarea>').val('<div>\n <script src="'+embedbase+'embed_'+streamName+'.js"></' + 'script>\n</div>') - ) - ).append( - $('<button>').text('Back').addClass('escape-to-cancel').click(function(){ - showTab('streams'); - }) ) - ).append( - $('<p>').text('Preview:') - ).append( - $('<div>').attr('id','preview-container') ); + var embedbase = 'http://'+parseURL(settings.server).host+':'+(httpConnector.port ? httpConnector.port : 8080)+'/'; - // jQuery doesn't work -> use DOM magic - var script = document.createElement('script'); - script.src = embedbase+'embed_'+streamName+'.js'; - document.getElementById('preview-container').appendChild( script ); + for (var s in settings.settings.streams) { + if (!streamName) { + streamName = s; + } + $('#liststreams').append( + $('<div>').addClass('button').text(settings.settings.streams[s].name).click(function(){ + buildstreamembed($(this).text()); + }) + ); + } + + buildstreamembed(streamName,embedbase); } else { $('#page').html( @@ -627,29 +753,31 @@ function showTab(tabName,streamName) { case 'limits': var $tbody = $('<tbody>'); $('#page').html( - $('<div>').addClass('description').text('This is an overview of the limits that have been configured on MistServer.') - ).append( - $('<table>').html( - $('<thead>').html( - $('<tr>').html( - $('<th>').text('Applies to') - ).append( - $('<th>').text('Type') - ).append( - $('<th>').text('Name') - ).append( - $('<th>').text('Value') - ).append( - $('<th>') + $('<div>').addClass('LTS-only').html( + $('<div>').addClass('description').text('This is an overview of the limits that have been configured on MistServer.') + ).append( + $('<table>').html( + $('<thead>').html( + $('<tr>').html( + $('<th>').text('Applies to') + ).append( + $('<th>').text('Type') + ).append( + $('<th>').text('Name') + ).append( + $('<th>').text('Value') + ).append( + $('<th>') + ) ) + ).append( + $tbody ) ).append( - $tbody + $('<button>').text('New').click(function(){ + showTab('edit limit','_new_'); + }) ) - ).append( - $('<button>').text('New').click(function(){ - showTab('edit limit','_new_'); - }) ); for (var index in settings.settings.config.limits) { @@ -1041,6 +1169,551 @@ function showTab(tabName,streamName) { } $('#logs-refresh-every').val(defaults.logRefreshing[1]); break; + case 'statistics': + var graphs = {}; + var plot; + $('#page').html( + $('<div>').addClass('description').text('Here, you can select all kinds of data, and view them in a graph.') + ).append( + $('<div>').addClass('input_container').html( + $('<p>').text('Select the data to display') + ).append( + $('<label>').text('Add to graph:').append( + $('<select>').attr('id','graphid').html( + $('<option>').text('New graph').val('new') + ).change(function(){ + if ($(this).val() == 'new') { + $('#graphtype').removeAttr('disabled'); + } + else { + $('#graphtype').attr('disabled','disabled'); + //set to correct type + } + }) + ) + ).append( + $('<label>').text('Graph x-axis type:').append( + $('<select>').attr('id','graphtype').html( + $('<option>').text('Time line').val('time') + ).append( + $('<option>').text('Map').val('coords') + ).change(function(){ + $('#dataset option').hide(); + $('#dataset option.axis_'+$(this).val()).show(); + $('#dataset').val( $('#dataset option.axis_'+$(this).val()).first().val()); + }) + ) + ).append( + $('<label>').text('Select data set:').append( + $('<select>').attr('id','dataset').html( + $('<option>').text('Viewers').val('clients').addClass('axis_time') + ).append( + $('<option>').text('Bandwidth (up)').val('upbps').addClass('axis_time') + ).append( + $('<option>').text('Bandwidth (down)').val('downbps').addClass('axis_time') + ).append( + $('<option>').text('% CPU').val('cpuload').addClass('axis_time') + ).append( + $('<option>').text('Memory load').val('memload').addClass('axis_time') + ).append( + $('<option>').text('Viewer location').val('coords').addClass('axis_coords') + ).change(function(){ + switch ($(this).val()) { + case 'clients': + case 'upbps': + case 'downbps': + $('#dataset-details .replace-dataset').text('amount of viewers') + $('#dataset-details').show(); + break; + default: + $('#dataset-details').hide(); + } + }) + ) + ).append( + $('<div>').attr('id','dataset-details').addClass('checklist').css({ + 'padding':'0.5em 0 0 40%', + 'font-size':'0.9em' + }).html('Show <span class=replace-dataset></span> for:').append( + $('<label>').text('The total').prepend( + $('<input>').attr('type','radio').attr('name','cumutype').attr('checked','checked').val('all') + ) + ).append( + $('<label>').text('The stream ').append( + $('<select>').addClass('stream cumuval') + ).prepend( + $('<input>').attr('type','radio').attr('name','cumutype').val('stream') + ) + ).append( + $('<label>').text('The protocol ').append( + $('<select>').addClass('protocol cumuval') + ).prepend( + $('<input>').attr('type','radio').attr('name','cumutype').val('protocol') + ) + ) + ).append( + $('<button>').text('Add data set').click(function(){ + //the graph + if ($('#graphid').val() == 'new') { + var graph = {}; + graph.id = $('#graphid').val(); + graph.type = $('#graphtype').val(); + graph.id = 'graph_'+($('#graphcontainer .graph').length+1); + graph.datasets = []; + graphs[graph.id] = graph; + $('#graphcontainer').append( + $('<div>').attr('id',graph.id).addClass('graph-item').html( + $('<div>').addClass('legend') + ).append( + $('<div>').addClass('graph') + ) + ); + $('#graphid').append( + $('<option>').text(graph.id) + ).val(graph.id).trigger('change'); + } + else { + var graph = graphs[$('#graphid').val()]; + } + //the dataset itself + var d = { + display: true, + type: $('#dataset').val(), + label: '', + yaxistype: 'amount', + data: [], + lines: { show: true }, + points: { show: false } + }; + switch (d.type) { + case 'cpuload': + d.label = 'CPU load'; + d.yaxistype = 'percentage'; + break; + case 'memload': + d.label = 'Memory load'; + d.yaxistype = 'percentage'; + break; + case 'upbps': + case 'downbps': + case 'clients': + d.cumutype = $('#dataset-details input[name=cumutype]:checked').val(); + d.yaxistype = 'bytespersec'; + if (d.cumutype == 'all') { + switch (d.type) { + case 'clients': + d.label = 'Total viewers'; + d.yaxistype = 'amount'; + break; + case 'upbps': + d.label = 'Total bandwidth (up)'; + break; + case 'downbps': + d.label = 'Total bandwidth (down)'; + break; + } + } + else { + var which = $('#dataset-details.cumuval.'+d.cumutype).val(); + if (d.cumutype == 'stream') { + d.stream = which; + } + else if (d.cumutype == 'protocol') { + d.protocol = which; + } + switch (d.type) { + case 'clients': + d.label = 'Viewers ('+d.stream+')'; + d.yaxistype = 'amount'; + break; + case 'upbps': + d.label = 'Bandwidth (up) ('+d.stream+')'; + break; + case 'downbps': + d.label = 'Bandwidth (down) ('+d.stream+')'; + break; + } + } + break; + } + graph.datasets.push(d); + getPlotData(); + }) + )/*.append( + $('<p>').text('Switch data display type').css('clear','both') + ).append( + $('<label>').text('Show data in a:').append( + $('<select>').html( + $('<option>').text('graph') + ).append( + $('<option>').text('table') + ) + ) + )*/ + ).append( + $('<div>').attr('id','graphcontainer') + ); + for (var i in settings.settings.streams) { + $('#dataset-details .cumuval.stream').append( + $('<option>').text(settings.settings.streams[i].name).val(i) + ); + } + for (var i in settings.settings.config.protocols) { + $('#dataset-details .cumuval.protocol').append( + $('<option>').text(settings.settings.config.protocols[i].connector) + ); + } + $('#graphtype').trigger('change'); + + var lastitem = null; + var $tooltip = $('<div>').attr('id','tooltip'); + $('body').append($tooltip); + $('.graph').live('plothover',function(e,pos,item){ + if (item) { + var pos; + if (item.pageX > ($(window).width() / 2)) { + pos.left = 'auto'; + pos.right = $(window).width() - item.pageX + 8+'px'; + } + else { + pos.left = item.pageX + 8+'px'; + pos.right = 'auto'; + } + if (item.pageY > ($(window).height() / 2)) { + pos.top = 'auto'; + pos.bottom = $(window).height() - item.pageY + 8+'px'; + } + else { + pos.top = item.pageY + 8+'px'; + pos.bottom = 'auto'; + } + $tooltip.css({ + 'left': pos.left, + 'top': pos.top, + 'right': pos.right, + 'bottom': pos.bottom + }).html( + $('<p>').text(item.series.label).prepend( + $('<div>').css({ + 'background-color': item.series.color, + 'width': '20px', + 'height': '20px', + 'display': 'inline-block', + 'margin': '0 0.5em' + }) + ) + ).append( + $('<table>').html( + $('<tr>').html( + $('<td>').text('Time:') + ).append( + $('<td>').text(item.series.xaxis.tickFormatter(item.datapoint[0],item.series.xaxis)) + ) + ).append( + $('<tr>').html( + $('<td>').text(item.series.label+':') + ).append( + $('<td>').text(item.series.yaxis.tickFormatter(item.datapoint[1],item.series.yaxis)) + ) + ) + ).fadeIn(); + } + else { + $('#tooltip').hide(); + } + }); + + + theInterval = setInterval(function(){ + getPlotData(); + },10000); + + function getPlotData() { + getData(function(data){ + for (var j in graphs) { + for (var i in graphs[j].datasets) { + graphs[j].datasets[i] = findDataset(graphs[j].datasets[i],data); + } + drawGraph(graphs[j]); + } + },{capabilities:true,totals:{}}); + } + + function findDataset(dataobj,sourcedata) { + var now = sourcedata.config.time; + switch (dataobj.type) { + case 'cpuload': + //remove any data older than 10 minutes + var removebefore = false; + for (var i in dataobj.data) { + if (dataobj.data[i][0] < (now-600)*1000) { + removebefore = Number(i)+1; + } + } + if (removebefore !== false) { + dataobj.data.splice(0,removebefore); + } + dataobj.data.push([now*1000,sourcedata.capabilities.load.one]); + break; + case 'memload': + //remove any data older than 10 minutes + var removebefore = false; + for (var i in dataobj.data) { + if (dataobj.data[i][0] < (now-600)*1000) { + removebefore = Number(i)+1; + } + } + if (removebefore !== false) { + dataobj.data.splice(0,removebefore); + } + dataobj.data.push([now*1000,sourcedata.capabilities.load.memory]); + break; + case 'upbps': + case 'downbps': + case 'clients': + //todo: depending on the stream.. + if (!sourcedata.totals || !sourcedata.totals.data) { + dataobj.data.push([(now-600)*1000,0]); + dataobj.data.push([now*1000,0]); + } + else { + var fields = {}; + for (var index in sourcedata.totals.fields) { + fields[sourcedata.totals.fields[index]] = index; + } + var time = sourcedata.totals.start; + dataobj.data = []; + if (time > now-590) { + //prepend data with 0 + dataobj.data.push([(now-600)*1000,0]); + dataobj.data.push([time*1000-1,0]); + } + var index = 0; + dataobj.data.push([[time*1000,sourcedata.totals.data[index][fields[dataobj.type]]]]); + for (var i in sourcedata.totals.interval) { + if ((i % 2) == 1) { + //fill gaps with 0 + time += sourcedata.totals.interval[i][1]; + dataobj.data.push([time*1000,0]); + } + else { + for (var j = 0; j < sourcedata.totals.interval[i][0]; j++) { + time += sourcedata.totals.interval[i][1]; + index++; + dataobj.data.push([time*1000,sourcedata.totals.data[index][fields[dataobj.type]]]); + } + if (i < sourcedata.totals.interval.length-1) { + dataobj.data.push([time*1000+1,0]); + } + } + } + if (now > time + 10) { + //append data with 0 + dataobj.data.push([time*1000+1,0]); + dataobj.data.push([now*1000,0]); + } + } + break; + } + + + return dataobj; + } + + function drawGraph(graph){ + var datasets = graph.datasets; + if (datasets.length < 1) { + $('#'+graph.id).children('.graph,.legend').html(''); + return; + } + var yaxes = []; + var yaxesTemplates = { + percentage: { + name: 'percentage', + color: 'black', + display: false, + tickColor: 0, + tickDecimals: 0, + tickFormatter: function(val,axis){ + return val.toFixed(axis.tickDecimals) + '%'; + }, + tickLength: 0, + min: 0 + }, + amount: { + name: 'amount', + color: 'black', + display: false, + tickColor: 0, + tickDecimals: 0, + tickFormatter: function(val,axis){ + return seperateThousands(val.toFixed(axis.tickDecimals),' '); + }, + tickLength: 0, + min: 0 + }, + bytespersec: { + name: 'bytespersec', + color: 'black', + display: false, + tickColor: 0, + tickDecimals: 1, + tickFormatter: function(val,axis){ + var suffix = ['bytes','KiB','MiB','GiB','TiB','PiB']; + if (val == 0) { + val = val+' '+suffix[0]; + } + else { + var exponent = Math.floor(Math.log(Math.abs(val)) / Math.log(1024)); + if (exponent < 0) { + val = val.toFixed(axis.tickDecimals)+' '+suffix[0]; + } + else { + val = Math.round(val / Math.pow(1024,exponent) * Math.pow(10,axis.tickDecimals)) / Math.pow(10,axis.tickDecimals) +' '+suffix[exponent]; + } + } + return val + '/s'; + }, + tickLength: 0, + ticks: function(axis,a,b,c,d){ + //taken from flot source code (function setupTickGeneration), + //modified to think in multiples of 1024 by Carina van der Meer for DDVTECH + + // heuristic based on the model a*sqrt(x) fitted to + // some data points that seemed reasonable + var noTicks = 0.3 * Math.sqrt($('.graph').first().height()); + + var delta = (axis.max - axis.min) / noTicks, + exponent = Math.floor(Math.log(Math.abs(delta)) / Math.log(1024)), + correcteddelta = delta / Math.pow(1024,exponent), + dec = -Math.floor(Math.log(correcteddelta) / Math.LN10), + maxDec = axis.tickDecimals; + + if (maxDec != null && dec > maxDec) { + dec = maxDec; + } + + var magn = Math.pow(10, -dec), + norm = correcteddelta / magn, // norm is between 1.0 and 10.0 + size; + + if (norm < 1.5) { + size = 1; + } else if (norm < 3) { + size = 2; + // special case for 2.5, requires an extra decimal + if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) { + size = 2.5; + ++dec; + } + } else if (norm < 7.5) { + size = 5; + } else { + size = 10; + } + + size *= magn; + size = size * Math.pow(1024,exponent); + + if (axis.minTickSize != null && size < axis.minTickSize) { + size = axis.minTickSize; + } + + axis.delta = delta; + axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); + axis.tickSize = size; + + var ticks = [], + start = axis.tickSize * Math.floor(axis.min / axis.tickSize), + i = 0, + v = Number.NaN, + prev; + + do { + prev = v; + v = start + i * axis.tickSize; + ticks.push(v); + ++i; + } while (v < axis.max && v != prev); + return ticks; + }, + min: 0 + } + }; + var xaxistemplates = { + time: { + name: 'time', + mode: 'time', + timezone: 'browser', + ticks: 5 + } + } + var plotsets = []; + for (var i in datasets) { + if (datasets[i].display) { + if (yaxesTemplates[datasets[i].yaxistype].display === false) { + yaxes.push(yaxesTemplates[datasets[i].yaxistype]); + yaxesTemplates[datasets[i].yaxistype].display = yaxes.length; + } + datasets[i].yaxis = yaxesTemplates[datasets[i].yaxistype].display; + datasets[i].color = Number(i); + plotsets.push(datasets[i]); + } + } + if (yaxes[0]) { yaxes[0].color = 0; } + plot = $.plot( + $('#'+graph.id+' .graph'), + plotsets, + { + legend: {show: false}, + xaxis: xaxistemplates[graph.type], + yaxes: yaxes, + grid: { + hoverable: true, + borderWidth: {top: 0, right: 0, bottom: 1, left: 1}, + color: 'black', + backgroundColor: {colors: ['#fff','#ededed']} + } + } + ); + $('#'+graph.id+' .legend').html( + $('<div>').addClass('legend-list').addClass('checklist') + ); + var plotdata = plot.getOptions(); + for (var i in datasets) { + var $checkbox = $('<input>').attr('type','checkbox').data('dataset-index',i).click(function(){ + if ($(this).is(':checked')) { + datasets[$(this).data('dataset-index')].display = true; + } + else { + datasets[$(this).data('dataset-index')].display = false; + } + drawGraph($(this).parents('.graph-item')); + }); + if (datasets[i].display) { + $checkbox.attr('checked','checked'); + } + $('#'+graph.id+' .legend-list').append( + $('<label>').html( + $checkbox + ).append( + $('<div>').addClass('series-color').css('background-color',plotdata.colors[datasets[i].color % plotdata.colors.length]) + ).append( + datasets[i].label + ) + ); + } + if (datasets.length > 0) { + $('#'+graph.id+' .legend').append( + $('<button>').text('Clear all').click(function(){ + var graph = graphs[$(this).parents('.graph-item').attr('id')]; + graph.datasets = []; + drawGraph(graph); + }).css({'float':'none'}) + ); + } + } + break; case 'server stats': var $cont = $('<div>').addClass('input_container'); @@ -1094,6 +1767,55 @@ function showTab(tabName,streamName) { $('#page').html($cont); break; + case 'email for help': + var config = $.extend({},settings.settings); + delete config.statistics; + config = JSON.stringify(config); + $('#page').html( + $('<div>').addClass('description').html( + 'You can use this form to email MistServer support if you\'re having difficulties.<br>' + ).append( + 'A copy of your server config file will automatically be included.' + ) + ).append( + $('<div>').addClass('input_container').html( + $('<form>').html( + $('<label>').text('Your name:').append( + $('<input>').attr('type','text').attr('name','name') + ) + ).append( + $('<input>').attr('type','hidden').attr('name','company').val('-') + ).append( + $('<label>').text('Your email address:').append( + $('<input>').attr('type','email').attr('name','email') + ) + ).append( + $('<input>').attr('type','hidden').attr('name','subject').val('Integrated Help') + ).append( + $('<label>').text('Your message:').append( + $('<textarea>').attr('name','message').height('20em') + ) + ).append( + $('<label>').text('Your config file:').append( + $('<textarea>').attr('name','configfile').attr('readonly','readonly').css({'height':'20em','font-size':'0.7em'}).val(config) + ) + ).append( + $('<button>').text('Send').click(function(e){ + var data = $(this).parents('form').serialize(); + $.ajax({ + type: 'POST', + url: 'http://mistserver.org/contact_us?skin=plain', + data: data, + success: function(d) { + $('#page').html(d); + } + }); + e.preventDefault(); + }) + ) + ) + ); + break; case 'disconnect': showTab('login'); $('#connection').addClass('red').removeClass('green').text('Disconnected'); @@ -1110,17 +1832,32 @@ function showTab(tabName,streamName) { if ((settings.credentials.authstring) && (!settings.settings.LTS)) { $('.LTS-only input').add('.LTS-only select').add('.LTS-only button').attr('disabled','disabled'); - $('.LTS-only, .LTS-only p, .LTS-only label, .LTS-only button ').css('color','#b4b4b4'); + //$('.LTS-only, .LTS-only p, .LTS-only label, .LTS-only button').css('color','#b4b4b4'); $('.LTS-only, .LTS-only > *').filter(':not(.LTSstuff_done)').each(function(){ var t = []; if ($(this).attr('title')) { t.push($(this).attr('title')); } - t.push('This is feature is only available in the LTS version.'); + t.push('This feature is only available in the LTS version.'); $(this).attr('title',t.join(' ')).addClass('LTSstuff_done'); }); + $('#page .LTS-only').prepend( + $('<a>').text('Upgrade to LTS').attr('target','_blank').attr('href','http://mistserver.org/products/MistServer LTS').addClass('fakebutton') + ); + + $('.linktoReleaseNotes.notedited').each(function(){ + $(this).attr('href',$(this).attr('href')+'/'+settings.settings.config.version.split('-')[0]).removeClass('.notedited'); + }); } else if (settings.settings.LTS) { $('.LTS-only').removeClass('LTS-only'); + $('.linktoTnC.notLTSlink').attr('href','http://mistserver.org/wiki/MistServerLTS_license').removeClass('notLTSlink'); + $('.linktoReleaseNotes.notedited').each(function(){ + $(this).attr('href',$(this).attr('href')+'/'+settings.settings.config.version.split('-')[0]+'LTS').removeClass('.notedited'); + }); + } + + if (ih) { + ihAddBalloons(); } } diff --git a/lsp/plugins/jquery.flot.crosshair.min.js b/lsp/plugins/jquery.flot.crosshair.min.js new file mode 100644 index 00000000..f97ce65a --- /dev/null +++ b/lsp/plugins/jquery.flot.crosshair.min.js @@ -0,0 +1 @@ +(function($){var options={crosshair:{mode:null,color:"rgba(170, 0, 0, 0.80)",lineWidth:1}};function init(plot){var crosshair={x:-1,y:-1,locked:false};plot.setCrosshair=function setCrosshair(pos){if(!pos)crosshair.x=-1;else{var o=plot.p2c(pos);crosshair.x=Math.max(0,Math.min(o.left,plot.width()));crosshair.y=Math.max(0,Math.min(o.top,plot.height()))}plot.triggerRedrawOverlay()};plot.clearCrosshair=plot.setCrosshair;plot.lockCrosshair=function lockCrosshair(pos){if(pos)plot.setCrosshair(pos);crosshair.locked=true};plot.unlockCrosshair=function unlockCrosshair(){crosshair.locked=false};function onMouseOut(e){if(crosshair.locked)return;if(crosshair.x!=-1){crosshair.x=-1;plot.triggerRedrawOverlay()}}function onMouseMove(e){if(crosshair.locked)return;if(plot.getSelection&&plot.getSelection()){crosshair.x=-1;return}var offset=plot.offset();crosshair.x=Math.max(0,Math.min(e.pageX-offset.left,plot.width()));crosshair.y=Math.max(0,Math.min(e.pageY-offset.top,plot.height()));plot.triggerRedrawOverlay()}plot.hooks.bindEvents.push(function(plot,eventHolder){if(!plot.getOptions().crosshair.mode)return;eventHolder.mouseout(onMouseOut);eventHolder.mousemove(onMouseMove)});plot.hooks.drawOverlay.push(function(plot,ctx){var c=plot.getOptions().crosshair;if(!c.mode)return;var plotOffset=plot.getPlotOffset();ctx.save();ctx.translate(plotOffset.left,plotOffset.top);if(crosshair.x!=-1){var adj=plot.getOptions().crosshair.lineWidth%2===0?0:.5;ctx.strokeStyle=c.color;ctx.lineWidth=c.lineWidth;ctx.lineJoin="round";ctx.beginPath();if(c.mode.indexOf("x")!=-1){var drawX=Math.round(crosshair.x)+adj;ctx.moveTo(drawX,0);ctx.lineTo(drawX,plot.height())}if(c.mode.indexOf("y")!=-1){var drawY=Math.round(crosshair.y)+adj;ctx.moveTo(0,drawY);ctx.lineTo(plot.width(),drawY)}ctx.stroke()}ctx.restore()});plot.hooks.shutdown.push(function(plot,eventHolder){eventHolder.unbind("mouseout",onMouseOut);eventHolder.unbind("mousemove",onMouseMove)})}$.plot.plugins.push({init:init,options:options,name:"crosshair",version:"1.0"})})(jQuery); \ No newline at end of file diff --git a/lsp/plugins/jquery.flot.min.js b/lsp/plugins/jquery.flot.min.js new file mode 100644 index 00000000..9620fc00 --- /dev/null +++ b/lsp/plugins/jquery.flot.min.js @@ -0,0 +1,2 @@ +(function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i<c.length;++i)o[c.charAt(i)]+=d;return o.normalize()};o.scale=function(c,f){for(var i=0;i<c.length;++i)o[c.charAt(i)]*=f;return o.normalize()};o.toString=function(){if(o.a>=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return value<min?min:value>max?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);(function($){var hasOwnProperty=Object.prototype.hasOwnProperty;function Canvas(cls,container){var element=container.children("."+cls)[0];if(element==null){element=document.createElement("canvas");element.className=cls;$(element).css({direction:"ltr",position:"absolute",left:0,top:0}).appendTo(container);if(!element.getContext){if(window.G_vmlCanvasManager){element=window.G_vmlCanvasManager.initElement(element)}else{throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.")}}}this.element=element;var context=this.context=element.getContext("2d");var devicePixelRatio=window.devicePixelRatio||1,backingStoreRatio=context.webkitBackingStorePixelRatio||context.mozBackingStorePixelRatio||context.msBackingStorePixelRatio||context.oBackingStorePixelRatio||context.backingStorePixelRatio||1;this.pixelRatio=devicePixelRatio/backingStoreRatio;this.resize(container.width(),container.height());this.textContainer=null;this.text={};this._textCache={}}Canvas.prototype.resize=function(width,height){if(width<=0||height<=0){throw new Error("Invalid dimensions for plot, width = "+width+", height = "+height)}var element=this.element,context=this.context,pixelRatio=this.pixelRatio;if(this.width!=width){element.width=width*pixelRatio;element.style.width=width+"px";this.width=width}if(this.height!=height){element.height=height*pixelRatio;element.style.height=height+"px";this.height=height}context.restore();context.save();context.scale(pixelRatio,pixelRatio)};Canvas.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)};Canvas.prototype.render=function(){var cache=this._textCache;for(var layerKey in cache){if(hasOwnProperty.call(cache,layerKey)){var layer=this.getTextLayer(layerKey),layerCache=cache[layerKey];layer.hide();for(var styleKey in layerCache){if(hasOwnProperty.call(layerCache,styleKey)){var styleCache=layerCache[styleKey];for(var key in styleCache){if(hasOwnProperty.call(styleCache,key)){var positions=styleCache[key].positions;for(var i=0,position;position=positions[i];i++){if(position.active){if(!position.rendered){layer.append(position.element);position.rendered=true}}else{positions.splice(i--,1);if(position.rendered){position.element.detach()}}}if(positions.length==0){delete styleCache[key]}}}}}layer.show()}}};Canvas.prototype.getTextLayer=function(classes){var layer=this.text[classes];if(layer==null){if(this.textContainer==null){this.textContainer=$("<div class='flot-text'></div>").css({position:"absolute",top:0,left:0,bottom:0,right:0,"font-size":"smaller",color:"#545454"}).insertAfter(this.element)}layer=this.text[classes]=$("<div></div>").addClass(classes).css({position:"absolute",top:0,left:0,bottom:0,right:0}).appendTo(this.textContainer)}return layer};Canvas.prototype.getTextInfo=function(layer,text,font,angle,width){var textStyle,layerCache,styleCache,info;text=""+text;if(typeof font==="object"){textStyle=font.style+" "+font.variant+" "+font.weight+" "+font.size+"px/"+font.lineHeight+"px "+font.family}else{textStyle=font}layerCache=this._textCache[layer];if(layerCache==null){layerCache=this._textCache[layer]={}}styleCache=layerCache[textStyle];if(styleCache==null){styleCache=layerCache[textStyle]={}}info=styleCache[text];if(info==null){var element=$("<div></div>").html(text).css({position:"absolute","max-width":width,top:-9999}).appendTo(this.getTextLayer(layer));if(typeof font==="object"){element.css({font:textStyle,color:font.color})}else if(typeof font==="string"){element.addClass(font)}info=styleCache[text]={width:element.outerWidth(true),height:element.outerHeight(true),element:element,positions:[]};element.detach()}return info};Canvas.prototype.addText=function(layer,x,y,text,font,angle,width,halign,valign){var info=this.getTextInfo(layer,text,font,angle,width),positions=info.positions;if(halign=="center"){x-=info.width/2}else if(halign=="right"){x-=info.width}if(valign=="middle"){y-=info.height/2}else if(valign=="bottom"){y-=info.height}for(var i=0,position;position=positions[i];i++){if(position.x==x&&position.y==y){position.active=true;return}}position={active:true,rendered:false,element:positions.length?info.element.clone():info.element,x:x,y:y};positions.push(position);position.element.css({top:Math.round(y),left:Math.round(x),"text-align":halign})};Canvas.prototype.removeText=function(layer,x,y,text,font,angle){if(text==null){var layerCache=this._textCache[layer];if(layerCache!=null){for(var styleKey in layerCache){if(hasOwnProperty.call(layerCache,styleKey)){var styleCache=layerCache[styleKey];for(var key in styleCache){if(hasOwnProperty.call(styleCache,key)){var positions=styleCache[key].positions;for(var i=0,position;position=positions[i];i++){position.active=false}}}}}}}else{var positions=this.getTextInfo(layer,text,font,angle).positions;for(var i=0,position;position=positions[i];i++){if(position.x==x&&position.y==y){position.active=false}}}};function Plot(placeholder,data_,options_,plugins){var series=[],options={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:.85,sorted:null},xaxis:{show:null,position:"bottom",mode:null,font:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null},yaxis:{autoscaleMargin:.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left",horizontal:false,zero:true},shadowSize:3,highlightColor:null},grid:{show:true,aboveData:false,color:"#545454",backgroundColor:null,borderColor:null,tickColor:null,margin:0,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},interaction:{redrawOverlayInterval:1e3/60},hooks:{}},surface=null,overlay=null,eventHolder=null,ctx=null,octx=null,xaxes=[],yaxes=[],plotOffset={left:0,right:0,top:0,bottom:0},plotWidth=0,plotHeight=0,hooks={processOptions:[],processRawData:[],processDatapoints:[],processOffset:[],drawBackground:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},plot=this;plot.setData=setData;plot.setupGrid=setupGrid;plot.draw=draw;plot.getPlaceholder=function(){return placeholder};plot.getCanvas=function(){return surface.element};plot.getPlotOffset=function(){return plotOffset};plot.width=function(){return plotWidth};plot.height=function(){return plotHeight};plot.offset=function(){var o=eventHolder.offset();o.left+=plotOffset.left;o.top+=plotOffset.top;return o};plot.getData=function(){return series};plot.getAxes=function(){var res={},i;$.each(xaxes.concat(yaxes),function(_,axis){if(axis)res[axis.direction+(axis.n!=1?axis.n:"")+"axis"]=axis});return res};plot.getXAxes=function(){return xaxes};plot.getYAxes=function(){return yaxes};plot.c2p=canvasToAxisCoords;plot.p2c=axisToCanvasCoords;plot.getOptions=function(){return options};plot.highlight=highlight;plot.unhighlight=unhighlight;plot.triggerRedrawOverlay=triggerRedrawOverlay;plot.pointOffset=function(point){return{left:parseInt(xaxes[axisNumber(point,"x")-1].p2c(+point.x)+plotOffset.left,10),top:parseInt(yaxes[axisNumber(point,"y")-1].p2c(+point.y)+plotOffset.top,10)}};plot.shutdown=shutdown;plot.destroy=function(){shutdown();placeholder.removeData("plot").empty();series=[];options=null;surface=null;overlay=null;eventHolder=null;ctx=null;octx=null;xaxes=[];yaxes=[];hooks=null;highlights=[];plot=null};plot.resize=function(){var width=placeholder.width(),height=placeholder.height();surface.resize(width,height);overlay.resize(width,height)};plot.hooks=hooks;initPlugins(plot);parseOptions(options_);setupCanvases();setData(data_);setupGrid();draw();bindEvents();function executeHooks(hook,args){args=[plot].concat(args);for(var i=0;i<hook.length;++i)hook[i].apply(this,args)}function initPlugins(){var classes={Canvas:Canvas};for(var i=0;i<plugins.length;++i){var p=plugins[i];p.init(plot,classes);if(p.options)$.extend(true,options,p.options)}}function parseOptions(opts){$.extend(true,options,opts);if(opts&&opts.colors){options.colors=opts.colors}if(options.xaxis.color==null)options.xaxis.color=$.color.parse(options.grid.color).scale("a",.22).toString();if(options.yaxis.color==null)options.yaxis.color=$.color.parse(options.grid.color).scale("a",.22).toString();if(options.xaxis.tickColor==null)options.xaxis.tickColor=options.grid.tickColor||options.xaxis.color;if(options.yaxis.tickColor==null)options.yaxis.tickColor=options.grid.tickColor||options.yaxis.color;if(options.grid.borderColor==null)options.grid.borderColor=options.grid.color;if(options.grid.tickColor==null)options.grid.tickColor=$.color.parse(options.grid.color).scale("a",.22).toString();var i,axisOptions,axisCount,fontSize=placeholder.css("font-size"),fontSizeDefault=fontSize?+fontSize.replace("px",""):13,fontDefaults={style:placeholder.css("font-style"),size:Math.round(.8*fontSizeDefault),variant:placeholder.css("font-variant"),weight:placeholder.css("font-weight"),family:placeholder.css("font-family")};axisCount=options.xaxes.length||1;for(i=0;i<axisCount;++i){axisOptions=options.xaxes[i];if(axisOptions&&!axisOptions.tickColor){axisOptions.tickColor=axisOptions.color}axisOptions=$.extend(true,{},options.xaxis,axisOptions);options.xaxes[i]=axisOptions;if(axisOptions.font){axisOptions.font=$.extend({},fontDefaults,axisOptions.font);if(!axisOptions.font.color){axisOptions.font.color=axisOptions.color}if(!axisOptions.font.lineHeight){axisOptions.font.lineHeight=Math.round(axisOptions.font.size*1.15)}}}axisCount=options.yaxes.length||1;for(i=0;i<axisCount;++i){axisOptions=options.yaxes[i];if(axisOptions&&!axisOptions.tickColor){axisOptions.tickColor=axisOptions.color}axisOptions=$.extend(true,{},options.yaxis,axisOptions);options.yaxes[i]=axisOptions;if(axisOptions.font){axisOptions.font=$.extend({},fontDefaults,axisOptions.font);if(!axisOptions.font.color){axisOptions.font.color=axisOptions.color}if(!axisOptions.font.lineHeight){axisOptions.font.lineHeight=Math.round(axisOptions.font.size*1.15)}}}if(options.xaxis.noTicks&&options.xaxis.ticks==null)options.xaxis.ticks=options.xaxis.noTicks;if(options.yaxis.noTicks&&options.yaxis.ticks==null)options.yaxis.ticks=options.yaxis.noTicks;if(options.x2axis){options.xaxes[1]=$.extend(true,{},options.xaxis,options.x2axis);options.xaxes[1].position="top"}if(options.y2axis){options.yaxes[1]=$.extend(true,{},options.yaxis,options.y2axis);options.yaxes[1].position="right"}if(options.grid.coloredAreas)options.grid.markings=options.grid.coloredAreas;if(options.grid.coloredAreasColor)options.grid.markingsColor=options.grid.coloredAreasColor;if(options.lines)$.extend(true,options.series.lines,options.lines);if(options.points)$.extend(true,options.series.points,options.points);if(options.bars)$.extend(true,options.series.bars,options.bars);if(options.shadowSize!=null)options.series.shadowSize=options.shadowSize;if(options.highlightColor!=null)options.series.highlightColor=options.highlightColor;for(i=0;i<options.xaxes.length;++i)getOrCreateAxis(xaxes,i+1).options=options.xaxes[i];for(i=0;i<options.yaxes.length;++i)getOrCreateAxis(yaxes,i+1).options=options.yaxes[i];for(var n in hooks)if(options.hooks[n]&&options.hooks[n].length)hooks[n]=hooks[n].concat(options.hooks[n]);executeHooks(hooks.processOptions,[options])}function setData(d){series=parseData(d);fillInSeriesOptions();processData()}function parseData(d){var res=[];for(var i=0;i<d.length;++i){var s=$.extend(true,{},options.series);if(d[i].data!=null){s.data=d[i].data;delete d[i].data;$.extend(true,s,d[i]);d[i].data=s.data}else s.data=d[i];res.push(s)}return res}function axisNumber(obj,coord){var a=obj[coord+"axis"];if(typeof a=="object")a=a.n;if(typeof a!="number")a=1;return a}function allAxes(){return $.grep(xaxes.concat(yaxes),function(a){return a})}function canvasToAxisCoords(pos){var res={},i,axis;for(i=0;i<xaxes.length;++i){axis=xaxes[i];if(axis&&axis.used)res["x"+axis.n]=axis.c2p(pos.left)}for(i=0;i<yaxes.length;++i){axis=yaxes[i];if(axis&&axis.used)res["y"+axis.n]=axis.c2p(pos.top)}if(res.x1!==undefined)res.x=res.x1;if(res.y1!==undefined)res.y=res.y1;return res}function axisToCanvasCoords(pos){var res={},i,axis,key;for(i=0;i<xaxes.length;++i){axis=xaxes[i];if(axis&&axis.used){key="x"+axis.n;if(pos[key]==null&&axis.n==1)key="x";if(pos[key]!=null){res.left=axis.p2c(pos[key]);break}}}for(i=0;i<yaxes.length;++i){axis=yaxes[i];if(axis&&axis.used){key="y"+axis.n;if(pos[key]==null&&axis.n==1)key="y";if(pos[key]!=null){res.top=axis.p2c(pos[key]);break}}}return res}function getOrCreateAxis(axes,number){if(!axes[number-1])axes[number-1]={n:number,direction:axes==xaxes?"x":"y",options:$.extend(true,{},axes==xaxes?options.xaxis:options.yaxis)};return axes[number-1]}function fillInSeriesOptions(){var neededColors=series.length,maxIndex=-1,i;for(i=0;i<series.length;++i){var sc=series[i].color;if(sc!=null){neededColors--;if(typeof sc=="number"&&sc>maxIndex){maxIndex=sc}}}if(neededColors<=maxIndex){neededColors=maxIndex+1}var c,colors=[],colorPool=options.colors,colorPoolSize=colorPool.length,variation=0;for(i=0;i<neededColors;i++){c=$.color.parse(colorPool[i%colorPoolSize]||"#666");if(i%colorPoolSize==0&&i){if(variation>=0){if(variation<.5){variation=-variation-.2}else variation=0}else variation=-variation}colors[i]=c.scale("rgb",1+variation)}var colori=0,s;for(i=0;i<series.length;++i){s=series[i];if(s.color==null){s.color=colors[colori].toString();++colori}else if(typeof s.color=="number")s.color=colors[s.color].toString();if(s.lines.show==null){var v,show=true;for(v in s)if(s[v]&&s[v].show){show=false;break}if(show)s.lines.show=true}if(s.lines.zero==null){s.lines.zero=!!s.lines.fill}s.xaxis=getOrCreateAxis(xaxes,axisNumber(s,"x"));s.yaxis=getOrCreateAxis(yaxes,axisNumber(s,"y"))}}function processData(){var topSentry=Number.POSITIVE_INFINITY,bottomSentry=Number.NEGATIVE_INFINITY,fakeInfinity=Number.MAX_VALUE,i,j,k,m,length,s,points,ps,x,y,axis,val,f,p,data,format;function updateAxis(axis,min,max){if(min<axis.datamin&&min!=-fakeInfinity)axis.datamin=min;if(max>axis.datamax&&max!=fakeInfinity)axis.datamax=max}$.each(allAxes(),function(_,axis){axis.datamin=topSentry;axis.datamax=bottomSentry;axis.used=false});for(i=0;i<series.length;++i){s=series[i];s.datapoints={points:[]};executeHooks(hooks.processRawData,[s,s.data,s.datapoints])}for(i=0;i<series.length;++i){s=series[i];data=s.data;format=s.datapoints.format;if(!format){format=[];format.push({x:true,number:true,required:true});format.push({y:true,number:true,required:true});if(s.bars.show||s.lines.show&&s.lines.fill){var autoscale=!!(s.bars.show&&s.bars.zero||s.lines.show&&s.lines.zero);format.push({y:true,number:true,required:false,defaultValue:0,autoscale:autoscale});if(s.bars.horizontal){delete format[format.length-1].y;format[format.length-1].x=true}}s.datapoints.format=format}if(s.datapoints.pointsize!=null)continue;s.datapoints.pointsize=format.length;ps=s.datapoints.pointsize;points=s.datapoints.points;var insertSteps=s.lines.show&&s.lines.steps;s.xaxis.used=s.yaxis.used=true;for(j=k=0;j<data.length;++j,k+=ps){p=data[j];var nullify=p==null;if(!nullify){for(m=0;m<ps;++m){val=p[m];f=format[m];if(f){if(f.number&&val!=null){val=+val;if(isNaN(val))val=null;else if(val==Infinity)val=fakeInfinity;else if(val==-Infinity)val=-fakeInfinity}if(val==null){if(f.required)nullify=true;if(f.defaultValue!=null)val=f.defaultValue}}points[k+m]=val}}if(nullify){for(m=0;m<ps;++m){val=points[k+m];if(val!=null){f=format[m];if(f.autoscale!==false){if(f.x){updateAxis(s.xaxis,val,val)}if(f.y){updateAxis(s.yaxis,val,val)}}}points[k+m]=null}}else{if(insertSteps&&k>0&&points[k-ps]!=null&&points[k-ps]!=points[k]&&points[k-ps+1]!=points[k+1]){for(m=0;m<ps;++m)points[k+ps+m]=points[k+m];points[k+1]=points[k-ps+1];k+=ps}}}}for(i=0;i<series.length;++i){s=series[i];executeHooks(hooks.processDatapoints,[s,s.datapoints])}for(i=0;i<series.length;++i){s=series[i];points=s.datapoints.points;ps=s.datapoints.pointsize;format=s.datapoints.format;var xmin=topSentry,ymin=topSentry,xmax=bottomSentry,ymax=bottomSentry;for(j=0;j<points.length;j+=ps){if(points[j]==null)continue;for(m=0;m<ps;++m){val=points[j+m];f=format[m];if(!f||f.autoscale===false||val==fakeInfinity||val==-fakeInfinity)continue;if(f.x){if(val<xmin)xmin=val;if(val>xmax)xmax=val}if(f.y){if(val<ymin)ymin=val;if(val>ymax)ymax=val}}}if(s.bars.show){var delta;switch(s.bars.align){case"left":delta=0;break;case"right":delta=-s.bars.barWidth;break;default:delta=-s.bars.barWidth/2}if(s.bars.horizontal){ymin+=delta;ymax+=delta+s.bars.barWidth}else{xmin+=delta;xmax+=delta+s.bars.barWidth}}updateAxis(s.xaxis,xmin,xmax);updateAxis(s.yaxis,ymin,ymax)}$.each(allAxes(),function(_,axis){if(axis.datamin==topSentry)axis.datamin=null;if(axis.datamax==bottomSentry)axis.datamax=null})}function setupCanvases(){placeholder.css("padding",0).children().filter(function(){return!$(this).hasClass("flot-overlay")&&!$(this).hasClass("flot-base")}).remove();if(placeholder.css("position")=="static")placeholder.css("position","relative");surface=new Canvas("flot-base",placeholder);overlay=new Canvas("flot-overlay",placeholder);ctx=surface.context;octx=overlay.context;eventHolder=$(overlay.element).unbind();var existing=placeholder.data("plot");if(existing){existing.shutdown();overlay.clear()}placeholder.data("plot",plot)}function bindEvents(){if(options.grid.hoverable){eventHolder.mousemove(onMouseMove);eventHolder.bind("mouseleave",onMouseLeave)}if(options.grid.clickable)eventHolder.click(onClick);executeHooks(hooks.bindEvents,[eventHolder])}function shutdown(){if(redrawTimeout)clearTimeout(redrawTimeout);eventHolder.unbind("mousemove",onMouseMove);eventHolder.unbind("mouseleave",onMouseLeave);eventHolder.unbind("click",onClick);executeHooks(hooks.shutdown,[eventHolder])}function setTransformationHelpers(axis){function identity(x){return x}var s,m,t=axis.options.transform||identity,it=axis.options.inverseTransform;if(axis.direction=="x"){s=axis.scale=plotWidth/Math.abs(t(axis.max)-t(axis.min));m=Math.min(t(axis.max),t(axis.min))}else{s=axis.scale=plotHeight/Math.abs(t(axis.max)-t(axis.min));s=-s;m=Math.max(t(axis.max),t(axis.min))}if(t==identity)axis.p2c=function(p){return(p-m)*s};else axis.p2c=function(p){return(t(p)-m)*s};if(!it)axis.c2p=function(c){return m+c/s};else axis.c2p=function(c){return it(m+c/s)}}function measureTickLabels(axis){var opts=axis.options,ticks=axis.ticks||[],labelWidth=opts.labelWidth||0,labelHeight=opts.labelHeight||0,maxWidth=labelWidth||(axis.direction=="x"?Math.floor(surface.width/(ticks.length||1)):null),legacyStyles=axis.direction+"Axis "+axis.direction+axis.n+"Axis",layer="flot-"+axis.direction+"-axis flot-"+axis.direction+axis.n+"-axis "+legacyStyles,font=opts.font||"flot-tick-label tickLabel";for(var i=0;i<ticks.length;++i){var t=ticks[i];if(!t.label)continue;var info=surface.getTextInfo(layer,t.label,font,null,maxWidth);labelWidth=Math.max(labelWidth,info.width);labelHeight=Math.max(labelHeight,info.height)}axis.labelWidth=opts.labelWidth||labelWidth;axis.labelHeight=opts.labelHeight||labelHeight}function allocateAxisBoxFirstPhase(axis){var lw=axis.labelWidth,lh=axis.labelHeight,pos=axis.options.position,isXAxis=axis.direction==="x",tickLength=axis.options.tickLength,axisMargin=options.grid.axisMargin,padding=options.grid.labelMargin,innermost=true,outermost=true,first=true,found=false;$.each(isXAxis?xaxes:yaxes,function(i,a){if(a&&a.reserveSpace){if(a===axis){found=true}else if(a.options.position===pos){if(found){outermost=false}else{innermost=false}}if(!found){first=false}}});if(outermost){axisMargin=0}if(tickLength==null){tickLength=first?"full":5}if(!isNaN(+tickLength))padding+=+tickLength;if(isXAxis){lh+=padding;if(pos=="bottom"){plotOffset.bottom+=lh+axisMargin;axis.box={top:surface.height-plotOffset.bottom,height:lh}}else{axis.box={top:plotOffset.top+axisMargin,height:lh};plotOffset.top+=lh+axisMargin}}else{lw+=padding;if(pos=="left"){axis.box={left:plotOffset.left+axisMargin,width:lw};plotOffset.left+=lw+axisMargin}else{plotOffset.right+=lw+axisMargin;axis.box={left:surface.width-plotOffset.right,width:lw}}}axis.position=pos;axis.tickLength=tickLength;axis.box.padding=padding;axis.innermost=innermost}function allocateAxisBoxSecondPhase(axis){if(axis.direction=="x"){axis.box.left=plotOffset.left-axis.labelWidth/2;axis.box.width=surface.width-plotOffset.left-plotOffset.right+axis.labelWidth}else{axis.box.top=plotOffset.top-axis.labelHeight/2;axis.box.height=surface.height-plotOffset.bottom-plotOffset.top+axis.labelHeight}}function adjustLayoutForThingsStickingOut(){var minMargin=options.grid.minBorderMargin,axis,i;if(minMargin==null){minMargin=0;for(i=0;i<series.length;++i)minMargin=Math.max(minMargin,2*(series[i].points.radius+series[i].points.lineWidth/2))}var margins={left:minMargin,right:minMargin,top:minMargin,bottom:minMargin};$.each(allAxes(),function(_,axis){if(axis.reserveSpace&&axis.ticks&&axis.ticks.length){var lastTick=axis.ticks[axis.ticks.length-1];if(axis.direction==="x"){margins.left=Math.max(margins.left,axis.labelWidth/2);if(lastTick.v<=axis.max){margins.right=Math.max(margins.right,axis.labelWidth/2)}}else{margins.bottom=Math.max(margins.bottom,axis.labelHeight/2);if(lastTick.v<=axis.max){margins.top=Math.max(margins.top,axis.labelHeight/2)}}}});plotOffset.left=Math.ceil(Math.max(margins.left,plotOffset.left));plotOffset.right=Math.ceil(Math.max(margins.right,plotOffset.right));plotOffset.top=Math.ceil(Math.max(margins.top,plotOffset.top));plotOffset.bottom=Math.ceil(Math.max(margins.bottom,plotOffset.bottom))}function setupGrid(){var i,axes=allAxes(),showGrid=options.grid.show;for(var a in plotOffset){var margin=options.grid.margin||0;plotOffset[a]=typeof margin=="number"?margin:margin[a]||0}executeHooks(hooks.processOffset,[plotOffset]);for(var a in plotOffset){if(typeof options.grid.borderWidth=="object"){plotOffset[a]+=showGrid?options.grid.borderWidth[a]:0}else{plotOffset[a]+=showGrid?options.grid.borderWidth:0}}$.each(axes,function(_,axis){axis.show=axis.options.show;if(axis.show==null)axis.show=axis.used;axis.reserveSpace=axis.show||axis.options.reserveSpace;setRange(axis)});if(showGrid){var allocatedAxes=$.grep(axes,function(axis){return axis.reserveSpace});$.each(allocatedAxes,function(_,axis){setupTickGeneration(axis);setTicks(axis);snapRangeToTicks(axis,axis.ticks);measureTickLabels(axis)});for(i=allocatedAxes.length-1;i>=0;--i)allocateAxisBoxFirstPhase(allocatedAxes[i]);adjustLayoutForThingsStickingOut();$.each(allocatedAxes,function(_,axis){allocateAxisBoxSecondPhase(axis)})}plotWidth=surface.width-plotOffset.left-plotOffset.right;plotHeight=surface.height-plotOffset.bottom-plotOffset.top;$.each(axes,function(_,axis){setTransformationHelpers(axis)});if(showGrid){drawAxisLabels()}insertLegend()}function setRange(axis){var opts=axis.options,min=+(opts.min!=null?opts.min:axis.datamin),max=+(opts.max!=null?opts.max:axis.datamax),delta=max-min;if(delta==0){var widen=max==0?1:.01;if(opts.min==null)min-=widen;if(opts.max==null||opts.min!=null)max+=widen}else{var margin=opts.autoscaleMargin;if(margin!=null){if(opts.min==null){min-=delta*margin;if(min<0&&axis.datamin!=null&&axis.datamin>=0)min=0}if(opts.max==null){max+=delta*margin;if(max>0&&axis.datamax!=null&&axis.datamax<=0)max=0}}}axis.min=min;axis.max=max}function setupTickGeneration(axis){var opts=axis.options;var noTicks;if(typeof opts.ticks=="number"&&opts.ticks>0)noTicks=opts.ticks;else noTicks=.3*Math.sqrt(axis.direction=="x"?surface.width:surface.height);var delta=(axis.max-axis.min)/noTicks,dec=-Math.floor(Math.log(delta)/Math.LN10),maxDec=opts.tickDecimals;if(maxDec!=null&&dec>maxDec){dec=maxDec}var magn=Math.pow(10,-dec),norm=delta/magn,size;if(norm<1.5){size=1}else if(norm<3){size=2;if(norm>2.25&&(maxDec==null||dec+1<=maxDec)){size=2.5;++dec}}else if(norm<7.5){size=5}else{size=10}size*=magn;if(opts.minTickSize!=null&&size<opts.minTickSize){size=opts.minTickSize}axis.delta=delta;axis.tickDecimals=Math.max(0,maxDec!=null?maxDec:dec);axis.tickSize=opts.tickSize||size;if(opts.mode=="time"&&!axis.tickGenerator){throw new Error("Time mode requires the flot.time plugin.")}if(!axis.tickGenerator){axis.tickGenerator=function(axis){var ticks=[],start=floorInBase(axis.min,axis.tickSize),i=0,v=Number.NaN,prev;do{prev=v;v=start+i*axis.tickSize;ticks.push(v);++i}while(v<axis.max&&v!=prev);return ticks};axis.tickFormatter=function(value,axis){var factor=axis.tickDecimals?Math.pow(10,axis.tickDecimals):1;var formatted=""+Math.round(value*factor)/factor;if(axis.tickDecimals!=null){var decimal=formatted.indexOf(".");var precision=decimal==-1?0:formatted.length-decimal-1;if(precision<axis.tickDecimals){return(precision?formatted:formatted+".")+(""+factor).substr(1,axis.tickDecimals-precision)}}return formatted}}if($.isFunction(opts.tickFormatter))axis.tickFormatter=function(v,axis){return""+opts.tickFormatter(v,axis)};if(opts.alignTicksWithAxis!=null){var otherAxis=(axis.direction=="x"?xaxes:yaxes)[opts.alignTicksWithAxis-1];if(otherAxis&&otherAxis.used&&otherAxis!=axis){var niceTicks=axis.tickGenerator(axis);if(niceTicks.length>0){if(opts.min==null)axis.min=Math.min(axis.min,niceTicks[0]);if(opts.max==null&&niceTicks.length>1)axis.max=Math.max(axis.max,niceTicks[niceTicks.length-1])}axis.tickGenerator=function(axis){var ticks=[],v,i;for(i=0;i<otherAxis.ticks.length;++i){v=(otherAxis.ticks[i].v-otherAxis.min)/(otherAxis.max-otherAxis.min);v=axis.min+v*(axis.max-axis.min);ticks.push(v)}return ticks};if(!axis.mode&&opts.tickDecimals==null){var extraDec=Math.max(0,-Math.floor(Math.log(axis.delta)/Math.LN10)+1),ts=axis.tickGenerator(axis);if(!(ts.length>1&&/\..*0$/.test((ts[1]-ts[0]).toFixed(extraDec))))axis.tickDecimals=extraDec}}}}function setTicks(axis){var oticks=axis.options.ticks,ticks=[];if(oticks==null||typeof oticks=="number"&&oticks>0)ticks=axis.tickGenerator(axis);else if(oticks){if($.isFunction(oticks))ticks=oticks(axis);else ticks=oticks}var i,v;axis.ticks=[];for(i=0;i<ticks.length;++i){var label=null;var t=ticks[i];if(typeof t=="object"){v=+t[0];if(t.length>1)label=t[1]}else v=+t;if(label==null)label=axis.tickFormatter(v,axis);if(!isNaN(v))axis.ticks.push({v:v,label:label})}}function snapRangeToTicks(axis,ticks){if(axis.options.autoscaleMargin&&ticks.length>0){if(axis.options.min==null)axis.min=Math.min(axis.min,ticks[0].v);if(axis.options.max==null&&ticks.length>1)axis.max=Math.max(axis.max,ticks[ticks.length-1].v)}}function draw(){surface.clear();executeHooks(hooks.drawBackground,[ctx]);var grid=options.grid;if(grid.show&&grid.backgroundColor)drawBackground();if(grid.show&&!grid.aboveData){drawGrid()}for(var i=0;i<series.length;++i){executeHooks(hooks.drawSeries,[ctx,series[i]]);drawSeries(series[i])}executeHooks(hooks.draw,[ctx]);if(grid.show&&grid.aboveData){drawGrid()}surface.render();triggerRedrawOverlay()}function extractRange(ranges,coord){var axis,from,to,key,axes=allAxes();for(var i=0;i<axes.length;++i){axis=axes[i];if(axis.direction==coord){key=coord+axis.n+"axis";if(!ranges[key]&&axis.n==1)key=coord+"axis";if(ranges[key]){from=ranges[key].from;to=ranges[key].to;break}}}if(!ranges[key]){axis=coord=="x"?xaxes[0]:yaxes[0];from=ranges[coord+"1"];to=ranges[coord+"2"]}if(from!=null&&to!=null&&from>to){var tmp=from;from=to;to=tmp}return{from:from,to:to,axis:axis}}function drawBackground(){ctx.save();ctx.translate(plotOffset.left,plotOffset.top);ctx.fillStyle=getColorOrGradient(options.grid.backgroundColor,plotHeight,0,"rgba(255, 255, 255, 0)");ctx.fillRect(0,0,plotWidth,plotHeight);ctx.restore()}function drawGrid(){var i,axes,bw,bc;ctx.save();ctx.translate(plotOffset.left,plotOffset.top);var markings=options.grid.markings;if(markings){if($.isFunction(markings)){axes=plot.getAxes();axes.xmin=axes.xaxis.min;axes.xmax=axes.xaxis.max;axes.ymin=axes.yaxis.min;axes.ymax=axes.yaxis.max;markings=markings(axes)}for(i=0;i<markings.length;++i){var m=markings[i],xrange=extractRange(m,"x"),yrange=extractRange(m,"y");if(xrange.from==null)xrange.from=xrange.axis.min;if(xrange.to==null)xrange.to=xrange.axis.max;if(yrange.from==null)yrange.from=yrange.axis.min;if(yrange.to==null)yrange.to=yrange.axis.max;if(xrange.to<xrange.axis.min||xrange.from>xrange.axis.max||yrange.to<yrange.axis.min||yrange.from>yrange.axis.max)continue;xrange.from=Math.max(xrange.from,xrange.axis.min);xrange.to=Math.min(xrange.to,xrange.axis.max); +yrange.from=Math.max(yrange.from,yrange.axis.min);yrange.to=Math.min(yrange.to,yrange.axis.max);if(xrange.from==xrange.to&&yrange.from==yrange.to)continue;xrange.from=xrange.axis.p2c(xrange.from);xrange.to=xrange.axis.p2c(xrange.to);yrange.from=yrange.axis.p2c(yrange.from);yrange.to=yrange.axis.p2c(yrange.to);if(xrange.from==xrange.to||yrange.from==yrange.to){ctx.beginPath();ctx.strokeStyle=m.color||options.grid.markingsColor;ctx.lineWidth=m.lineWidth||options.grid.markingsLineWidth;ctx.moveTo(xrange.from,yrange.from);ctx.lineTo(xrange.to,yrange.to);ctx.stroke()}else{ctx.fillStyle=m.color||options.grid.markingsColor;ctx.fillRect(xrange.from,yrange.to,xrange.to-xrange.from,yrange.from-yrange.to)}}}axes=allAxes();bw=options.grid.borderWidth;for(var j=0;j<axes.length;++j){var axis=axes[j],box=axis.box,t=axis.tickLength,x,y,xoff,yoff;if(!axis.show||axis.ticks.length==0)continue;ctx.lineWidth=1;if(axis.direction=="x"){x=0;if(t=="full")y=axis.position=="top"?0:plotHeight;else y=box.top-plotOffset.top+(axis.position=="top"?box.height:0)}else{y=0;if(t=="full")x=axis.position=="left"?0:plotWidth;else x=box.left-plotOffset.left+(axis.position=="left"?box.width:0)}if(!axis.innermost){ctx.strokeStyle=axis.options.color;ctx.beginPath();xoff=yoff=0;if(axis.direction=="x")xoff=plotWidth+1;else yoff=plotHeight+1;if(ctx.lineWidth==1){if(axis.direction=="x"){y=Math.floor(y)+.5}else{x=Math.floor(x)+.5}}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff);ctx.stroke()}ctx.strokeStyle=axis.options.tickColor;ctx.beginPath();for(i=0;i<axis.ticks.length;++i){var v=axis.ticks[i].v;xoff=yoff=0;if(isNaN(v)||v<axis.min||v>axis.max||t=="full"&&(typeof bw=="object"&&bw[axis.position]>0||bw>0)&&(v==axis.min||v==axis.max))continue;if(axis.direction=="x"){x=axis.p2c(v);yoff=t=="full"?-plotHeight:t;if(axis.position=="top")yoff=-yoff}else{y=axis.p2c(v);xoff=t=="full"?-plotWidth:t;if(axis.position=="left")xoff=-xoff}if(ctx.lineWidth==1){if(axis.direction=="x")x=Math.floor(x)+.5;else y=Math.floor(y)+.5}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff)}ctx.stroke()}if(bw){bc=options.grid.borderColor;if(typeof bw=="object"||typeof bc=="object"){if(typeof bw!=="object"){bw={top:bw,right:bw,bottom:bw,left:bw}}if(typeof bc!=="object"){bc={top:bc,right:bc,bottom:bc,left:bc}}if(bw.top>0){ctx.strokeStyle=bc.top;ctx.lineWidth=bw.top;ctx.beginPath();ctx.moveTo(0-bw.left,0-bw.top/2);ctx.lineTo(plotWidth,0-bw.top/2);ctx.stroke()}if(bw.right>0){ctx.strokeStyle=bc.right;ctx.lineWidth=bw.right;ctx.beginPath();ctx.moveTo(plotWidth+bw.right/2,0-bw.top);ctx.lineTo(plotWidth+bw.right/2,plotHeight);ctx.stroke()}if(bw.bottom>0){ctx.strokeStyle=bc.bottom;ctx.lineWidth=bw.bottom;ctx.beginPath();ctx.moveTo(plotWidth+bw.right,plotHeight+bw.bottom/2);ctx.lineTo(0,plotHeight+bw.bottom/2);ctx.stroke()}if(bw.left>0){ctx.strokeStyle=bc.left;ctx.lineWidth=bw.left;ctx.beginPath();ctx.moveTo(0-bw.left/2,plotHeight+bw.bottom);ctx.lineTo(0-bw.left/2,0);ctx.stroke()}}else{ctx.lineWidth=bw;ctx.strokeStyle=options.grid.borderColor;ctx.strokeRect(-bw/2,-bw/2,plotWidth+bw,plotHeight+bw)}}ctx.restore()}function drawAxisLabels(){$.each(allAxes(),function(_,axis){var box=axis.box,legacyStyles=axis.direction+"Axis "+axis.direction+axis.n+"Axis",layer="flot-"+axis.direction+"-axis flot-"+axis.direction+axis.n+"-axis "+legacyStyles,font=axis.options.font||"flot-tick-label tickLabel",tick,x,y,halign,valign;surface.removeText(layer);if(!axis.show||axis.ticks.length==0)return;for(var i=0;i<axis.ticks.length;++i){tick=axis.ticks[i];if(!tick.label||tick.v<axis.min||tick.v>axis.max)continue;if(axis.direction=="x"){halign="center";x=plotOffset.left+axis.p2c(tick.v);if(axis.position=="bottom"){y=box.top+box.padding}else{y=box.top+box.height-box.padding;valign="bottom"}}else{valign="middle";y=plotOffset.top+axis.p2c(tick.v);if(axis.position=="left"){x=box.left+box.width-box.padding;halign="right"}else{x=box.left+box.padding}}surface.addText(layer,x,y,tick.label,font,null,null,halign,valign)}})}function drawSeries(series){if(series.lines.show)drawSeriesLines(series);if(series.bars.show)drawSeriesBars(series);if(series.points.show)drawSeriesPoints(series)}function drawSeriesLines(series){function plotLine(datapoints,xoffset,yoffset,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize,prevx=null,prevy=null;ctx.beginPath();for(var i=ps;i<points.length;i+=ps){var x1=points[i-ps],y1=points[i-ps+1],x2=points[i],y2=points[i+1];if(x1==null||x2==null)continue;if(y1<=y2&&y1<axisy.min){if(y2<axisy.min)continue;x1=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.min}else if(y2<=y1&&y2<axisy.min){if(y1<axisy.min)continue;x2=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.min}if(y1>=y2&&y1>axisy.max){if(y2>axisy.max)continue;x1=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.max}else if(y2>=y1&&y2>axisy.max){if(y1>axisy.max)continue;x2=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.max}if(x1<=x2&&x1<axisx.min){if(x2<axisx.min)continue;y1=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.min}else if(x2<=x1&&x2<axisx.min){if(x1<axisx.min)continue;y2=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.min}if(x1>=x2&&x1>axisx.max){if(x2>axisx.max)continue;y1=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.max}else if(x2>=x1&&x2>axisx.max){if(x1>axisx.max)continue;y2=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.max}if(x1!=prevx||y1!=prevy)ctx.moveTo(axisx.p2c(x1)+xoffset,axisy.p2c(y1)+yoffset);prevx=x2;prevy=y2;ctx.lineTo(axisx.p2c(x2)+xoffset,axisy.p2c(y2)+yoffset)}ctx.stroke()}function plotLineArea(datapoints,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize,bottom=Math.min(Math.max(0,axisy.min),axisy.max),i=0,top,areaOpen=false,ypos=1,segmentStart=0,segmentEnd=0;while(true){if(ps>0&&i>points.length+ps)break;i+=ps;var x1=points[i-ps],y1=points[i-ps+ypos],x2=points[i],y2=points[i+ypos];if(areaOpen){if(ps>0&&x1!=null&&x2==null){segmentEnd=i;ps=-ps;ypos=2;continue}if(ps<0&&i==segmentStart+ps){ctx.fill();areaOpen=false;ps=-ps;ypos=1;i=segmentStart=segmentEnd+ps;continue}}if(x1==null||x2==null)continue;if(x1<=x2&&x1<axisx.min){if(x2<axisx.min)continue;y1=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.min}else if(x2<=x1&&x2<axisx.min){if(x1<axisx.min)continue;y2=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.min}if(x1>=x2&&x1>axisx.max){if(x2>axisx.max)continue;y1=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.max}else if(x2>=x1&&x2>axisx.max){if(x1>axisx.max)continue;y2=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.max}if(!areaOpen){ctx.beginPath();ctx.moveTo(axisx.p2c(x1),axisy.p2c(bottom));areaOpen=true}if(y1>=axisy.max&&y2>=axisy.max){ctx.lineTo(axisx.p2c(x1),axisy.p2c(axisy.max));ctx.lineTo(axisx.p2c(x2),axisy.p2c(axisy.max));continue}else if(y1<=axisy.min&&y2<=axisy.min){ctx.lineTo(axisx.p2c(x1),axisy.p2c(axisy.min));ctx.lineTo(axisx.p2c(x2),axisy.p2c(axisy.min));continue}var x1old=x1,x2old=x2;if(y1<=y2&&y1<axisy.min&&y2>=axisy.min){x1=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.min}else if(y2<=y1&&y2<axisy.min&&y1>=axisy.min){x2=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.min}if(y1>=y2&&y1>axisy.max&&y2<=axisy.max){x1=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.max}else if(y2>=y1&&y2>axisy.max&&y1<=axisy.max){x2=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.max}if(x1!=x1old){ctx.lineTo(axisx.p2c(x1old),axisy.p2c(y1))}ctx.lineTo(axisx.p2c(x1),axisy.p2c(y1));ctx.lineTo(axisx.p2c(x2),axisy.p2c(y2));if(x2!=x2old){ctx.lineTo(axisx.p2c(x2),axisy.p2c(y2));ctx.lineTo(axisx.p2c(x2old),axisy.p2c(y2))}}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);ctx.lineJoin="round";var lw=series.lines.lineWidth,sw=series.shadowSize;if(lw>0&&sw>0){ctx.lineWidth=sw;ctx.strokeStyle="rgba(0,0,0,0.1)";var angle=Math.PI/18;plotLine(series.datapoints,Math.sin(angle)*(lw/2+sw/2),Math.cos(angle)*(lw/2+sw/2),series.xaxis,series.yaxis);ctx.lineWidth=sw/2;plotLine(series.datapoints,Math.sin(angle)*(lw/2+sw/4),Math.cos(angle)*(lw/2+sw/4),series.xaxis,series.yaxis)}ctx.lineWidth=lw;ctx.strokeStyle=series.color;var fillStyle=getFillStyle(series.lines,series.color,0,plotHeight);if(fillStyle){ctx.fillStyle=fillStyle;plotLineArea(series.datapoints,series.xaxis,series.yaxis)}if(lw>0)plotLine(series.datapoints,0,0,series.xaxis,series.yaxis);ctx.restore()}function drawSeriesPoints(series){function plotPoints(datapoints,radius,fillStyle,offset,shadow,axisx,axisy,symbol){var points=datapoints.points,ps=datapoints.pointsize;for(var i=0;i<points.length;i+=ps){var x=points[i],y=points[i+1];if(x==null||x<axisx.min||x>axisx.max||y<axisy.min||y>axisy.max)continue;ctx.beginPath();x=axisx.p2c(x);y=axisy.p2c(y)+offset;if(symbol=="circle")ctx.arc(x,y,radius,0,shadow?Math.PI:Math.PI*2,false);else symbol(ctx,x,y,radius,shadow);ctx.closePath();if(fillStyle){ctx.fillStyle=fillStyle;ctx.fill()}ctx.stroke()}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);var lw=series.points.lineWidth,sw=series.shadowSize,radius=series.points.radius,symbol=series.points.symbol;if(lw==0)lw=1e-4;if(lw>0&&sw>0){var w=sw/2;ctx.lineWidth=w;ctx.strokeStyle="rgba(0,0,0,0.1)";plotPoints(series.datapoints,radius,null,w+w/2,true,series.xaxis,series.yaxis,symbol);ctx.strokeStyle="rgba(0,0,0,0.2)";plotPoints(series.datapoints,radius,null,w/2,true,series.xaxis,series.yaxis,symbol)}ctx.lineWidth=lw;ctx.strokeStyle=series.color;plotPoints(series.datapoints,radius,getFillStyle(series.points,series.color),0,false,series.xaxis,series.yaxis,symbol);ctx.restore()}function drawBar(x,y,b,barLeft,barRight,fillStyleCallback,axisx,axisy,c,horizontal,lineWidth){var left,right,bottom,top,drawLeft,drawRight,drawTop,drawBottom,tmp;if(horizontal){drawBottom=drawRight=drawTop=true;drawLeft=false;left=b;right=x;top=y+barLeft;bottom=y+barRight;if(right<left){tmp=right;right=left;left=tmp;drawLeft=true;drawRight=false}}else{drawLeft=drawRight=drawTop=true;drawBottom=false;left=x+barLeft;right=x+barRight;bottom=b;top=y;if(top<bottom){tmp=top;top=bottom;bottom=tmp;drawBottom=true;drawTop=false}}if(right<axisx.min||left>axisx.max||top<axisy.min||bottom>axisy.max)return;if(left<axisx.min){left=axisx.min;drawLeft=false}if(right>axisx.max){right=axisx.max;drawRight=false}if(bottom<axisy.min){bottom=axisy.min;drawBottom=false}if(top>axisy.max){top=axisy.max;drawTop=false}left=axisx.p2c(left);bottom=axisy.p2c(bottom);right=axisx.p2c(right);top=axisy.p2c(top);if(fillStyleCallback){c.fillStyle=fillStyleCallback(bottom,top);c.fillRect(left,top,right-left,bottom-top)}if(lineWidth>0&&(drawLeft||drawRight||drawTop||drawBottom)){c.beginPath();c.moveTo(left,bottom);if(drawLeft)c.lineTo(left,top);else c.moveTo(left,top);if(drawTop)c.lineTo(right,top);else c.moveTo(right,top);if(drawRight)c.lineTo(right,bottom);else c.moveTo(right,bottom);if(drawBottom)c.lineTo(left,bottom);else c.moveTo(left,bottom);c.stroke()}}function drawSeriesBars(series){function plotBars(datapoints,barLeft,barRight,fillStyleCallback,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize;for(var i=0;i<points.length;i+=ps){if(points[i]==null)continue;drawBar(points[i],points[i+1],points[i+2],barLeft,barRight,fillStyleCallback,axisx,axisy,ctx,series.bars.horizontal,series.bars.lineWidth)}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);ctx.lineWidth=series.bars.lineWidth;ctx.strokeStyle=series.color;var barLeft;switch(series.bars.align){case"left":barLeft=0;break;case"right":barLeft=-series.bars.barWidth;break;default:barLeft=-series.bars.barWidth/2}var fillStyleCallback=series.bars.fill?function(bottom,top){return getFillStyle(series.bars,series.color,bottom,top)}:null;plotBars(series.datapoints,barLeft,barLeft+series.bars.barWidth,fillStyleCallback,series.xaxis,series.yaxis);ctx.restore()}function getFillStyle(filloptions,seriesColor,bottom,top){var fill=filloptions.fill;if(!fill)return null;if(filloptions.fillColor)return getColorOrGradient(filloptions.fillColor,bottom,top,seriesColor);var c=$.color.parse(seriesColor);c.a=typeof fill=="number"?fill:.4;c.normalize();return c.toString()}function insertLegend(){if(options.legend.container!=null){$(options.legend.container).html("")}else{placeholder.find(".legend").remove()}if(!options.legend.show){return}var fragments=[],entries=[],rowStarted=false,lf=options.legend.labelFormatter,s,label;for(var i=0;i<series.length;++i){s=series[i];if(s.label){label=lf?lf(s.label,s):s.label;if(label){entries.push({label:label,color:s.color})}}}if(options.legend.sorted){if($.isFunction(options.legend.sorted)){entries.sort(options.legend.sorted)}else if(options.legend.sorted=="reverse"){entries.reverse()}else{var ascending=options.legend.sorted!="descending";entries.sort(function(a,b){return a.label==b.label?0:a.label<b.label!=ascending?1:-1})}}for(var i=0;i<entries.length;++i){var entry=entries[i];if(i%options.legend.noColumns==0){if(rowStarted)fragments.push("</tr>");fragments.push("<tr>");rowStarted=true}fragments.push('<td class="legendColorBox"><div style="border:1px solid '+options.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+entry.color+';overflow:hidden"></div></div></td>'+'<td class="legendLabel">'+entry.label+"</td>")}if(rowStarted)fragments.push("</tr>");if(fragments.length==0)return;var table='<table style="font-size:smaller;color:'+options.grid.color+'">'+fragments.join("")+"</table>";if(options.legend.container!=null)$(options.legend.container).html(table);else{var pos="",p=options.legend.position,m=options.legend.margin;if(m[0]==null)m=[m,m];if(p.charAt(0)=="n")pos+="top:"+(m[1]+plotOffset.top)+"px;";else if(p.charAt(0)=="s")pos+="bottom:"+(m[1]+plotOffset.bottom)+"px;";if(p.charAt(1)=="e")pos+="right:"+(m[0]+plotOffset.right)+"px;";else if(p.charAt(1)=="w")pos+="left:"+(m[0]+plotOffset.left)+"px;";var legend=$('<div class="legend">'+table.replace('style="','style="position:absolute;'+pos+";")+"</div>").appendTo(placeholder);if(options.legend.backgroundOpacity!=0){var c=options.legend.backgroundColor;if(c==null){c=options.grid.backgroundColor;if(c&&typeof c=="string")c=$.color.parse(c);else c=$.color.extract(legend,"background-color");c.a=1;c=c.toString()}var div=legend.children();$('<div style="position:absolute;width:'+div.width()+"px;height:"+div.height()+"px;"+pos+"background-color:"+c+';"> </div>').prependTo(legend).css("opacity",options.legend.backgroundOpacity)}}}var highlights=[],redrawTimeout=null;function findNearbyItem(mouseX,mouseY,seriesFilter){var maxDistance=options.grid.mouseActiveRadius,smallestDistance=maxDistance*maxDistance+1,item=null,foundPoint=false,i,j,ps;for(i=series.length-1;i>=0;--i){if(!seriesFilter(series[i]))continue;var s=series[i],axisx=s.xaxis,axisy=s.yaxis,points=s.datapoints.points,mx=axisx.c2p(mouseX),my=axisy.c2p(mouseY),maxx=maxDistance/axisx.scale,maxy=maxDistance/axisy.scale;ps=s.datapoints.pointsize;if(axisx.options.inverseTransform)maxx=Number.MAX_VALUE;if(axisy.options.inverseTransform)maxy=Number.MAX_VALUE;if(s.lines.show||s.points.show){for(j=0;j<points.length;j+=ps){var x=points[j],y=points[j+1];if(x==null)continue;if(x-mx>maxx||x-mx<-maxx||y-my>maxy||y-my<-maxy)continue;var dx=Math.abs(axisx.p2c(x)-mouseX),dy=Math.abs(axisy.p2c(y)-mouseY),dist=dx*dx+dy*dy;if(dist<smallestDistance){smallestDistance=dist;item=[i,j/ps]}}}if(s.bars.show&&!item){var barLeft,barRight;switch(s.bars.align){case"left":barLeft=0;break;case"right":barLeft=-s.bars.barWidth;break;default:barLeft=-s.bars.barWidth/2}barRight=barLeft+s.bars.barWidth;for(j=0;j<points.length;j+=ps){var x=points[j],y=points[j+1],b=points[j+2];if(x==null)continue;if(series[i].bars.horizontal?mx<=Math.max(b,x)&&mx>=Math.min(b,x)&&my>=y+barLeft&&my<=y+barRight:mx>=x+barLeft&&mx<=x+barRight&&my>=Math.min(b,y)&&my<=Math.max(b,y))item=[i,j/ps]}}}if(item){i=item[0];j=item[1];ps=series[i].datapoints.pointsize;return{datapoint:series[i].datapoints.points.slice(j*ps,(j+1)*ps),dataIndex:j,series:series[i],seriesIndex:i}}return null}function onMouseMove(e){if(options.grid.hoverable)triggerClickHoverEvent("plothover",e,function(s){return s["hoverable"]!=false})}function onMouseLeave(e){if(options.grid.hoverable)triggerClickHoverEvent("plothover",e,function(s){return false})}function onClick(e){triggerClickHoverEvent("plotclick",e,function(s){return s["clickable"]!=false})}function triggerClickHoverEvent(eventname,event,seriesFilter){var offset=eventHolder.offset(),canvasX=event.pageX-offset.left-plotOffset.left,canvasY=event.pageY-offset.top-plotOffset.top,pos=canvasToAxisCoords({left:canvasX,top:canvasY});pos.pageX=event.pageX;pos.pageY=event.pageY;var item=findNearbyItem(canvasX,canvasY,seriesFilter);if(item){item.pageX=parseInt(item.series.xaxis.p2c(item.datapoint[0])+offset.left+plotOffset.left,10);item.pageY=parseInt(item.series.yaxis.p2c(item.datapoint[1])+offset.top+plotOffset.top,10)}if(options.grid.autoHighlight){for(var i=0;i<highlights.length;++i){var h=highlights[i];if(h.auto==eventname&&!(item&&h.series==item.series&&h.point[0]==item.datapoint[0]&&h.point[1]==item.datapoint[1]))unhighlight(h.series,h.point)}if(item)highlight(item.series,item.datapoint,eventname)}placeholder.trigger(eventname,[pos,item])}function triggerRedrawOverlay(){var t=options.interaction.redrawOverlayInterval;if(t==-1){drawOverlay();return}if(!redrawTimeout)redrawTimeout=setTimeout(drawOverlay,t)}function drawOverlay(){redrawTimeout=null;octx.save();overlay.clear();octx.translate(plotOffset.left,plotOffset.top);var i,hi;for(i=0;i<highlights.length;++i){hi=highlights[i];if(hi.series.bars.show)drawBarHighlight(hi.series,hi.point);else drawPointHighlight(hi.series,hi.point)}octx.restore();executeHooks(hooks.drawOverlay,[octx])}function highlight(s,point,auto){if(typeof s=="number")s=series[s];if(typeof point=="number"){var ps=s.datapoints.pointsize;point=s.datapoints.points.slice(ps*point,ps*(point+1))}var i=indexOfHighlight(s,point);if(i==-1){highlights.push({series:s,point:point,auto:auto});triggerRedrawOverlay()}else if(!auto)highlights[i].auto=false}function unhighlight(s,point){if(s==null&&point==null){highlights=[];triggerRedrawOverlay();return}if(typeof s=="number")s=series[s];if(typeof point=="number"){var ps=s.datapoints.pointsize;point=s.datapoints.points.slice(ps*point,ps*(point+1))}var i=indexOfHighlight(s,point);if(i!=-1){highlights.splice(i,1);triggerRedrawOverlay()}}function indexOfHighlight(s,p){for(var i=0;i<highlights.length;++i){var h=highlights[i];if(h.series==s&&h.point[0]==p[0]&&h.point[1]==p[1])return i}return-1}function drawPointHighlight(series,point){var x=point[0],y=point[1],axisx=series.xaxis,axisy=series.yaxis,highlightColor=typeof series.highlightColor==="string"?series.highlightColor:$.color.parse(series.color).scale("a",.5).toString();if(x<axisx.min||x>axisx.max||y<axisy.min||y>axisy.max)return;var pointRadius=series.points.radius+series.points.lineWidth/2;octx.lineWidth=pointRadius;octx.strokeStyle=highlightColor;var radius=1.5*pointRadius;x=axisx.p2c(x);y=axisy.p2c(y);octx.beginPath();if(series.points.symbol=="circle")octx.arc(x,y,radius,0,2*Math.PI,false);else series.points.symbol(octx,x,y,radius,false);octx.closePath();octx.stroke()}function drawBarHighlight(series,point){var highlightColor=typeof series.highlightColor==="string"?series.highlightColor:$.color.parse(series.color).scale("a",.5).toString(),fillStyle=highlightColor,barLeft;switch(series.bars.align){case"left":barLeft=0;break;case"right":barLeft=-series.bars.barWidth;break;default:barLeft=-series.bars.barWidth/2}octx.lineWidth=series.bars.lineWidth;octx.strokeStyle=highlightColor;drawBar(point[0],point[1],point[2]||0,barLeft,barLeft+series.bars.barWidth,function(){return fillStyle},series.xaxis,series.yaxis,octx,series.bars.horizontal,series.bars.lineWidth)}function getColorOrGradient(spec,bottom,top,defaultColor){if(typeof spec=="string")return spec;else{var gradient=ctx.createLinearGradient(0,top,0,bottom);for(var i=0,l=spec.colors.length;i<l;++i){var c=spec.colors[i];if(typeof c!="string"){var co=$.color.parse(defaultColor);if(c.brightness!=null)co=co.scale("rgb",c.brightness);if(c.opacity!=null)co.a*=c.opacity;c=co.toString()}gradient.addColorStop(i/(l-1),c)}return gradient}}}$.plot=function(placeholder,data,options){var plot=new Plot($(placeholder),data,options,$.plot.plugins);return plot};$.plot.version="0.8.2";$.plot.plugins=[];$.fn.plot=function(data,options){return this.each(function(){$.plot(this,data,options)})};function floorInBase(n,base){return base*Math.floor(n/base)}})(jQuery); \ No newline at end of file diff --git a/lsp/plugins/jquery.flot.time.min.js b/lsp/plugins/jquery.flot.time.min.js new file mode 100644 index 00000000..aaf319c9 --- /dev/null +++ b/lsp/plugins/jquery.flot.time.min.js @@ -0,0 +1 @@ +(function($){var options={xaxis:{timezone:null,timeformat:null,twelveHourClock:false,monthNames:null}};function floorInBase(n,base){return base*Math.floor(n/base)}function formatDate(d,fmt,monthNames,dayNames){if(typeof d.strftime=="function"){return d.strftime(fmt)}var leftPad=function(n,pad){n=""+n;pad=""+(pad==null?"0":pad);return n.length==1?pad+n:n};var r=[];var escape=false;var hours=d.getHours();var isAM=hours<12;if(monthNames==null){monthNames=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}if(dayNames==null){dayNames=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]}var hours12;if(hours>12){hours12=hours-12}else if(hours==0){hours12=12}else{hours12=hours}for(var i=0;i<fmt.length;++i){var c=fmt.charAt(i);if(escape){switch(c){case"a":c=""+dayNames[d.getDay()];break;case"b":c=""+monthNames[d.getMonth()];break;case"d":c=leftPad(d.getDate());break;case"e":c=leftPad(d.getDate()," ");break;case"h":case"H":c=leftPad(hours);break;case"I":c=leftPad(hours12);break;case"l":c=leftPad(hours12," ");break;case"m":c=leftPad(d.getMonth()+1);break;case"M":c=leftPad(d.getMinutes());break;case"q":c=""+(Math.floor(d.getMonth()/3)+1);break;case"S":c=leftPad(d.getSeconds());break;case"y":c=leftPad(d.getFullYear()%100);break;case"Y":c=""+d.getFullYear();break;case"p":c=isAM?""+"am":""+"pm";break;case"P":c=isAM?""+"AM":""+"PM";break;case"w":c=""+d.getDay();break}r.push(c);escape=false}else{if(c=="%"){escape=true}else{r.push(c)}}}return r.join("")}function makeUtcWrapper(d){function addProxyMethod(sourceObj,sourceMethod,targetObj,targetMethod){sourceObj[sourceMethod]=function(){return targetObj[targetMethod].apply(targetObj,arguments)}}var utc={date:d};if(d.strftime!=undefined){addProxyMethod(utc,"strftime",d,"strftime")}addProxyMethod(utc,"getTime",d,"getTime");addProxyMethod(utc,"setTime",d,"setTime");var props=["Date","Day","FullYear","Hours","Milliseconds","Minutes","Month","Seconds"];for(var p=0;p<props.length;p++){addProxyMethod(utc,"get"+props[p],d,"getUTC"+props[p]);addProxyMethod(utc,"set"+props[p],d,"setUTC"+props[p])}return utc}function dateGenerator(ts,opts){if(opts.timezone=="browser"){return new Date(ts)}else if(!opts.timezone||opts.timezone=="utc"){return makeUtcWrapper(new Date(ts))}else if(typeof timezoneJS!="undefined"&&typeof timezoneJS.Date!="undefined"){var d=new timezoneJS.Date;d.setTimezone(opts.timezone);d.setTime(ts);return d}else{return makeUtcWrapper(new Date(ts))}}var timeUnitSize={second:1e3,minute:60*1e3,hour:60*60*1e3,day:24*60*60*1e3,month:30*24*60*60*1e3,quarter:3*30*24*60*60*1e3,year:365.2425*24*60*60*1e3};var baseSpec=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[.25,"month"],[.5,"month"],[1,"month"],[2,"month"]];var specMonths=baseSpec.concat([[3,"month"],[6,"month"],[1,"year"]]);var specQuarters=baseSpec.concat([[1,"quarter"],[2,"quarter"],[1,"year"]]);function init(plot){plot.hooks.processOptions.push(function(plot,options){$.each(plot.getAxes(),function(axisName,axis){var opts=axis.options;if(opts.mode=="time"){axis.tickGenerator=function(axis){var ticks=[];var d=dateGenerator(axis.min,opts);var minSize=0;var spec=opts.tickSize&&opts.tickSize[1]==="quarter"||opts.minTickSize&&opts.minTickSize[1]==="quarter"?specQuarters:specMonths;if(opts.minTickSize!=null){if(typeof opts.tickSize=="number"){minSize=opts.tickSize}else{minSize=opts.minTickSize[0]*timeUnitSize[opts.minTickSize[1]]}}for(var i=0;i<spec.length-1;++i){if(axis.delta<(spec[i][0]*timeUnitSize[spec[i][1]]+spec[i+1][0]*timeUnitSize[spec[i+1][1]])/2&&spec[i][0]*timeUnitSize[spec[i][1]]>=minSize){break}}var size=spec[i][0];var unit=spec[i][1];if(unit=="year"){if(opts.minTickSize!=null&&opts.minTickSize[1]=="year"){size=Math.floor(opts.minTickSize[0])}else{var magn=Math.pow(10,Math.floor(Math.log(axis.delta/timeUnitSize.year)/Math.LN10));var norm=axis.delta/timeUnitSize.year/magn;if(norm<1.5){size=1}else if(norm<3){size=2}else if(norm<7.5){size=5}else{size=10}size*=magn}if(size<1){size=1}}axis.tickSize=opts.tickSize||[size,unit];var tickSize=axis.tickSize[0];unit=axis.tickSize[1];var step=tickSize*timeUnitSize[unit];if(unit=="second"){d.setSeconds(floorInBase(d.getSeconds(),tickSize))}else if(unit=="minute"){d.setMinutes(floorInBase(d.getMinutes(),tickSize))}else if(unit=="hour"){d.setHours(floorInBase(d.getHours(),tickSize))}else if(unit=="month"){d.setMonth(floorInBase(d.getMonth(),tickSize))}else if(unit=="quarter"){d.setMonth(3*floorInBase(d.getMonth()/3,tickSize))}else if(unit=="year"){d.setFullYear(floorInBase(d.getFullYear(),tickSize))}d.setMilliseconds(0);if(step>=timeUnitSize.minute){d.setSeconds(0)}if(step>=timeUnitSize.hour){d.setMinutes(0)}if(step>=timeUnitSize.day){d.setHours(0)}if(step>=timeUnitSize.day*4){d.setDate(1)}if(step>=timeUnitSize.month*2){d.setMonth(floorInBase(d.getMonth(),3))}if(step>=timeUnitSize.quarter*2){d.setMonth(floorInBase(d.getMonth(),6))}if(step>=timeUnitSize.year){d.setMonth(0)}var carry=0;var v=Number.NaN;var prev;do{prev=v;v=d.getTime();ticks.push(v);if(unit=="month"||unit=="quarter"){if(tickSize<1){d.setDate(1);var start=d.getTime();d.setMonth(d.getMonth()+(unit=="quarter"?3:1));var end=d.getTime();d.setTime(v+carry*timeUnitSize.hour+(end-start)*tickSize);carry=d.getHours();d.setHours(0)}else{d.setMonth(d.getMonth()+tickSize*(unit=="quarter"?3:1))}}else if(unit=="year"){d.setFullYear(d.getFullYear()+tickSize)}else{d.setTime(v+step)}}while(v<axis.max&&v!=prev);return ticks};axis.tickFormatter=function(v,axis){var d=dateGenerator(v,axis.options);if(opts.timeformat!=null){return formatDate(d,opts.timeformat,opts.monthNames,opts.dayNames)}var useQuarters=axis.options.tickSize&&axis.options.tickSize[1]=="quarter"||axis.options.minTickSize&&axis.options.minTickSize[1]=="quarter";var t=axis.tickSize[0]*timeUnitSize[axis.tickSize[1]];var span=axis.max-axis.min;var suffix=opts.twelveHourClock?" %p":"";var hourCode=opts.twelveHourClock?"%I":"%H";var fmt;if(t<timeUnitSize.minute){fmt=hourCode+":%M:%S"+suffix}else if(t<timeUnitSize.day){if(span<2*timeUnitSize.day){fmt=hourCode+":%M"+suffix}else{fmt="%b %d "+hourCode+":%M"+suffix}}else if(t<timeUnitSize.month){fmt="%b %d"}else if(useQuarters&&t<timeUnitSize.quarter||!useQuarters&&t<timeUnitSize.year){if(span<timeUnitSize.year){fmt="%b"}else{fmt="%b %Y"}}else if(useQuarters&&t<timeUnitSize.year){if(span<timeUnitSize.year){fmt="Q%q"}else{fmt="Q%q %Y"}}else{fmt="%Y"}var rt=formatDate(d,fmt,opts.monthNames,opts.dayNames);return rt}}})})}$.plot.plugins.push({init:init,options:options,name:"time",version:"1.0"});$.plot.formatDate=formatDate})(jQuery); \ No newline at end of file diff --git a/lsp/plugins/jquery.js b/lsp/plugins/jquery.js new file mode 100755 index 00000000..38837795 --- /dev/null +++ b/lsp/plugins/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v1.8.3 jquery.com | jquery.org/license */ +(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r<i;r++)v.event.add(t,n,u[n][r])}o.data&&(o.data=v.extend({},o.data))}function Ot(e,t){var n;if(t.nodeType!==1)return;t.clearAttributes&&t.clearAttributes(),t.mergeAttributes&&t.mergeAttributes(e),n=t.nodeName.toLowerCase(),n==="object"?(t.parentNode&&(t.outerHTML=e.outerHTML),v.support.html5Clone&&e.innerHTML&&!v.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):n==="input"&&Et.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):n==="option"?t.selected=e.defaultSelected:n==="input"||n==="textarea"?t.defaultValue=e.defaultValue:n==="script"&&t.text!==e.text&&(t.text=e.text),t.removeAttribute(v.expando)}function Mt(e){return typeof e.getElementsByTagName!="undefined"?e.getElementsByTagName("*"):typeof e.querySelectorAll!="undefined"?e.querySelectorAll("*"):[]}function _t(e){Et.test(e.type)&&(e.defaultChecked=e.checked)}function Qt(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Jt.length;while(i--){t=Jt[i]+n;if(t in e)return t}return r}function Gt(e,t){return e=t||e,v.css(e,"display")==="none"||!v.contains(e.ownerDocument,e)}function Yt(e,t){var n,r,i=[],s=0,o=e.length;for(;s<o;s++){n=e[s];if(!n.style)continue;i[s]=v._data(n,"olddisplay"),t?(!i[s]&&n.style.display==="none"&&(n.style.display=""),n.style.display===""&&Gt(n)&&(i[s]=v._data(n,"olddisplay",nn(n.nodeName)))):(r=Dt(n,"display"),!i[s]&&r!=="none"&&v._data(n,"olddisplay",r))}for(s=0;s<o;s++){n=e[s];if(!n.style)continue;if(!t||n.style.display==="none"||n.style.display==="")n.style.display=t?i[s]||"":"none"}return e}function Zt(e,t,n){var r=Rt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function en(e,t,n,r){var i=n===(r?"border":"content")?4:t==="width"?1:0,s=0;for(;i<4;i+=2)n==="margin"&&(s+=v.css(e,n+$t[i],!0)),r?(n==="content"&&(s-=parseFloat(Dt(e,"padding"+$t[i]))||0),n!=="margin"&&(s-=parseFloat(Dt(e,"border"+$t[i]+"Width"))||0)):(s+=parseFloat(Dt(e,"padding"+$t[i]))||0,n!=="padding"&&(s+=parseFloat(Dt(e,"border"+$t[i]+"Width"))||0));return s}function tn(e,t,n){var r=t==="width"?e.offsetWidth:e.offsetHeight,i=!0,s=v.support.boxSizing&&v.css(e,"boxSizing")==="border-box";if(r<=0||r==null){r=Dt(e,t);if(r<0||r==null)r=e.style[t];if(Ut.test(r))return r;i=s&&(v.support.boxSizingReliable||r===e.style[t]),r=parseFloat(r)||0}return r+en(e,t,n||(s?"border":"content"),i)+"px"}function nn(e){if(Wt[e])return Wt[e];var t=v("<"+e+">").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write("<!doctype html><html><body>"),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u<a;u++)r=o[u],s=/^\+/.test(r),s&&(r=r.substr(1)||"*"),i=e[r]=e[r]||[],i[s?"unshift":"push"](n)}}function kn(e,n,r,i,s,o){s=s||n.dataTypes[0],o=o||{},o[s]=!0;var u,a=e[s],f=0,l=a?a.length:0,c=e===Sn;for(;f<l&&(c||!u);f++)u=a[f](n,r,i),typeof u=="string"&&(!c||o[u]?u=t:(n.dataTypes.unshift(u),u=kn(e,n,r,i,u,o)));return(c||!u)&&!o["*"]&&(u=kn(e,n,r,i,"*",o)),u}function Ln(e,n){var r,i,s=v.ajaxSettings.flatOptions||{};for(r in n)n[r]!==t&&((s[r]?e:i||(i={}))[r]=n[r]);i&&v.extend(!0,e,i)}function An(e,n,r){var i,s,o,u,a=e.contents,f=e.dataTypes,l=e.responseFields;for(s in l)s in r&&(n[l[s]]=r[s]);while(f[0]==="*")f.shift(),i===t&&(i=e.mimeType||n.getResponseHeader("content-type"));if(i)for(s in a)if(a[s]&&a[s].test(i)){f.unshift(s);break}if(f[0]in r)o=f[0];else{for(s in r){if(!f[0]||e.converters[s+" "+f[0]]){o=s;break}u||(u=s)}o=o||u}if(o)return o!==f[0]&&f.unshift(o),r[o]}function On(e,t){var n,r,i,s,o=e.dataTypes.slice(),u=o[0],a={},f=0;e.dataFilter&&(t=e.dataFilter(t,e.dataType));if(o[1])for(n in e.converters)a[n.toLowerCase()]=e.converters[n];for(;i=o[++f];)if(i!=="*"){if(u!=="*"&&u!==i){n=a[u+" "+i]||a["* "+i];if(!n)for(r in a){s=r.split(" ");if(s[1]===i){n=a[u+" "+s[0]]||a["* "+s[0]];if(n){n===!0?n=a[r]:a[r]!==!0&&(i=s[0],o.splice(f--,0,i));break}}}if(n!==!0)if(n&&e["throws"])t=n(t);else try{t=n(t)}catch(l){return{state:"parsererror",error:n?l:"No conversion from "+u+" to "+i}}}u=i}return{state:"success",data:t}}function Fn(){try{return new e.XMLHttpRequest}catch(t){}}function In(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function $n(){return setTimeout(function(){qn=t},0),qn=v.now()}function Jn(e,t){v.each(t,function(t,n){var r=(Vn[t]||[]).concat(Vn["*"]),i=0,s=r.length;for(;i<s;i++)if(r[i].call(e,t,n))return})}function Kn(e,t,n){var r,i=0,s=0,o=Xn.length,u=v.Deferred().always(function(){delete a.elem}),a=function(){var t=qn||$n(),n=Math.max(0,f.startTime+f.duration-t),r=n/f.duration||0,i=1-r,s=0,o=f.tweens.length;for(;s<o;s++)f.tweens[s].run(i);return u.notifyWith(e,[f,i,n]),i<1&&o?n:(u.resolveWith(e,[f]),!1)},f=u.promise({elem:e,props:v.extend({},t),opts:v.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:qn||$n(),duration:n.duration,tweens:[],createTween:function(t,n,r){var i=v.Tween(e,f.opts,t,n,f.opts.specialEasing[t]||f.opts.easing);return f.tweens.push(i),i},stop:function(t){var n=0,r=t?f.tweens.length:0;for(;n<r;n++)f.tweens[n].run(1);return t?u.resolveWith(e,[f,t]):u.rejectWith(e,[f,t]),this}}),l=f.props;Qn(l,f.opts.specialEasing);for(;i<o;i++){r=Xn[i].call(f,e,l,f.opts);if(r)return r}return Jn(f,l),v.isFunction(f.opts.start)&&f.opts.start.call(e,f),v.fx.timer(v.extend(a,{anim:f,queue:f.opts.queue,elem:e})),f.progress(f.opts.progress).done(f.opts.done,f.opts.complete).fail(f.opts.fail).always(f.opts.always)}function Qn(e,t){var n,r,i,s,o;for(n in e){r=v.camelCase(n),i=t[r],s=e[n],v.isArray(s)&&(i=s[1],s=e[n]=s[0]),n!==r&&(e[r]=s,delete e[n]),o=v.cssHooks[r];if(o&&"expand"in o){s=o.expand(s),delete e[r];for(n in s)n in e||(e[n]=s[n],t[n]=i)}else t[r]=i}}function Gn(e,t,n){var r,i,s,o,u,a,f,l,c,h=this,p=e.style,d={},m=[],g=e.nodeType&&Gt(e);n.queue||(l=v._queueHooks(e,"fx"),l.unqueued==null&&(l.unqueued=0,c=l.empty.fire,l.empty.fire=function(){l.unqueued||c()}),l.unqueued++,h.always(function(){h.always(function(){l.unqueued--,v.queue(e,"fx").length||l.empty.fire()})})),e.nodeType===1&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],v.css(e,"display")==="inline"&&v.css(e,"float")==="none"&&(!v.support.inlineBlockNeedsLayout||nn(e.nodeName)==="inline"?p.display="inline-block":p.zoom=1)),n.overflow&&(p.overflow="hidden",v.support.shrinkWrapBlocks||h.done(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t){s=t[r];if(Un.exec(s)){delete t[r],a=a||s==="toggle";if(s===(g?"hide":"show"))continue;m.push(r)}}o=m.length;if(o){u=v._data(e,"fxshow")||v._data(e,"fxshow",{}),"hidden"in u&&(g=u.hidden),a&&(u.hidden=!g),g?v(e).show():h.done(function(){v(e).hide()}),h.done(function(){var t;v.removeData(e,"fxshow",!0);for(t in d)v.style(e,t,d[t])});for(r=0;r<o;r++)i=m[r],f=h.createTween(i,g?u[i]:0),d[i]=u[i]||v.style(e,i),i in u||(u[i]=f.start,g&&(f.end=f.start,f.start=i==="width"||i==="height"?1:0))}}function Yn(e,t,n,r,i){return new Yn.prototype.init(e,t,n,r,i)}function Zn(e,t){var n,r={height:e},i=0;t=t?1:0;for(;i<4;i+=2-t)n=$t[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function tr(e){return v.isWindow(e)?e:e.nodeType===9?e.defaultView||e.parentWindow:!1}var n,r,i=e.document,s=e.location,o=e.navigator,u=e.jQuery,a=e.$,f=Array.prototype.push,l=Array.prototype.slice,c=Array.prototype.indexOf,h=Object.prototype.toString,p=Object.prototype.hasOwnProperty,d=String.prototype.trim,v=function(e,t){return new v.fn.init(e,t,n)},m=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,g=/\S/,y=/\s+/,b=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,w=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a<f;a++)if((e=arguments[a])!=null)for(n in e){r=u[n],i=e[n];if(u===i)continue;l&&i&&(v.isPlainObject(i)||(s=v.isArray(i)))?(s?(s=!1,o=r&&v.isArray(r)?r:[]):o=r&&v.isPlainObject(r)?r:{},u[n]=v.extend(l,o,i)):i!==t&&(u[n]=i)}return u},v.extend({noConflict:function(t){return e.$===v&&(e.$=a),t&&e.jQuery===v&&(e.jQuery=u),v},isReady:!1,readyWait:1,holdReady:function(e){e?v.readyWait++:v.ready(!0)},ready:function(e){if(e===!0?--v.readyWait:v.isReady)return;if(!i.body)return setTimeout(v.ready,1);v.isReady=!0;if(e!==!0&&--v.readyWait>0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s<o;)if(n.apply(e[s++],r)===!1)break}else if(u){for(i in e)if(n.call(e[i],i,e[i])===!1)break}else for(;s<o;)if(n.call(e[s],s,e[s++])===!1)break;return e},trim:d&&!d.call("\ufeff\u00a0")?function(e){return e==null?"":d.call(e)}:function(e){return e==null?"":(e+"").replace(b,"")},makeArray:function(e,t){var n,r=t||[];return e!=null&&(n=v.type(e),e.length==null||n==="string"||n==="function"||n==="regexp"||v.isWindow(e)?f.call(r,e):v.merge(r,e)),r},inArray:function(e,t,n){var r;if(t){if(c)return c.call(t,e,n);r=t.length,n=n?n<0?Math.max(0,r+n):n:0;for(;n<r;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,s=0;if(typeof r=="number")for(;s<r;s++)e[i++]=n[s];else while(n[s]!==t)e[i++]=n[s++];return e.length=i,e},grep:function(e,t,n){var r,i=[],s=0,o=e.length;n=!!n;for(;s<o;s++)r=!!t(e[s],s),n!==r&&i.push(e[s]);return i},map:function(e,n,r){var i,s,o=[],u=0,a=e.length,f=e instanceof v||a!==t&&typeof a=="number"&&(a>0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u<a;u++)i=n(e[u],u,r),i!=null&&(o[o.length]=i);else for(s in e)i=n(e[s],s,r),i!=null&&(o[o.length]=i);return o.concat.apply([],o)},guid:1,proxy:function(e,n){var r,i,s;return typeof n=="string"&&(r=e[n],n=e,e=r),v.isFunction(e)?(i=l.call(arguments,2),s=function(){return e.apply(n,i.concat(l.call(arguments)))},s.guid=e.guid=e.guid||v.guid++,s):t},access:function(e,n,r,i,s,o,u){var a,f=r==null,l=0,c=e.length;if(r&&typeof r=="object"){for(l in r)v.access(e,n,l,r[l],1,o,i);s=1}else if(i!==t){a=u===t&&v.isFunction(i),f&&(a?(a=n,n=function(e,t,n){return a.call(v(e),n)}):(n.call(e,i),n=null));if(n)for(;l<c;l++)n(e[l],r,a?i.call(e[l],l,n(e[l],r)):i,u);s=1}return s?e:f?n.call(e):c?n(e[0],r):o},now:function(){return(new Date).getTime()}}),v.ready.promise=function(t){if(!r){r=v.Deferred();if(i.readyState==="complete")setTimeout(v.ready,1);else if(i.addEventListener)i.addEventListener("DOMContentLoaded",A,!1),e.addEventListener("load",v.ready,!1);else{i.attachEvent("onreadystatechange",A),e.attachEvent("onload",v.ready);var n=!1;try{n=e.frameElement==null&&i.documentElement}catch(s){}n&&n.doScroll&&function o(){if(!v.isReady){try{n.doScroll("left")}catch(e){return setTimeout(o,50)}v.ready()}}()}}return r.promise(t)},v.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(e,t){O["[object "+t+"]"]=t.toLowerCase()}),n=v(i);var M={};v.Callbacks=function(e){e=typeof e=="string"?M[e]||_(e):v.extend({},e);var n,r,i,s,o,u,a=[],f=!e.once&&[],l=function(t){n=e.memory&&t,r=!0,u=s||0,s=0,o=a.length,i=!0;for(;a&&u<o;u++)if(a[u].apply(t[0],t[1])===!1&&e.stopOnFalse){n=!1;break}i=!1,a&&(f?f.length&&l(f.shift()):n?a=[]:c.disable())},c={add:function(){if(a){var t=a.length;(function r(t){v.each(t,function(t,n){var i=v.type(n);i==="function"?(!e.unique||!c.has(n))&&a.push(n):n&&n.length&&i!=="string"&&r(n)})})(arguments),i?o=a.length:n&&(s=t,l(n))}return this},remove:function(){return a&&v.each(arguments,function(e,t){var n;while((n=v.inArray(t,a,n))>-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t<r;t++)n[t]&&v.isFunction(n[t].promise)?n[t].promise().done(o(t,f,n)).fail(s.reject).progress(o(t,a,u)):--i}return i||s.resolveWith(f,n),s.promise()}}),v.support=function(){var t,n,r,s,o,u,a,f,l,c,h,p=i.createElement("div");p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="<table><tr><td></td><td>t</td></tr></table>",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="<div></div>",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i<s;i++)delete r[t[i]];if(!(n?B:v.isEmptyObject)(r))return}}if(!n){delete u[a].data;if(!B(u[a]))return}o?v.cleanData([e],!0):v.support.deleteExpando||u!=u.window?delete u[a]:u[a]=null},_data:function(e,t,n){return v.data(e,t,n,!0)},acceptData:function(e){var t=e.nodeName&&v.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),v.fn.extend({data:function(e,n){var r,i,s,o,u,a=this[0],f=0,l=null;if(e===t){if(this.length){l=v.data(a);if(a.nodeType===1&&!v._data(a,"parsedAttrs")){s=a.attributes;for(u=s.length;f<u;f++)o=s[f].name,o.indexOf("data-")||(o=v.camelCase(o.substring(5)),H(a,o,l[o]));v._data(a,"parsedAttrs",!0)}}return l}return typeof e=="object"?this.each(function(){v.data(this,e)}):(r=e.split(".",2),r[1]=r[1]?"."+r[1]:"",i=r[1]+"!",v.access(this,function(n){if(n===t)return l=this.triggerHandler("getData"+i,[r[0]]),l===t&&a&&(l=v.data(a,e),l=H(a,e,l)),l===t&&r[1]?this.data(r[0]):l;r[1]=n,this.each(function(){var t=v(this);t.triggerHandler("setData"+i,r),v.data(this,e,n),t.triggerHandler("changeData"+i,r)})},null,n,arguments.length>1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length<r?v.queue(this[0],e):n===t?this:this.each(function(){var t=v.queue(this,e,n);v._queueHooks(this,e),e==="fx"&&t[0]!=="inprogress"&&v.dequeue(this,e)})},dequeue:function(e){return this.each(function(){v.dequeue(this,e)})},delay:function(e,t){return e=v.fx?v.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,s=v.Deferred(),o=this,u=this.length,a=function(){--i||s.resolveWith(o,[o])};typeof e!="string"&&(n=e,e=t),e=e||"fx";while(u--)r=v._data(o[u],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(a));return a(),s.promise(n)}});var j,F,I,q=/[\t\r\n]/g,R=/\r/g,U=/^(?:button|input)$/i,z=/^(?:button|input|object|select|textarea)$/i,W=/^a(?:rea|)$/i,X=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,V=v.support.getSetAttribute;v.fn.extend({attr:function(e,t){return v.access(this,v.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n<r;n++){i=this[n];if(i.nodeType===1)if(!i.className&&t.length===1)i.className=e;else{s=" "+i.className+" ";for(o=0,u=t.length;o<u;o++)s.indexOf(" "+t[o]+" ")<0&&(s+=t[o]+" ");i.className=v.trim(s)}}}return this},removeClass:function(e){var n,r,i,s,o,u,a;if(v.isFunction(e))return this.each(function(t){v(this).removeClass(e.call(this,t,this.className))});if(e&&typeof e=="string"||e===t){n=(e||"").split(y);for(u=0,a=this.length;u<a;u++){i=this[u];if(i.nodeType===1&&i.className){r=(" "+i.className+" ").replace(q," ");for(s=0,o=n.length;s<o;s++)while(r.indexOf(" "+n[s]+" ")>=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n<r;n++)if(this[n].nodeType===1&&(" "+this[n].className+" ").replace(q," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a<u;a++){n=r[a];if((n.selected||a===i)&&(v.support.optDisabled?!n.disabled:n.getAttribute("disabled")===null)&&(!n.parentNode.disabled||!v.nodeName(n.parentNode,"optgroup"))){t=v(n).val();if(s)return t;o.push(t)}}return o},set:function(e,t){var n=v.makeArray(t);return v(e).find("option").each(function(){this.selected=v.inArray(v(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o<r.length;o++)i=r[o],i&&(n=v.propFix[i]||i,s=X.test(i),s||v.attr(e,i,""),e.removeAttribute(V?i:n),s&&n in e&&(e[n]=!1))}},attrHooks:{type:{set:function(e,t){if(U.test(e.nodeName)&&e.parentNode)v.error("type property can't be changed");else if(!v.support.radioValue&&t==="radio"&&v.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}},value:{get:function(e,t){return j&&v.nodeName(e,"button")?j.get(e,t):t in e?e.value:null},set:function(e,t,n){if(j&&v.nodeName(e,"button"))return j.set(e,t,n);e.value=t}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,s,o,u=e.nodeType;if(!e||u===3||u===8||u===2)return;return o=u!==1||!v.isXMLDoc(e),o&&(n=v.propFix[n]||n,s=v.propHooks[n]),r!==t?s&&"set"in s&&(i=s.set(e,r,n))!==t?i:e[n]=r:s&&"get"in s&&(i=s.get(e,n))!==null?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):z.test(e.nodeName)||W.test(e.nodeName)&&e.href?0:t}}}}),F={get:function(e,n){var r,i=v.prop(e,n);return i===!0||typeof i!="boolean"&&(r=e.getAttributeNode(n))&&r.nodeValue!==!1?n.toLowerCase():t},set:function(e,t,n){var r;return t===!1?v.removeAttr(e,n):(r=v.propFix[n]||n,r in e&&(e[r]=!0),e.setAttribute(n,n.toLowerCase())),n}},V||(I={name:!0,id:!0,coords:!0},j=v.valHooks.button={get:function(e,n){var r;return r=e.getAttributeNode(n),r&&(I[n]?r.value!=="":r.specified)?r.value:t},set:function(e,t,n){var r=e.getAttributeNode(n);return r||(r=i.createAttribute(n),e.setAttributeNode(r)),r.value=t+""}},v.each(["width","height"],function(e,t){v.attrHooks[t]=v.extend(v.attrHooks[t],{set:function(e,n){if(n==="")return e.setAttribute(t,"auto"),n}})}),v.attrHooks.contenteditable={get:j.get,set:function(e,t,n){t===""&&(t="false"),j.set(e,t,n)}}),v.support.hrefNormalized||v.each(["href","src","width","height"],function(e,n){v.attrHooks[n]=v.extend(v.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return r===null?t:r}})}),v.support.style||(v.attrHooks.style={get:function(e){return e.style.cssText.toLowerCase()||t},set:function(e,t){return e.style.cssText=t+""}}),v.support.optSelected||(v.propHooks.selected=v.extend(v.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),v.support.enctype||(v.propFix.enctype="encoding"),v.support.checkOn||v.each(["radio","checkbox"],function(){v.valHooks[this]={get:function(e){return e.getAttribute("value")===null?"on":e.value}}}),v.each(["radio","checkbox"],function(){v.valHooks[this]=v.extend(v.valHooks[this],{set:function(e,t){if(v.isArray(t))return e.checked=v.inArray(v(e).val(),t)>=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f<n.length;f++){l=J.exec(n[f])||[],c=l[1],h=(l[2]||"").split(".").sort(),g=v.event.special[c]||{},c=(s?g.delegateType:g.bindType)||c,g=v.event.special[c]||{},p=v.extend({type:c,origType:l[1],data:i,handler:r,guid:r.guid,selector:s,needsContext:s&&v.expr.match.needsContext.test(s),namespace:h.join(".")},d),m=a[c];if(!m){m=a[c]=[],m.delegateCount=0;if(!g.setup||g.setup.call(e,i,h,u)===!1)e.addEventListener?e.addEventListener(c,u,!1):e.attachEvent&&e.attachEvent("on"+c,u)}g.add&&(g.add.call(e,p),p.handler.guid||(p.handler.guid=r.guid)),s?m.splice(m.delegateCount++,0,p):m.push(p),v.event.global[c]=!0}e=null},global:{},remove:function(e,t,n,r,i){var s,o,u,a,f,l,c,h,p,d,m,g=v.hasData(e)&&v._data(e);if(!g||!(h=g.events))return;t=v.trim(Z(t||"")).split(" ");for(s=0;s<t.length;s++){o=J.exec(t[s])||[],u=a=o[1],f=o[2];if(!u){for(u in h)v.event.remove(e,u+t[s],n,r,!0);continue}p=v.event.special[u]||{},u=(r?p.delegateType:p.bindType)||u,d=h[u]||[],l=d.length,f=f?new RegExp("(^|\\.)"+f.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null;for(c=0;c<d.length;c++)m=d[c],(i||a===m.origType)&&(!n||n.guid===m.guid)&&(!f||f.test(m.namespace))&&(!r||r===m.selector||r==="**"&&m.selector)&&(d.splice(c--,1),m.selector&&d.delegateCount--,p.remove&&p.remove.call(e,m));d.length===0&&l!==d.length&&((!p.teardown||p.teardown.call(e,f,g.handle)===!1)&&v.removeEvent(e,u,g.handle),delete h[u])}v.isEmptyObject(h)&&(delete g.handle,v.removeData(e,"events",!0))},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(n,r,s,o){if(!s||s.nodeType!==3&&s.nodeType!==8){var u,a,f,l,c,h,p,d,m,g,y=n.type||n,b=[];if(Y.test(y+v.event.triggered))return;y.indexOf("!")>=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f<m.length&&!n.isPropagationStopped();f++)l=m[f][0],n.type=m[f][1],d=(v._data(l,"events")||{})[n.type]&&v._data(l,"handle"),d&&d.apply(l,r),d=h&&l[h],d&&v.acceptData(l)&&d.apply&&d.apply(l,r)===!1&&n.preventDefault();return n.type=y,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(s.ownerDocument,r)===!1)&&(y!=="click"||!v.nodeName(s,"a"))&&v.acceptData(s)&&h&&s[y]&&(y!=="focus"&&y!=="blur"||n.target.offsetWidth!==0)&&!v.isWindow(s)&&(c=s[h],c&&(s[h]=null),v.event.triggered=y,s[y](),v.event.triggered=t,c&&(s[h]=c)),n.result}return},dispatch:function(n){n=v.event.fix(n||e.event);var r,i,s,o,u,a,f,c,h,p,d=(v._data(this,"events")||{})[n.type]||[],m=d.delegateCount,g=l.call(arguments),y=!n.exclusive&&!n.namespace,b=v.event.special[n.type]||{},w=[];g[0]=n,n.delegateTarget=this;if(b.preDispatch&&b.preDispatch.call(this,n)===!1)return;if(m&&(!n.button||n.type!=="click"))for(s=n.target;s!=this;s=s.parentNode||this)if(s.disabled!==!0||n.type!=="click"){u={},f=[];for(r=0;r<m;r++)c=d[r],h=c.selector,u[h]===t&&(u[h]=c.needsContext?v(h,this).index(s)>=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r<w.length&&!n.isPropagationStopped();r++){a=w[r],n.currentTarget=a.elem;for(i=0;i<a.matches.length&&!n.isImmediatePropagationStopped();i++){c=a.matches[i];if(y||!n.namespace&&!c.namespace||n.namespace_re&&n.namespace_re.test(c.namespace))n.data=c.data,n.handleObj=c,o=((v.event.special[c.origType]||{}).handle||c.handler).apply(a.elem,g),o!==t&&(n.result=o,o===!1&&(n.preventDefault(),n.stopPropagation()))}}return b.postDispatch&&b.postDispatch.call(this,n),n.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return e.which==null&&(e.which=t.charCode!=null?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,s,o,u=n.button,a=n.fromElement;return e.pageX==null&&n.clientX!=null&&(r=e.target.ownerDocument||i,s=r.documentElement,o=r.body,e.pageX=n.clientX+(s&&s.scrollLeft||o&&o.scrollLeft||0)-(s&&s.clientLeft||o&&o.clientLeft||0),e.pageY=n.clientY+(s&&s.scrollTop||o&&o.scrollTop||0)-(s&&s.clientTop||o&&o.clientTop||0)),!e.relatedTarget&&a&&(e.relatedTarget=a===e.target?n.toElement:a),!e.which&&u!==t&&(e.which=u&1?1:u&2?3:u&4?2:0),e}},fix:function(e){if(e[v.expando])return e;var t,n,r=e,s=v.event.fixHooks[e.type]||{},o=s.props?this.props.concat(s.props):this.props;e=v.Event(r);for(t=o.length;t;)n=o[--t],e[n]=r[n];return e.target||(e.target=r.srcElement||i),e.target.nodeType===3&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,r):e},special:{load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(e,t,n){v.isWindow(this)&&(this.onbeforeunload=n)},teardown:function(e,t){this.onbeforeunload===t&&(this.onbeforeunload=null)}}},simulate:function(e,t,n,r){var i=v.extend(new v.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?v.event.trigger(i,null,t):v.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},v.event.handle=v.event.dispatch,v.removeEvent=i.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]=="undefined"&&(e[r]=null),e.detachEvent(r,n))},v.Event=function(e,t){if(!(this instanceof v.Event))return new v.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?tt:et):this.type=e,t&&v.extend(this,t),this.timeStamp=e&&e.timeStamp||v.now(),this[v.expando]=!0},v.Event.prototype={preventDefault:function(){this.isDefaultPrevented=tt;var e=this.originalEvent;if(!e)return;e.preventDefault?e.preventDefault():e.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=tt;var e=this.originalEvent;if(!e)return;e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=tt,this.stopPropagation()},isDefaultPrevented:et,isPropagationStopped:et,isImmediatePropagationStopped:et},v.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){v.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,s=e.handleObj,o=s.selector;if(!i||i!==r&&!v.contains(r,i))e.type=s.origType,n=s.handler.apply(this,arguments),e.type=t;return n}}}),v.support.submitBubbles||(v.event.special.submit={setup:function(){if(v.nodeName(this,"form"))return!1;v.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=v.nodeName(n,"input")||v.nodeName(n,"button")?n.form:t;r&&!v._data(r,"_submit_attached")&&(v.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),v._data(r,"_submit_attached",!0))})},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&v.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){if(v.nodeName(this,"form"))return!1;v.event.remove(this,"._submit")}}),v.support.changeBubbles||(v.event.special.change={setup:function(){if($.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")v.event.add(this,"propertychange._change",function(e){e.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),v.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),v.event.simulate("change",this,e,!0)});return!1}v.event.add(this,"beforeactivate._change",function(e){var t=e.target;$.test(t.nodeName)&&!v._data(t,"_change_attached")&&(v.event.add(t,"change._change",function(e){this.parentNode&&!e.isSimulated&&!e.isTrigger&&v.event.simulate("change",this.parentNode,e,!0)}),v._data(t,"_change_attached",!0))})},handle:function(e){var t=e.target;if(this!==t||e.isSimulated||e.isTrigger||t.type!=="radio"&&t.type!=="checkbox")return e.handleObj.handler.apply(this,arguments)},teardown:function(){return v.event.remove(this,"._change"),!$.test(this.nodeName)}}),v.support.focusinBubbles||v.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){v.event.simulate(t,e.target,v.event.fix(e),!0)};v.event.special[t]={setup:function(){n++===0&&i.addEventListener(e,r,!0)},teardown:function(){--n===0&&i.removeEventListener(e,r,!0)}}}),v.fn.extend({on:function(e,n,r,i,s){var o,u;if(typeof e=="object"){typeof n!="string"&&(r=r||n,n=t);for(u in e)this.on(u,n,r,e[u],s);return this}r==null&&i==null?(i=n,r=n=t):i==null&&(typeof n=="string"?(i=r,r=t):(i=r,r=n,n=t));if(i===!1)i=et;else if(!i)return this;return s===1&&(o=i,i=function(e){return v().off(e),o.apply(this,arguments)},i.guid=o.guid||(o.guid=v.guid++)),this.each(function(){v.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,s;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,v(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if(typeof e=="object"){for(s in e)this.off(s,n,e[s]);return this}if(n===!1||typeof n=="function")r=n,n=t;return r===!1&&(r=et),this.each(function(){v.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},live:function(e,t,n){return v(this.context).on(e,this.selector,t,n),this},die:function(e,t){return v(this.context).off(e,this.selector||"**",t),this},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return arguments.length===1?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){v.event.trigger(e,t,this)})},triggerHandler:function(e,t){if(this[0])return v.event.trigger(e,t,this[0],!0)},toggle:function(e){var t=arguments,n=e.guid||v.guid++,r=0,i=function(n){var i=(v._data(this,"lastToggle"+e.guid)||0)%r;return v._data(this,"lastToggle"+e.guid,i+1),n.preventDefault(),t[i].apply(this,arguments)||!1};i.guid=n;while(r<t.length)t[r++].guid=n;return this.click(i)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),v.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){v.fn[t]=function(e,n){return n==null&&(n=e,e=null),arguments.length>0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u<a;u++)if(s=e[u])if(!n||n(s,r,i))o.push(s),f&&t.push(u);return o}function ct(e,t,n,r,i,s){return r&&!r[d]&&(r=ct(r)),i&&!i[d]&&(i=ct(i,s)),N(function(s,o,u,a){var f,l,c,h=[],p=[],d=o.length,v=s||dt(t||"*",u.nodeType?[u]:u,[]),m=e&&(s||!t)?lt(v,h,e,u,a):v,g=n?i||(s?e:d||r)?[]:o:m;n&&n(m,g,u,a);if(r){f=lt(g,p),r(f,[],u,a),l=f.length;while(l--)if(c=f[l])g[p[l]]=!(m[p[l]]=c)}if(s){if(i||e){if(i){f=[],l=g.length;while(l--)(c=g[l])&&f.push(m[l]=c);i(null,g=[],f,a)}l=g.length;while(l--)(c=g[l])&&(f=i?T.call(s,c):h[l])>-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a<s;a++)if(n=i.relative[e[a].type])h=[at(ft(h),n)];else{n=i.filter[e[a].type].apply(null,e[a].matches);if(n[d]){r=++a;for(;r<s;r++)if(i.relative[e[r].type])break;return ct(a>1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a<r&&ht(e.slice(a,r)),r<s&&ht(e=e.slice(r)),r<s&&e.join(""))}h.push(n)}return ft(h)}function pt(e,t){var r=t.length>0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r<i;r++)nt(e,t[r],n);return n}function vt(e,t,n,r,s){var o,u,f,l,c,h=ut(e),p=h.length;if(!r&&h.length===1){u=h[0]=h[0].slice(0);if(u.length>2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;t<n;t++)if(this[t]===e)return t;return-1},N=function(e,t){return e[d]=t==null||t,e},C=function(){var e={},t=[];return N(function(n,r){return t.push(n)>i.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="<a name='"+d+"'></a><div name='"+d+"'></div>",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:st(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:st(function(e,t,n){for(var r=n<0?n+t:n;--r>=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}},f=y.compareDocumentPosition?function(e,t){return e===t?(l=!0,0):(!e.compareDocumentPosition||!t.compareDocumentPosition?e.compareDocumentPosition:e.compareDocumentPosition(t)&4)?-1:1}:function(e,t){if(e===t)return l=!0,0;if(e.sourceIndex&&t.sourceIndex)return e.sourceIndex-t.sourceIndex;var n,r,i=[],s=[],o=e.parentNode,u=t.parentNode,a=o;if(o===u)return ot(e,t);if(!o)return-1;if(!u)return 1;while(a)i.unshift(a),a=a.parentNode;a=u;while(a)s.unshift(a),a=a.parentNode;n=i.length,r=s.length;for(var f=0;f<n&&f<r;f++)if(i[f]!==s[f])return ot(i[f],s[f]);return f===n?ot(e,s[f],-1):ot(i[f],t,1)},[0,0].sort(f),h=!l,nt.uniqueSort=function(e){var t,n=[],r=1,i=0;l=h,e.sort(f);if(l){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e},nt.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},a=nt.compile=function(e,t){var n,r=[],i=[],s=A[d][e+" "];if(!s){t||(t=ut(e)),n=t.length;while(n--)s=ht(t[n]),s[d]?r.push(s):i.push(s);s=A(e,pt(i,r))}return s},g.querySelectorAll&&function(){var e,t=vt,n=/'|\\/g,r=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,i=[":focus"],s=[":active"],u=y.matchesSelector||y.mozMatchesSelector||y.webkitMatchesSelector||y.oMatchesSelector||y.msMatchesSelector;K(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="<p test=''></p>",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="<input type='hidden'/>",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t<n;t++)if(v.contains(u[t],this))return!0});o=this.pushStack("","find",e);for(t=0,n=this.length;t<n;t++){r=o.length,v.find(e,this[t],o);if(t>0)for(i=r;i<o.length;i++)for(s=0;s<r;s++)if(o[s]===o[i]){o.splice(i--,1);break}}return o},has:function(e){var t,n=v(e,this),r=n.length;return this.filter(function(){for(t=0;t<r;t++)if(v.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1),"not",e)},filter:function(e){return this.pushStack(ft(this,e,!0),"filter",e)},is:function(e){return!!e&&(typeof e=="string"?st.test(e)?v(e,this.context).index(this[0])>=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r<i;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&n.nodeType!==11){if(o?o.index(n)>-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/<tbody/i,gt=/<|&#?\w+;/,yt=/<(?:script|style|link)/i,bt=/<(?:script|object|embed|option|style)/i,wt=new RegExp("<(?:"+ct+")[\\s/>]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,Nt={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X<div>","</div>"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1></$2>");try{for(;r<i;r++)n=this[r]||{},n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),n.innerHTML=e);n=0}catch(s){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){return ut(this[0])?this.length?this.pushStack(v(v.isFunction(e)?e():e),"replaceWith",e):this:v.isFunction(e)?this.each(function(t){var n=v(this),r=n.html();n.replaceWith(e.call(this,t,r))}):(typeof e!="string"&&(e=v(e).detach()),this.each(function(){var t=this.nextSibling,n=this.parentNode;v(this).remove(),t?v(t).before(e):v(n).append(e)}))},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=[].concat.apply([],e);var i,s,o,u,a=0,f=e[0],l=[],c=this.length;if(!v.support.checkClone&&c>1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a<c;a++)r.call(n&&v.nodeName(this[a],"table")?Lt(this[a],"tbody"):this[a],a===u?o:v.clone(o,!0,!0))}o=s=null,l.length&&v.each(l,function(e,t){t.src?v.ajax?v.ajax({url:t.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):v.error("no ajax"):v.globalEval((t.text||t.textContent||t.innerHTML||"").replace(Tt,"")),t.parentNode&&t.parentNode.removeChild(t)})}return this}}),v.buildFragment=function(e,n,r){var s,o,u,a=e[0];return n=n||i,n=!n.nodeType&&n[0]||n,n=n.ownerDocument||n,e.length===1&&typeof a=="string"&&a.length<512&&n===i&&a.charAt(0)==="<"&&!bt.test(a)&&(v.support.checkClone||!St.test(a))&&(v.support.html5Clone||!wt.test(a))&&(o=!0,s=v.fragments[a],u=s!==t),s||(s=n.createDocumentFragment(),v.clean(e,n,s,r),o&&(v.fragments[a]=u&&s)),{fragment:s,cacheable:o}},v.fragments={},v.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){v.fn[e]=function(n){var r,i=0,s=[],o=v(n),u=o.length,a=this.length===1&&this[0].parentNode;if((a==null||a&&a.nodeType===11&&a.childNodes.length===1)&&u===1)return o[t](this[0]),this;for(;i<u;i++)r=(i>0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1></$2>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]==="<table>"&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("<div>").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r<i;r++)n=e[r],Vn[n]=Vn[n]||[],Vn[n].unshift(t)},prefilter:function(e,t){t?Xn.unshift(e):Xn.push(e)}}),v.Tween=Yn,Yn.prototype={constructor:Yn,init:function(e,t,n,r,i,s){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=s||(v.cssNumber[n]?"":"px")},cur:function(){var e=Yn.propHooks[this.prop];return e&&e.get?e.get(this):Yn.propHooks._default.get(this)},run:function(e){var t,n=Yn.propHooks[this.prop];return this.options.duration?this.pos=t=v.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Yn.propHooks._default.set(this),this}},Yn.prototype.init.prototype=Yn.prototype,Yn.propHooks={_default:{get:function(e){var t;return e.elem[e.prop]==null||!!e.elem.style&&e.elem.style[e.prop]!=null?(t=v.css(e.elem,e.prop,!1,""),!t||t==="auto"?0:t):e.elem[e.prop]},set:function(e){v.fx.step[e.prop]?v.fx.step[e.prop](e):e.elem.style&&(e.elem.style[v.cssProps[e.prop]]!=null||v.cssHooks[e.prop])?v.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},Yn.propHooks.scrollTop=Yn.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},v.each(["toggle","show","hide"],function(e,t){var n=v.fn[t];v.fn[t]=function(r,i,s){return r==null||typeof r=="boolean"||!e&&v.isFunction(r)&&v.isFunction(i)?n.apply(this,arguments):this.animate(Zn(t,!0),r,i,s)}}),v.fn.extend({fadeTo:function(e,t,n,r){return this.filter(Gt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=v.isEmptyObject(e),s=v.speed(t,n,r),o=function(){var t=Kn(this,v.extend({},e),s);i&&t.stop(!0)};return i||s.queue===!1?this.each(o):this.queue(s.queue,o)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return typeof e!="string"&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=e!=null&&e+"queueHooks",s=v.timers,o=v._data(this);if(n)o[n]&&o[n].stop&&i(o[n]);else for(n in o)o[n]&&o[n].stop&&Wn.test(n)&&i(o[n]);for(n=s.length;n--;)s[n].elem===this&&(e==null||s[n].queue===e)&&(s[n].anim.stop(r),t=!1,s.splice(n,1));(t||!r)&&v.dequeue(this,e)})}}),v.each({slideDown:Zn("show"),slideUp:Zn("hide"),slideToggle:Zn("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){v.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),v.speed=function(e,t,n){var r=e&&typeof e=="object"?v.extend({},e):{complete:n||!n&&t||v.isFunction(e)&&e,duration:e,easing:n&&t||t&&!v.isFunction(t)&&t};r.duration=v.fx.off?0:typeof r.duration=="number"?r.duration:r.duration in v.fx.speeds?v.fx.speeds[r.duration]:v.fx.speeds._default;if(r.queue==null||r.queue===!0)r.queue="fx";return r.old=r.complete,r.complete=function(){v.isFunction(r.old)&&r.old.call(this),r.queue&&v.dequeue(this,r.queue)},r},v.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},v.timers=[],v.fx=Yn.prototype.init,v.fx.tick=function(){var e,n=v.timers,r=0;qn=v.now();for(;r<n.length;r++)e=n[r],!e()&&n[r]===e&&n.splice(r--,1);n.length||v.fx.stop(),qn=t},v.fx.timer=function(e){e()&&v.timers.push(e)&&!Rn&&(Rn=setInterval(v.fx.tick,v.fx.interval))},v.fx.interval=13,v.fx.stop=function(){clearInterval(Rn),Rn=null},v.fx.speeds={slow:600,fast:200,_default:400},v.fx.step={},v.expr&&v.expr.filters&&(v.expr.filters.animated=function(e){return v.grep(v.timers,function(t){return e===t.elem}).length});var er=/^(?:body|html)$/i;v.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){v.offset.setOffset(this,e,t)});var n,r,i,s,o,u,a,f={top:0,left:0},l=this[0],c=l&&l.ownerDocument;if(!c)return;return(r=c.body)===l?v.offset.bodyOffset(l):(n=c.documentElement,v.contains(n,l)?(typeof l.getBoundingClientRect!="undefined"&&(f=l.getBoundingClientRect()),i=tr(c),s=n.clientTop||r.clientTop||0,o=n.clientLeft||r.clientLeft||0,u=i.pageYOffset||n.scrollTop,a=i.pageXOffset||n.scrollLeft,{top:f.top+u-s,left:f.left+a-o}):f)},v.offset={bodyOffset:function(e){var t=e.offsetTop,n=e.offsetLeft;return v.support.doesNotIncludeMarginInBodyOffset&&(t+=parseFloat(v.css(e,"marginTop"))||0,n+=parseFloat(v.css(e,"marginLeft"))||0),{top:t,left:n}},setOffset:function(e,t,n){var r=v.css(e,"position");r==="static"&&(e.style.position="relative");var i=v(e),s=i.offset(),o=v.css(e,"top"),u=v.css(e,"left"),a=(r==="absolute"||r==="fixed")&&v.inArray("auto",[o,u])>-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); \ No newline at end of file diff --git a/lsp/md5.js b/lsp/plugins/md5.js similarity index 100% rename from lsp/md5.js rename to lsp/plugins/md5.js diff --git a/lsp/placeholder.js b/lsp/plugins/placeholder.js similarity index 100% rename from lsp/placeholder.js rename to lsp/plugins/placeholder.js diff --git a/lsp/tablesort.js b/lsp/plugins/tablesort.js similarity index 100% rename from lsp/tablesort.js rename to lsp/plugins/tablesort.js diff --git a/lsp/server.html b/lsp/server.html index ea792182..e9780973 100755 --- a/lsp/server.html +++ b/lsp/server.html @@ -2,63 +2,67 @@ <html> <head> <meta http-equiv='content-type' content='text/html;charset=utf-8' /> - <title>MistServer Manager</title> - <script src='jquery.js'></script> - <script src='placeholder.js'></script> - <script src='md5.js'></script> + <title>MistServer MI</title> + <script src='plugins/jquery.js'></script> + <script src='plugins/placeholder.js'></script> + <script src='plugins/md5.js'></script> + <script src='plugins/tablesort.js'></script> + <script src='plugins/jquery.flot.min.js'></script> + <script src='plugins/jquery.flot.time.min.js'></script> + <script src='plugins/jquery.flot.crosshair.min.js'></script> <script src='main.js'></script> <script src='pages.js'></script> - <script src='tablesort.js'></script> <link rel='stylesheet' href='main.css' /> - <script> - //these are placed here because the compression compiler does not deal with the eval function properly. - //enter the values of the settings object into their input fields - function enterSettings(){ - $('.isSetting').each(function(){ - var objpath = findObjPath($(this)); - var val = ''; - try { - eval('val = '+objpath+';'); - } - catch(e) { } - if ($(this).is('input,select')) { - $(this).val(val); - } - else { - $(this).text(val); - } - }); - } - //this function is for moving limits (LTS only) - function moveLimit(destination,streamName,objpath) { - if ((streamName == '_new_') || (destination != streamName[0])) { - var target; - if (destination == 'server') { - if (!settings.settings.config.limits) { - settings.settings.config.limits = []; - } - target = settings.settings.config.limits; - } - else { - destination = destination.replace('stream-',''); - if (!settings.settings.streams[destination].limits) { - settings.settings.streams[destination].limits = []; - } - target = settings.settings.streams[destination].limits; - } - - eval('target.push(settings.'+objpath+');'); - eval('delete settings.'+objpath+';'); + <link rel='icon' href='data:image/png;base64,AAABAAQAEBAAAAEAIABoBAAARgAAADAwAAABACAAqCUAAK4EAAAgIAAAAQAgAKgQAABWKgAAQEAAAAEAIAAoQgAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////ANu2kgfPsozBz7OM0NOxkBf///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDStYw+z7OM/8+zjP/Qsoxd////AP///wD///8A////AP///wD///8A////AP///wD///8A////AMzMmQXQso1n0LSMgc+0jKrPtI3E0LKMh86zjGjMzJkF////AP///wD///8A////AP+AgALMtooj////AM6yjVPOso2I0LWNJv///wDPtI06z7ONZf///wDQtY0mz7OMidCyi1b///8BzLaKI/+AgALPs4y2z7OM/9C1jsTSvJZE38+vEN/PrxDfz68Q28imwdrFo/DezrUf38+vEN/PrxDSvJZEz7SOxs+zjP/Ps4y20LOM18+zjP/RtpDd4dCybeLTtWji07Vo4tO1aNzJqfDaxqP94dK2d+LTtWji07Vo4NKzbNG2kN3Ps4z/0LOM19C1jSbPtIyfz7WLT8+zjJDPtIxf/4CAAuHStDPaw6GQ07uVleLTtmn///8A0LOLUdCzjJfNs4tR0LSNo9C1jSb///8AzMyZBc60jX3///8A0LSNQc+0jKbbxqSpz7ONZc+zjJvcyaiK0LWPrc+0jVX///8Az7ONf7+/gAj///8A////AP///wDQsoxx1biOEv///wDi1LaC0raPfc+zjfzPs4z/z7OMr+DQsJL///8Az6+PENC0i3f///8A////AP///wD///8AzLOMFM6xi3Li1bRO3sqsXM6zi43Qs4z5z7OM/8+zjLbYxKBW49O0XM+zjG/MuI8Z////AP///wD///8A////AP///wDQtIzn0LaP89CzjJ7RtY1T0LOLUdG0i2PRtY5IzrONk9C2jvDQtIzuqqqqA////wD///8A////AP///wD///8Az7OLwM6zi8bPs4tAz7OLQM+zi0DPs4tAz7OLQM+zi0DOs4y7z7SMz/+AgAL///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AAD+fwAA/n8AAPw/AAD37wAAHngAAB54AACudQAA+R8AAPofAAD8PwAA48cAAOfnAAD//wAA//8AACgAAAAwAAAAYAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDMu4gPzrSLmM+zjN7Ps4zuz7OMu8+yjDX///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANG5iwvPs4zYz7OM/8+zjP/Ps4z/z7OM/8+zjPvQs4pG////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+zjInPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4zg/4CAAv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+zjM/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LKOK////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+zjN3Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LOMPP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7+ABNCzjMzPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Os4z8z6+AEP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOso1Tz7OM2c+zjP/Ps4z9z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7SM38+zjFv///8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrWMPs+zjMjPs4z/z7OMyc+zi0DPsYxFz7OM5M+zjP/Ps4z/zrOM/M+yjIXMs4w8zrONws+zjP/Ps4zP0LSMR////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANG0iyzPs4y0z7OM/8+zjNnOs4xU////AP///wD///8AzMyZBc6zi7LPs4z/z7KMNf///wD///8A////ANGzi03Ps4zUz7OM/86zjL3OtY40////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDTsI0dz7SMn8+zjP3Qs4znzrOMaL+/gAT///8A////AP///wD///8A////AM+zjaXPs4z/zrONL////wD///8A////AP///wD/gIAC0LOLYc+zjOPPs4z+zrOMqMy2iiP///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0rSHEc+0i4vQs4z3z7OM8s60jX3RuYsL////AP///wD///8A////AP///wD///8A////AM+zjKvPs4z/z7KMNf///wD///8A////AP///wD///8A////AL+/gAjPs4x2z7OM78+zjPrPs4yU0a6LFv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANC0jVzPs4yg0LOLrM+zi3XMs4AK////AL+/gAjPs4x2z7OM78+zjPnQs42RzraSFf///wD///8A////AP///wD///8A////AP///wD///8A////AM6zi7LPs4z/z7GKO////wD///8A////AP///wD///8A////AP///wD///8A0rSHEc+0i4vQs4z3z7OM88+zjX/VqpUM////AMyzgArPs4t10LOLrM+zjKDQtI1c////AP///wC/v4AEz7OMtM+zjP/Ps4z/z7OM/8+zjP/Ps4za0LSMc8+zjOPPs4z+z7ONpdGyiyH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A49O0bdS8l/rPs4z/3cqq7+LTtlf///8A////AP///wD///8A////AP///wD///8A////AP///wDMs4gez7OMoM+zjP3Ps4zoz7KNe8+zjNvPs4z/z7OM/8+zjP/Ps4z/z7OMtL+/gATPso17z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+0jLrQsY0x////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDh1bZU4tO1/9S7l//Ps4z/3cqp/+LTtf3h1LY7////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANK1ji3Ps4u1z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+yjXvPs4zTz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LSN/t7Nqz3k1bUw5NW1MOTVtTDk1bUw5NW1MOTVtTDk1bUw5NW1MOTVtTDk1bUw5NW1MOTVtTDj07XF4tO1/9O7lv/Ps4z/3Mmo/+LTtf/i07Wv5NW1MOTVtTDk1bUw5NW1MOTVtTDk1bUw5NW1MOTVtTDk1bUw5NW1MOTVtTDezas90LSN/s+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjNPPs4zzz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/97Nrf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/9O6lf/Ps4z/3Min/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/eza3/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjPPPs4zKz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LOM/d7KqU3j07NA49OzQOPTs0Dj07NA49OzQOPTs0Dj07NA49OzQOPTs0Dj07NA49OzQOPTs0Di1Lan4tO1/9O5lP/Ps4z/28im/+LTtf/i0rWU49OzQOPTs0Dj07NA49OzQOPTs0Dj07NA49OzQOPTs0Dj07NA49OzQOPTs0DdzKpL0LOM/c+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjMrPtIxfz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjeHPs4xb////Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDe1bM249S1/dK4k//Ps4z/28el/+LTtfnc0bkW////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANGzi03Ps4zbz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+0jF////8Az7SLi8+zjP3Ps4z/z7OM/8+zjP/Ps4y+zbSMUtCzjNfPs4z/0LOMzNC0jUH///8A////AP///wD///8A////AP///wD///8A////ANXVqgbh07XP4tO03tC1junPs4z/17+bpOLTtPPi0rag////AP///wD///8A////AP///wD///8A////AP///wDNtI8pzrOMuM+zjP/Ps4zlz7SOX86yi73Ps4z/z7OM/8+zjP/Ps4z9z7SLi////wD///8A////AM+1ijDPs4t1z7OMkM+zjfzQtIt3////AMzMmQXPtItwz7OM78+zjP/Ps4y0zrCMKv///wD///8A////AP///wD///8A////AOHTtYri07X85NKzOc+zjeHPs4z/z7OMb+PStmzi07X/4tS0R////wD///8A////AP///wD///8Az6+PEM+yjY/Ps4z7z7OM+8+0jI7YsYkN////AM60jG3Ps4z+zrOLls+zi3XPtYow////AP///wD///8A////AP///wD///8A////ANCzjKfPs4zjv7+ABP///wD///8AzraSFc+yjJnPs4z9z7OM/M+yjJnKtYoY////AP///wD///8A49OzQOLTtf3h1LaB////ANCzjOfPs4z/z7OLdf///wHi07XE49O14OrVvwz///8A////AP+AgALQtIxmz7OM68+zjP/Ps4zFzbGLLv///wD///8A/4CAAs+zjNvQsou3////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANG1izfPs4z/0LONV////wD///8A////AP///wDPtYowz7KMwc+zjP/Ps4zzz7ONf9G5iwvfz68Q4tO14uLStcj/v78E////AM+zjO7Ps4z/z7KNe////wDf0rMo4tO1+OLUtJP///8AzrWMPs+zjM/Ps4z/z7OM69C0jWL///8B////AP///wD///8A0bOLTc+zjP/QtIxH////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQs4zH0LOMx////wD///8A////AP///wD///8A////ANCyi1bPs43hz7OM/8+zjObYwqDK4tO19OPVuCT///8A////AM+zjPTPs4z/0LOLgv///wD///8A4tO2euDPsP7RtpC9z7OM/8+zjP7Ps42a1biOEv///wD///8A////AP///wD///8A0LOMvM+zi9b///8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQs41Xz7OM/9G1izf///8A////AP///wD///8A////AP///wDGqo4JzrKMfs+zjPnPs4z/0LaP5c6yjEnMs4AKz7KNj8+zjP/Ps4z/z7OM9s+zi4DItpIO0LONgtC0jf3Ps4z/z7ON0M2yjTj///8A////AP///wD///8A////AP///wDRtIssz7OM/9CyjWf///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wC/v4AEz7OM486zjKj///8A////AP///wD///8A////AP///wD///8A3ta1H9/QsPLUvJfjz7OM/8+zjP/Ps4zrz7OM/8+zjP/Ps4z/z7OM/8+zjP/Qs43zz7OM/8+zjPTZxaL94tO1hv///wD///8A////AP///wD///8A////AP///wDPs4ybz7OM7cyzgAr///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0LSLd8+zjPvRtokc////AP///wD///8A////AP///wD//4AC4tS1wuLTtefm2bMU0LOMPM+zjNrPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7ONtcy4jxnj1LaI4tO1++TVtTD///8A////AP///wD///8A////AMyzjBTQs4z30LOMh////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A1biOEs+zjfXOso2I////AP///wD///8A////AP///wDh07V54tO1/uLUtEf///8A////AM+zjYDPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LOLYf///wDf378I4tS12ePStc7/v78E////AP///wD///8A////AM+0jHrPs4z70baJHP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANCzjJfPs4ztzLOACv///wD///8A////AOHStDPi07X64dO2kv///wD///8A////AM+yjI7Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LSMZv///wD///8A4tK1PuLTtf7h0rV4////AP///wD///8AzMyZBc+zjOXQs4yn////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM20jynPs4z+zrOMaP///wD///8A5syzCuHTtdji07XV39+/CP///wDStY4tz7SMqdCzjP3Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM8s60jX3YsYkN////AOLTtJbi07X35dG3J////wD///8Az7OLWs+zjP/Nso04////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOs4y40LOM19G5iwu/v4AE4tO0luLTtfnk1bUw0LGNMc+zja7Ps4z+z7OM/8+zjNDPs4y/z7OM/8+zjP/Ps4z/z7OM/8+zjP/PtIy70LOM58+zjP/Ps4zrz7KNdNzFohbi07Xi4tO0w////wHItpIOz7OMyc+zjMj///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOtI5Yz7OM/8+zjPTQtI3r2cOh/9vHpJXOs4yyz7OM/8+zjP7Ps4y2zrONQ////wD///8BzrSMbc+zjNDPs4zzz7OMys6yjF3///8Av7+ACM6zjGjPs4zez7OM/8+zjObVvZeW28ak/9G2kOXPtIz6z7OM/82yjHH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQs42Nz7OM/8+zjP/Ps4z/z7OM/8+zjP/Qs4z3z7OMm820jyn///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCqqqoDz7OMW86zjNLPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjbn///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANu2kgfPs4ztz7OM/8+zjP/Ps4z/z7OM/8+zjPjPs4y+zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjPs43rz7OM/8+zjP/Ps4z/z7OM/8+zjP7NsY4k////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP+AgALPs4zgz7OM/8+zjP/Ps4z/z7OM/86yi+zOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjOs4y4zrOMuM6zjLjPs4zez7OM/8+zjP/Ps4z/z7OM/8+zjPnTsZAX////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQs4x8z7OM/8+zjP/Ps4z/z7OM/860jU7///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDRsoshz7OM/c+zjP/Ps4z/z7OM/8+0jKb///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDGqo4JzrOMaM+zjNPPs4vAzrOMVP+AgAL///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrWMPs+zjKvPtIzfz7SMesmuhhP///8A////AP///wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA////////AAD///w///8AAP//+B///wAA///wD///AAD///AP//8AAP//8A///wAA///wD///AAD//+AH//8AAP//jBH//wAA//4+fH//AAD/+P5/H/8AAP/j/n/H/wAA58f+f+PnAACBH/4/+IEAAIB//B/+AQAAAP/4D/8AAAAAAAAAAAAAAAD/+A//AAAAgH/8H/4BAACBH/gP+IEAAPPH8m/hzwAA+eHyZ8efAAD9+OZzH78AAPz+Djg/PwAA/v+MEP9/AAD+f4AA/n8AAP9/MAT+fwAA/z9wDn7/AAD/PnAPfP8AAP+84Ac9/wAA/5mAAZn/AAD/wB48A/8AAP+Af/8B/wAA/4AAAAH/AAD/gAAAAf8AAP/D///B/wAA/+f//+f/AAD///////8AAP///////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANCyjUzQs4y8z7OL1tCzi2zMzJkF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDRtokcz7OM/M+zjP/Ps4z/z7OM/860i1j///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM60jX3Ps4z/z7OM/8+zjP/Ps4z/zrOMvf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrOLcs+zjP/Ps4z/z7OM/8+zjP/PtIyp////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A07CNHc+0jJ/Ps43hz7OM5s+zjP/Ps4z/z7ON9dCzjOLQs4yn0rSPIv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0rSHEc+0i4vQs4zn0LSLd8aqjgn/gIAC0LONlM+zjN3RuYsL1aqABs+zjG/Ps4zl0LKMks62khX///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7+ACM+zjHbQs4znz7SLi9K0hxH///8A////AP///wDOtIxt0LOMzP///wD///8A////AMi2kg7PtIuE0LOM5860jX3RuYsL////AP///wD///8A////AP///wD///8A////AP///wDMs4AKzrONXs6zjS////8AqqqqA9C0jWLPs4zgz7SMn9OwjR3///8A////AP///wD///8A////AM+yjXTOs4zS////AP///wD///8A////AP///wDMuI8Zz7KMmc+zjOPOs4xov7+ABP///wDOs40vzrONXsyzgAr///8Az7GKO8+zjO/Ps4z/z7OM/9C0jcTPs4zUz7OMs9G0iyz///8A////AP///wD///8A////AP///wDg07Qp2MKf3tK3kv3i0rWU////AP///wD///8A////AP///wD///8A0bGJJ86yjK3Ps4zYz7OMxs+zjP/Ps4z/z7OM78+xijvOsoytz7OM/8+zjP/Ps4z/z7OM/9W6l3Pfz7cg38+3IN/PtyDfz7cg38+3IN/PtyDfz7cg38+3IOLTtq/Yw6D/0reR/+LTtf/i07ZX38+3IN/PtyDfz7cg38+3IN/PtyDfz7cg38+3INS7lnDPs4z/z7OM/8+zjP/Ps4z/zrKMrc+zjODPs4z/z7OM/8+zjP/Ps4z/2caj5+HTtdjh07XY4dO12OHTtdjh07XY4dO12OHTtdjh07XY4tO19djCn//Rt5H/4tO1/+LUtuXh07XY4dO12OHTtdjh07XY4dO12OHTtdjh07XY2caj58+zjP/Ps4z/z7OM/8+zjP/Ps4zgz7ONf8+zjP/Ps4z/z7OM/9CzjP3Os4y4zrCMKv///wD///8A////AP///wD///8A////AP///wDi07aE2MKf/tG2kP/i07X538+vEP///wD///8A////AP///wD///8A////AM+vjyDPs4yv0LOM/c+zjP/Ps4z/z7OM/8+zjX/StIcRz7OMic+zjO3Ps4z5zrSLY8+yjXvPs4zxz7ONmsy4jxn///8A////AP///wD///8A4Na4GeLStuXSt5Gu0LON8+HTtZvj1LWH////AP///wD///8A////ANG5iwvOs4yDz7OM8dCzjIfNtY1gz7OM+s+zjO3Ps4yJ0rSHEf///wD///8A/4CAAtCzjIfPs4yJ////ANCzjhvPs4ykz7OM88+zi4DRuYsL////AP///wHi07W54tO2c8+zjZrPs4313cy7D+PTteDg0bMy////AP///wHPs4xbz7OM5M+zjL7NtI8p////ANC0jIHPso2P/4CAAv///wD///8A////AP///wD///8A0baJHM+zjOnMs4AK////AP///wDOs4s5z7KLy9CzjOfQtIxm39K1cePUtLz///8Bz7OLoc+zjPv///8A4tW0TuHTtc/Rto04z7OMxtCzjOfPs4xb////AP///wDbtpIHz7OM5tK0jyL///8A////AP///wD///8A////AP///wD///8A0LOMp86zjGj///8A////AP///wD///8B0LOLYc+zjOzRuJL71LmUX8i2kg7Qs43Nz7OM/8+1iUXMuI8Z1byY38+zjP3QsoySzLuID////wD///8A////AM+yjWDPs4yv////AP///wD///8A////AP///wD///8A////AP///wDRtYs3z7OL1v///wH///8A////AP///wDb27YH4dGz1NK6lLHPs4z5z7OM8c+zjP/Ps4z/z7OM/8+zjPjOs43N3syt6t/Ssyj///8A////AP///wD///8Az7OMz86yjj////8A////AP///wD///8A////AP///wD///8A////AP///wDQs4zH0LSMR////wD///8A////AOLStY7i1LWf////ANGzjk3Ps4z/z7OM/8+zjP/Ps4z/zrON4f///wHh1LZe4tO1xP//gAL///8A////AM6yjj/Ps4zP////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANCzjVfPs4y2////AP///wDh07VF4tO02+vYsQ3/gIACzrSMc8+zjP/Ps4z/z7OM/8+zjP/Ps4zo0baJHP///wDi07a449K2bP///wD///8Az7ONrs+0jF////8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7+ABM+zjeHRtIss286qFeLTteHhz7Y7zrONXs+zjNnPs4zpz7SM3M+zjP/Ps4z/z7OM/8+zjNDQs4z3z7KNj9vHozLi07Xn3ta1H9GxkCfPs4zl27aSB////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrOMqM+zjfXRtpD01b2Yxs+zjN3Ps4vWz7OMZNu2kge/v4AE0LONV8+zjJDRtIss////AM20jDPQs4usz7OM8tO6lcfSt5Hz0LOM+M6zjLj///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AL+/gATOs4znz7OM/8+zjP/Ps4z4z7SMn8yxi3nOs4x4zrOMeM6zjHjOs4x4zrOMeM6zjHjOs4x4zrOMeM6zjHjOs4uNz7OL7s+zjP/Ps4z/z7OM+NW4jhL///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////Ac+zjNzPs4z/z7OM/8+zjN7Ps4uAz7OLgM+zi4DPs4uAz7OLgM+zi4DPs4uAz7OLgM+zi4DPs4uAz7OLgM+zi4DPs4zOz7OM/8+zjP/Ps4zx0bmLC////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrKMSc+zjNHPtI3E0LOONv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+ziiXQsou3z7OM286zjV7///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////n////w////8H////B////AP///Oc///n3n//n9+f4H+P4EH/D/gAAAAAIP8P8GM/B8R5jpeZ/eaae/35mPv++AH3/vMG9/93Bu//bAFv/wPcD/8H/g//AAAP/5//n//////////////////////KAAAAEAAAACAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7WKMM+zjKTPs4zZz7OM78+zjL7Qs41yv7+ABP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7OMds+zjPvPs4z/z7OM/8+zjP/Ps4z/z7OM/86zjc3Rtokc////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7GMRc+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OMvv///wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+zjNTPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/QtIxS////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AMy7iA/Ps4z+z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OMiv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDRs4oyz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjKv///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0bmLC8+zjPzPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Os4yD////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANC0jUHPsozzz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/zrOMfP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzbSMM8+zjbnPs4z/z7OM/8+0jf7Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4zDz7GKO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQtY0mz7OMqs+zjP7Ps4z/z7SM386zjV7QtI1cz7OM8c+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7SMtM+1jWDPs4zZz7OM/8+zjP/Ps4y0zbGLLv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANCzjhvPs42az7OM+8+zjP/Ps4zpzrOLbtWqgAb///8A////AMyziB7Qs4yMz7OM9M+zjP/Os4zXzrKMWf///wD///8AqqqqA8+zjGTPs4zjz7OM/8+zjP7Ps42l0rSPIv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A1biOEs+0i4vQs4z3z7OM/8+zjPHOtI191aqVDP///wD///8A////AP///wD///8A////AM+zjNrPs4z/0LKMkv///wD///8A////AP///wD///8Av7+ACM60jHPPs4zsz7OM/8+zjPrPtIyVyrWKGP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDRuYsL0LOMfM+zjPDPs4z/0LOM99CzjIzJroYT////AP///wD///8A////AP///wD///8A////AP///wDPs4zgz7OM/860i5j///8A////AP///wD///8A////AP///wD///8AyLaSDtCzi4LPs4zzz7OM/8+zjfXPs42Gz6+PEP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANWqgAbQs4tsz7OM6M+zjP/Ps4z8z7OMm9G2iRz///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7OM5s+zjP/Ps4ye////AP///wD///8A////AP///wD///8A////AP///wD///8A0a6LFtCzjZHPs4z5z7OM/8+zjO7QtIt3xqqOCf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM61jB/Os41DzrWMH////wD///8A////AP///wD///8A/4CAAs6yjF3Ps4zez7OM/8+zjP7Ps4yr0bGJJ////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM+zjO3Ps4z/z7ONpf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrWMH8+zjKDPs4z9z7OM/8+zjObOs4xozMyZBf///wD///8A////AP///wD///8AzrWMH86zjUPOtYwf////AP///wD///8A////AP///wD///8Az7SNVdCzjOfPs4z/z7OM/8+zjP/Ps4zkz7OMUP///wD///8A0bOLTc+zjNPPs4z/z7OM/8+0jLrNtIwz////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AOvYsQ3QtIz3z7OM/9K4k8jf1bUY////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrCMKs+zjK/Ps4z/z7OM/8+zjNzOsoxZ////Af///wDPs4xQz7OM5M+zjP/Ps4z/z7OM/9CzjOfPtI1V////AP///wD///8Az7KMhc+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/QtIydz7OMxc+zjP/Ps4z/z7OMydGyi0L///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AOPStj/i07Xhz7SN/8+zjP/VvZn/4tO18eHUtF////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0bWLN8+zjL7Ps4z/z7OM/8+zjNDQtI2jz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+yjIX///8A0LOMPM+zjP3Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4vW0LOLUf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AODWuBni07Xu4tO1/8+zjP/Ps4z/1LyY/+LTtf/i07X94NO0Ov///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7GMRdCzjMzPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z90LOMPM+zjKrPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4zKqqqqA////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDj1LaI4tO1/+LStP/Ps4z/z7OM/9S7lv/i07X/4tO1/+LTtrj///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Bz7OMxs+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjKrPtIzfz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0baP9OLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO16OLTtf/h0bP/z7OM/8+zjP/TupX/4tO1/+LTtf/h07T54tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qOLTtaji07Wo4tO1qNG2j/TPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/PtIzfz7OM68+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/9C2j//i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4dGy/8+zjP/Ps4z/07qV/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/i07X/4tO1/+LTtf/Qto//z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM69Cyi7fPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/QtI7Q5NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjji0rSP4tO1/+DQsf/Ps4z/z7OM/9O5lP/i07X/4tO1/+PTtbTk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY45NG2OOTRtjjk0bY40LSPz8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/9Cyi7fQs4thz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/c+zjZrOsYka////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzMzMBeLTtOzgz7D/z7OM/8+zjP/SuJL/4tO1/+LTtvrg1rgZ////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPr48QzrOLjc+zjPvPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Qs4th////ANCyi7fPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7KM38+zjPvPs4z/z7ON9c+zjYbMu4gP////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AOPTtFLi07X/3s6u+s+zjP/Ps4z/0beR/+LTtfTi07X/4dO1bv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKqqqgPPsoxqz7OM68+zjP/Ps4z+0LOM4s+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Qsou3////AP///wDRuYsLz7SMlc+zjP7Ps4z/z7OM/8+zjP/Ps4z/zrOMp8aqjgnNtI8p0LKLt8+zjP/Ps4z/z7OM7NCzjXLbtpIH////AP///wD///8A////AP///wD///8A////AP///wD///8A////AOTRthzi07Xu4tO1/9vIpnDPs4z/z7OM/8+zjeTj1bM24tO1/OLTtfXi07Yj////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM21jkjPtIzVz7OM/8+zjP/Ps4zPz7SNOsaqjgnPs4yez7OM/8+zjP/Ps4z/z7OM/8+zjP7PtIyV0bmLC////wD///8A////AP///wDNtI8pz7SMX86zjIPPs4ulz7OM/8+0jKb///8A////AP///wDNtY5Iz7SM1c+zjP/Ps4z/z7OM4M6zjV7/gIAC////AP///wD///8A////AP///wD///8A////AP//gALi07XA4tO1/+HStJrQsY0xz7OM/8+zjP/Ps4zp////AOHStYni07X/4tO1wP///wH///8A////AP///wD///8A////AP///wD///8AzbSPKdCyi7fPs4z/z7OM/8+zjO7PsoxqqqqqA////wD///8Az7KNj8+zjP/Ps4y0zrOMg8+0jF/NtI8p////AP///wD///8A////AP///wD///8A////AP///wD///8A1aqVDM+zjPDPs4z70baJHP///wD///8A////AKqqqgPPsoxqz7OM68+zjP/Ps4z/z7OM0c6yjEn///8A////AP///wD///8A////AP///wDi07R74tO1/+PTtdfmzLMKzbKNOM+zjP/Ps4z/z7OM8P///wDf378I49O11+LTtf/i1LVr////AP///wD///8A////AP///wDJroYTz7OMlM+zjPvPs4z/z7OM/tC0jZzMs4wU////AP///wD///8AzLuID8+zjPLPs4z6zrGJGv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPs4yKz7OM/8+zjIr///8A////AP///wD///8A////AM+vjxDOs4uNz7OM+c+zjP/Ps4z/z7OMv9Czjjb///8A////AP///wDk0bY44tO1++LTtfnk1bUw////AM61jD7Ps4z/z7OM/8+zjPb///8A////AOTRtjji07X94tO19ODRsiH///8A////AMzMmQXQs41yz7OM78+zjP/Ps4z/z7KLy9Czjjb///8A////AP///wD///8A////AM+yjXTPs4z/0LOMov///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0baJHM+zjPvPs4zv1aqVDP///wD///8A////AP///wD///8A////AM2xjiTPs4yvz7OM/8+zjP/Ps4z/z7OMq9C1jSbb27YO4tO13uLTtf/i1LVx////AP///wDOtItEz7OM/8+zjP/Ps4z8////AP///wD///8A4tO2i+LTtf/j0rW9////Ac+yi0/Ps4zbz7OM/8+zjP/Ps4zs0LSMZv+AgAL///8A////AP///wD///8A////AL+/gATOs4ziz7OM/8+1ijD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPs42lz7OM/860jG3///8A////AP///wD///8A////AP///wD///8A////AM+zi0DPs4zPz7OM/8+zjP/Ps4z71byY2uDQsf/i0rW3////Af///wD///8Az7SLS8+zjP/Ps4z/z7OM/6qqqgP///8A////AN/fvwjh07XY3s2t/9G2kdnPs4z/z7OM/8+zjP3OtIuY1biOEv///wD///8A////AP///wD///8A////AP///wDOsoxZz7OM/86zjL3///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzbSMM8+zjP/Ps4zd/4CAAv///wD///8A////AP///wD///8A////AP///wD///8A/4CAAs6yi2PQs4znz7OM/8+zjP/QtY7+0LWQjsi2kg7///8AzrWMH8+zjcnPs4z/z7OM/8+zjP/Ps4zF0bWLN////wDKtYoY0LaRs8+zjP/Ps4z/z7OM/8+zjMjNtIwz////AP///wD///8A////AP///wD///8A////AP///wD///8Az7KLy8+zjP/PtItL////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPsozBz7OM/9Czi1H///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A3MuoLNfCn/nQtI3/z7OM/8+zjP/Ps4zrzrSMks+zjPHPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjPzOs4yxz7OM8s+zjP/Ps4z/0baP/9rEo9X///8B////AP///wD///8A////AP///wD///8A////AP///wD///8A0bSKPc+zjP/Qs4zX////Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzrSNTs+zjP/Ps4zD////AP///wD///8A////AP///wD///8A////AP///wD///8A/7+/BOLStcji07X/3sytnNCzjKfPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z80LOMl+HRs9zi07X/49S2Zf///wD///8A////AP///wD///8A////AP///wD///8A////AM+0jbDPs4z/0LSMZv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP+AgALPs4zaz7OM/8+yjDX///8A////AP///wD///8A////AP///wD///8A////AOLTtIXi07X/4dO1z9XVqgb///8AzrOLOc+zjODPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4vqzrONL////wDh1bc84tO1/eLTtfHl07kd////AP///wD///8A////AP///wD///8A////AM2xjiTPs4z9z7OM68aqjgn///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7KMas+zjP/Qs4yn////AP///wD///8A////AP///wD///8A////AOPTs0Di07X94tO19d/Ssyj///8A////AP///wDPs4ykz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OL1v///wD///8A////AOPTtZDi07X/4tO2uP///wD///8A////AP///wD///8A////AP///wDPs4yUz7OM/9Czi4L///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AMyzgArPs4ztz7OM+9OwjR3///8A////AP///wD///8A////AOPVuBLi07Xk4tO1/+PUtmX///8A////AP///wD///8Az7OMtM+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjOX///8A////AP///wDmzLMK4tO13OLTtf/i07Zi////AP///wD///8A////AP///wDVuI4Sz7ON9c+zjPjTsZAX////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7KMhc+zjP/PtIuL////AP///wD///8A////AP///wDi07au4tO1/+HTtaz///8A////AP///wD///8A0bSKPc+0jNzPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/PtI3q0LOONv///wD///8A////AOLStT7i07X+4tO18OPQsxv///8A////AP///wD///8Az7OLec+zjP/Os4yd////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AMy4jxnPs4z5z7OM8NWqlQz///8A////AP///wDh0rVn4tO1/+LTteLh0rQR////AP///wDQtIxHzrONws+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4y0zbSMM////wD///8A4dO2kuLTtf/h07S1////AP///wD///8AzMyZBc+zjOXPs4z/0LKOK////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7OMoM+zjP/Ps4xvv7+ABP///wDg07Qp4tO19uLTtf3i0rU+////AdCzi1HQs4zMz7OM/8+zjP/Ps4z/z7OM2NCzjLLPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM08+zjN7Ps4z/z7OM/8+zjP/Ps4yxz7WKMOjRuQvi07Xd4tO1/+HUtF////8AzMyZBc6zjV7Ps4z/zrOMuP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM2xiy7Ps4z/0LSM99CzjPfPs43v1byY8ODPsP/i1LWDz7OMW8+zjNTPs4z/z7OM/8+zjP/Ps4y7z7ONSv///wH///8Az7OLZc+zjPHPs4z/z7OM/8+zjP/Ps4z80LOMh7+/gAiqqqoDzrSLWM+zjM/Ps4z/z7OM/8+zjP7Osoyt2MOhYuHStP7XwZ74zrOM58+zjP3Qs4z3z7OM/9Czikb///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDRsotC0LSN/s+zjP/Ps4z/z7OM/8+zjP/PtI78z7SN38+zjP/Ps4z/0LOM99C0jZzRtIss////AP///wD///8A////AP///wDMu4gPz7GMRdCzjXLPsotPyrWKGP///wD///8A////AP///wD///8A0LOKRtCzjLzPs4z/z7OM/8+zjP7QtI72z7OM/8+zjP/Ps4z/z7OM/8+zjP/QtIt3////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7OM2s+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Qs4znzrSNfc62khX///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzbSMM8+0jKnPs4z9z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM+cq1ihj///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A07GQF8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z7z7SM2c+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7OM2M+zjNjPs4zYz7SM8c+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/PsotP////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM62khXPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/0LKNTP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az7OM1M+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM/9CzjJe/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAi/v4AIv7+ACL+/gAjOtYxZz7OM/8+zjP/Ps4z/z7OM/8+zjP/Ps4z/z7OM9s62khX///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM6ziznPs4z5z7OM/8+zjP/Ps4z/z7OM/8+zjeHStIcR////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM6zjLLPs4z/z7OM/8+zjP/Ps4z/z7OM/8+zjWv///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0bSLLM+zjLPPs4zsz7OM4NC0jZzJroYT////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wC/v4AEz7KNe8+zjNTPs4zyz7OMxdGzi03///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////////////////////////////////////////////////////////////////////////////////////////////8P/////////gP////////8Af////////gB////////+AD////////4AP////////gA////////+AH////////gAH///////4YCH//////+Hw+H//////h/j+H/////8P+P8H/////D/4/8P////w//j/8P//g8P/+P/8PB4AD//wf/8ABgA//+A//8AEAH//wB//4AAAAAAAAAAAAAAAAAAAAAAAAH//wB//4AIAP//gP//ABgAP/+A//4AHAYf/yJ/+GA/h4f+Ij/h4f/n4f5jP4Pn/+PwfOOfD+f/8/wZ44w/z//z/wHjwH/P//n/wcHB/5//+f/gAAP/n//8/8AAA/8///z/jgA5/z///n+eADj+P//+fz4APP5///4+PgA+fn///z54AA48////POAAAzz///+Ag8Dggf///4AP//gB////AH///gD///8AAAAAAP///wAAAAAA////AP///4D///+B////gf///8P////j///////////////////////////////////////////////////////////////////////////////////////w=='> + <script> + //these are placed here because the compression compiler does not deal with the eval function properly. + //enter the values of the settings object into their input fields + function enterSettings(){ + $('.isSetting').each(function(){ + var objpath = findObjPath($(this)); + var val = ''; + try { + eval('val = '+objpath+';'); } + catch(e) { } + if ($(this).is('input,select')) { + $(this).val(val); + } + else { + $(this).text(val); + } + }); + } + //this function is for moving limits (LTS only) + function moveLimit(destination,streamName,objpath) { + if ((streamName == '_new_') || (destination != streamName[0])) { + var target; + if (destination == 'server') { + if (!settings.settings.config.limits) { + settings.settings.config.limits = []; + } + target = settings.settings.config.limits; + } + else { + destination = destination.replace('stream-',''); + if (!settings.settings.streams[destination].limits) { + settings.settings.streams[destination].limits = []; + } + target = settings.settings.streams[destination].limits; + } + + eval('target.push(settings.'+objpath+');'); + eval('delete settings.'+objpath+';'); } - </script> + } + </script> </head> <body> <div id='header'> <div id='logo'> - <a href='http://mistserver.org' target='_blank'> - <span>Mist/</span> Server + <a> + <span>Mist</span>Server Management Interface </a> </div> <div id='status'> @@ -68,20 +72,33 @@ </div> </div> - <div id='menu'> + <div id='menu' class='menu'> <div class='button'>Overview</div> <div class='button'>Protocols</div> <div class='button'>Streams</div> + <div class='button'>Preview</div> <div class='button LTS-only'>Limits</div> <div class='button'>Conversion</div> <div class='button'>Logs</div> + <div class='button'>Statistics</div> <div class='button'>Server Stats</div> <br> <div class='button red'>Disconnect</div> - <a class='button' href='http://shop.mistserver.org' target='_blank'>Mist Shop</a> + <br> + <div class='expandbutton'> + Tools <span class=arrowdown></span> + <div class='expandcontainer'> + <a class='button linktoReleaseNotes notedited' href='http://mistserver.org/wiki/Release_notes/MistServer' target='_blank'>Release notes</a> + <a class='button' href='http://mistserver.org/products' target='_blank'>Mist Shop</a> + <a class='button' href='http://mistserver.org/streamtester' target='_blank'>Stream Tester</a> + <a class='button' href='http://mistserver.org/wiki/Category:Guides' target='_blank'>Guides</a> + <div class='button'>Email for Help</div> + <a class='button linktoTnC notLTSlink' href='http://mistserver.org/wiki/Mistserver_license' target='_blank'>Terms & Conditions</a> + </div> + </div> </div> <div id='page'></div> - + <div id='ih-button' title='Activate integrated help'>?</div> </body> </html> \ No newline at end of file diff --git a/src/analysers/dtsc_analyser.cpp b/src/analysers/dtsc_analyser.cpp index 9e6e70cd..c3c46667 100644 --- a/src/analysers/dtsc_analyser.cpp +++ b/src/analysers/dtsc_analyser.cpp @@ -3,10 +3,12 @@ #include <string> #include <iostream> +#include <sstream> #include <mist/dtsc.h> #include <mist/json.h> #include <mist/config.h> +#include <mist/defines.h> ///\brief Holds everything unique to the analysers. namespace Analysers { @@ -17,11 +19,34 @@ namespace Analysers { ///\return The return code of the analyser. int analyseDTSC(Util::Config conf){ DTSC::File F(conf.getString("filename")); - std::cout << F.getMeta().toJSON().toPrettyString() << std::endl; + std::stringstream meta; + F.getMeta().toPrettyString(meta,0, 0x03); + std::cout << meta.str() << std::endl; + int bPos = 0; + F.seek_bpos(0); F.parseNext(); - while (F.getJSON()){ - std::cout << F.getJSON().toPrettyString() << std::endl; + JSON::Value tmp; + std::string tmpStr; + while (F.getPacket()){ + tmpStr = std::string(F.getPacket().getData(), F.getPacket().getDataLen()); + switch (F.getPacket().getVersion()){ + case DTSC::DTSC_V1: { + unsigned int i = 8; + JSON::fromDTMI((const unsigned char*)tmpStr.data(), tmpStr.size(), i, tmp); + break; + } + case DTSC::DTSC_V2: { + unsigned int i = 8; + JSON::fromDTMI2((const unsigned char*)tmpStr.data(), tmpStr.size(), i, tmp); + break; + } + default: + DEBUG_MSG(DLVL_WARN,"Invalid dtsc packet @ bpos %d", bPos); + break; + } + std::cout << tmp.toPrettyString() << std::endl; + bPos = F.getBytePos(); F.parseNext(); } return 0; diff --git a/src/analysers/ogg_analyser.cpp b/src/analysers/ogg_analyser.cpp index ac519232..8fd881a2 100644 --- a/src/analysers/ogg_analyser.cpp +++ b/src/analysers/ogg_analyser.cpp @@ -10,7 +10,73 @@ #include <mist/theora.h> namespace Analysers{ - int analyseOGG(){ + std::string Opus_prettyPacket(char * part,int len){ + if (len < 1){ + return "Invalid packet (0 byte length)"; + } + std::stringstream r; + char config = part[0] >> 3; + char code = part[0] & 3; + if ((part[0] & 4) == 4){r << "Stereo, ";}else{r << "Mono, ";} + if (config < 14){ + r << "SILK, "; + if (config < 4){r << "NB, ";} + if (config < 8 && config > 3){r << "MB, ";} + if (config < 14 && config > 7){r << "WB, ";} + if (config % 4 == 0){r << "10ms";} + if (config % 4 == 1){r << "20ms";} + if (config % 4 == 2){r << "40ms";} + if (config % 4 == 3){r << "60ms";} + } + if (config < 16 && config > 13){ + r << "Hybrid, "; + if (config < 14){r << "SWB, ";}else{r << "FB, ";} + if (config % 2 == 0){r << "10ms";}else{r << "20ms";} + } + if (config > 15){ + r << "CELT, "; + if (config < 20){r << "NB, ";} + if (config < 24 && config > 19){r << "WB, ";} + if (config < 28 && config > 23){r << "SWB, ";} + if (config > 27){r << "FB, ";} + if (config % 4 == 0){r << "2.5ms";} + if (config % 4 == 1){r << "5ms";} + if (config % 4 == 2){r << "10ms";} + if (config % 4 == 3){r << "20ms";} + } + if (code == 0){ + r << ": 1 packet (" << (len-1) << "b)"; + return r.str(); + } + if (code == 1){ + r << ": 2 packets (" << ((len-1)/2) << "b / " << ((len-1)/2) << "b)"; + return r.str(); + } + if (code == 2){ + if (len < 2){ + return "Invalid packet (code 2 must be > 1 byte long)"; + } + if (part[1] < 252){ + r << ": 2 packets (" << (int)part[1] << "b / " << (int)(len-2-part[1]) << "b)"; + }else{ + int ilen = part[1] + part[2]*4; + r << ": 2 packets (" << ilen << "b / " << (int)(len-3-ilen) << "b)"; + } + return r.str(); + } + //code 3 + bool VBR = (part[1] & 128) == 128; + bool pad = (part[1] & 64) == 64; + bool packets = (part[1] & 63); + r << ": " << packets << " packets (VBR = " << VBR << ", padding = " << pad << ")"; + return r.str(); + } + + int analyseOGG(int argc, char ** argv){ + Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); + conf.addOption("pages", JSON::fromString("{\"long\":\"pages\", \"short\":\"p\", \"long_off\":\"nopages\", \"short_off\":\"P\", \"default\":0, \"help\":\"Enable/disable printing of Ogg pages\"}")); + conf.parseArgs(argc, argv); + std::map<int,std::string> sn2Codec; std::string oggBuffer; OGG::Page oggPage; @@ -22,17 +88,94 @@ namespace Analysers{ } //while OGG::page check function read while (oggPage.read(oggBuffer)){//reading ogg to string - oggPage.setInternalCodec(""); - if (oggPage.getHeaderType() & 0x02){ + //print the Ogg page details, if requested + if (conf.getBool("pages")){ + std::cout << oggPage.toPrettyString() << std::endl; + } + + //attempt to detect codec if this is the first page of a stream + if (oggPage.getHeaderType() & OGG::BeginOfStream){ if (memcmp("theora",oggPage.getFullPayload() + 1,6) == 0){ - sn2Codec[oggPage.getBitstreamSerialNumber()] = "theora"; + sn2Codec[oggPage.getBitstreamSerialNumber()] = "Theora"; } if (memcmp("vorbis",oggPage.getFullPayload() + 1,6) == 0){ - sn2Codec[oggPage.getBitstreamSerialNumber()] = "vorbis"; + sn2Codec[oggPage.getBitstreamSerialNumber()] = "Vorbis"; + } + if (memcmp("OpusHead",oggPage.getFullPayload(),8) == 0){ + sn2Codec[oggPage.getBitstreamSerialNumber()] = "Opus"; + } + if (sn2Codec[oggPage.getBitstreamSerialNumber()] != ""){ + std::cout << "Bitstream " << oggPage.getBitstreamSerialNumber() << " recognized as " << sn2Codec[oggPage.getBitstreamSerialNumber()] << std::endl; + }else{ + std::cout << "Bitstream " << oggPage.getBitstreamSerialNumber() << " could not be recognized as any known codec" << std::endl; + } + + } + + if (sn2Codec[oggPage.getBitstreamSerialNumber()] == "Theora"){ + std::cout << "Theora data" << std::endl; + int offset = 0; + for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){ + theora::header tmpHeader; + int len = oggPage.getSegmentTableDeque()[i]; + if (tmpHeader.read(oggPage.getFullPayload()+offset,len)){ + std::cout << tmpHeader.toPrettyString(2); + } + theora::frame tmpFrame; + if (tmpFrame.read(oggPage.getFullPayload()+offset,len)){ + std::cout << tmpFrame.toPrettyString(2); + } + offset += len; + } + }else if(sn2Codec[oggPage.getBitstreamSerialNumber()] == "Vorbis"){ + std::cout << "Vorbis data" << std::endl; + int offset = 0; + for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){ + vorbis::header tmpHeader; + int len = oggPage.getSegmentTableDeque()[i]; + if (tmpHeader.read(oggPage.getFullPayload()+offset,len)){ + std::cout << tmpHeader.toPrettyString(2); + } + offset += len; + } + }else if(sn2Codec[oggPage.getBitstreamSerialNumber()] == "Opus"){ + std::cout << "Opus data" << std::endl; + int offset = 0; + for (unsigned int i = 0; i < oggPage.getSegmentTableDeque().size(); i++){ + int len = oggPage.getSegmentTableDeque()[i]; + char * part = oggPage.getFullPayload() + offset; + if (len >= 8 && memcmp(part, "Opus", 4) == 0){ + if (memcmp(part, "OpusHead", 8) == 0){ + std::cout << " Version: " << (int)(part[8]) << std::endl; + std::cout << " Channels: " << (int)(part[9]) << std::endl; + std::cout << " Pre-skip: " << (int)(part[10] + (part[11] << 8)) << std::endl; + std::cout << " Orig. sample rate: " << (int)(part[12] + (part[13] << 8) + (part[14] << 16) + (part[15] << 24)) << std::endl; + std::cout << " Gain: " << (int)(part[16] + (part[17] << 8)) << std::endl; + std::cout << " Channel map: " << (int)(part[18]) << std::endl; + if (part[18] > 0){ + std::cout << " Channel map family " << (int)(part[18]) << " not implemented - output incomplete" << std::endl; + } + } + if (memcmp(part, "OpusTags", 8) == 0){ + unsigned int vendor_len = part[8] + (part[9]<<8) + (part[10]<<16) + (part[11]<<24); + std::cout << " Vendor: " << std::string(part+12, vendor_len) << std::endl; + char * str_data = part+12+vendor_len; + unsigned int strings = str_data[0] + (str_data[1]<<8) + (str_data[2]<<16) + (str_data[3]<<24); + std::cout << " Tags: (" << strings << ")" << std::endl; + str_data += 4; + for (unsigned int j = 0; j < strings; j++){ + unsigned int strlen = str_data[0] + (str_data[1]<<8) + (str_data[2]<<16) + (str_data[3]<<24); + str_data += 4; + std::cout << " [" << j << "] " << std::string(str_data, strlen) << std::endl; + str_data += strlen; + } + } + }else{ + std::cout << " " << Opus_prettyPacket(part,len) << std::endl; + } + offset += len; } } - oggPage.setInternalCodec(sn2Codec[oggPage.getBitstreamSerialNumber()]); - std::cout << oggPage.toPrettyString() << std::endl; } } return 0; @@ -40,8 +183,6 @@ namespace Analysers{ } int main(int argc, char ** argv){ - Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); - conf.parseArgs(argc, argv); - return Analysers::analyseOGG(); + return Analysers::analyseOGG(argc, argv); } diff --git a/src/buffer/buffer.cpp b/src/buffer/buffer.cpp deleted file mode 100644 index 413f6fdc..00000000 --- a/src/buffer/buffer.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/// \file buffer.cpp -/// Contains the main code for the Buffer. - -#include <fcntl.h> -#include <iostream> -#include <string> -#include <vector> -#include <cstdlib> -#include <cstdio> -#include <string.h> -#include <unistd.h> -#include <signal.h> -#include <sstream> -#include <sys/time.h> -#include <mist/config.h> -#include <mist/timing.h> -#include "buffer_stream.h" -#include <mist/stream.h> -#include <mist/defines.h> - -/// Holds all code unique to the Buffer. -namespace Buffer { - - volatile bool buffer_running = true; ///< Set to false when shutting down. - Stream * thisStream = 0; - Socket::Server SS; ///< The server socket. - - ///\brief A function running in a thread to send all statistics. - ///\param empty A null pointer. - void handleStats(void * empty){ -#if defined(_TTHREAD_POSIX_) && defined(WITH_THREADNAMES) && !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) || defined(_WIN32) || defined(__CYGWIN__)) - pthread_setname_np(pthread_self(), "StatsHandler"); -#endif - if (empty != 0){ - return; - } - std::string double_newline = "\n\n"; - Socket::Connection StatsSocket = Socket::Connection(Util::getTmpFolder() + "statistics", true); - while (buffer_running){ - Util::sleep(1000); //sleep one second - if ( !StatsSocket.connected()){ - StatsSocket = Socket::Connection(Util::getTmpFolder() + "statistics", true); - } - if (StatsSocket.connected()){ - StatsSocket.SendNow(Stream::get()->getStats()); - StatsSocket.SendNow(double_newline); - if (StatsSocket.spool()){ - //Got a response. - buffer_running = false; - } - } - } - StatsSocket.close(); - } - - ///\brief A function to handle input data. - ///\param conn A socket reference. - void handlePushIn(Socket::Connection & conn){ - #if defined(_TTHREAD_POSIX_) && defined(WITH_THREADNAMES) && !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) || defined(_WIN32) || defined(__CYGWIN__)) - pthread_setname_np(pthread_self(), "Push Input"); - #endif - conn.setBlocking(true); - int sockNo = 0; - while (buffer_running && conn.connected()){ - while (thisStream->parsePacket(conn)){ - //do nothing while parsing - } - Util::sleep(10);//sleep to prevent high CPU usage - } - conn.close(); - if (buffer_running){ - thisStream->endStream(); - } - long long int wait_time = Util::getMS(); - while (Util::getMS() - wait_time < thisStream->metadata.bufferWindow){ - Util::sleep(thisStream->metadata.bufferWindow - (Util::getMS() - wait_time)); - } - thisStream->removeSocket(sockNo); - } - - ///\brief A function running a thread to handle input data through stdin. - ///Automatically slows down to realtime playback. - ///\param empty A null pointer. - void handleStdin(void * empty){ - if (empty != 0){ - return; - } - #if defined(_TTHREAD_POSIX_) && defined(WITH_THREADNAMES) && !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) || defined(_WIN32) || defined(__CYGWIN__)) - pthread_setname_np(pthread_self(), "Standard Input"); - #endif - long long int timeDiff = 0; //difference between local time and stream time - unsigned int lastPacket = 0; //last parsed packet timestamp - std::string inBuffer; - char charBuffer[1024 * 10]; - unsigned int charCount; - long long int now; - - while (std::cin.good() && buffer_running){ - //slow down packet receiving to real-time - now = Util::getMS(); - if (((now - timeDiff) >= lastPacket) || (lastPacket - (now - timeDiff) > 15000)){ - if (thisStream->parsePacket(inBuffer)){ - lastPacket = thisStream->getTime(); - if ((now - timeDiff - lastPacket) > 15000 || (now - timeDiff - lastPacket < -15000)){ - timeDiff = now - lastPacket; - } - }else{ - std::cin.read(charBuffer, 1024 * 10); - charCount = std::cin.gcount(); - inBuffer.append(charBuffer, charCount); - } - }else{ - Util::sleep(std::min(15LL, lastPacket - (now - timeDiff))); - } - } - buffer_running = false; - } - - ///\brief A function running in a thread to handle a new user connection. - ///\param v_usr The user that is connected. - void handleUser(void * v_usr){ - std::set<int> allowedTracks; - user * usr = (user*)v_usr; - thisStream->addUser(usr); -#if DEBUG >= 5 - std::cerr << "Thread launched for user " << usr->sID << ", socket number " << usr->S.getSocket() << std::endl; -#endif -#if defined(_TTHREAD_POSIX_) && defined(WITH_THREADNAMES) && !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) || defined(_WIN32) || defined(__CYGWIN__)) - pthread_setname_np(pthread_self(), usr->sID.c_str()); -#endif - usr->myRing = thisStream->getRing(); - thisStream->sendMeta(usr->S); - - while (usr->S.connected()){ - if (usr->myRing->playCount){ - if (usr->myRing->waiting){ - Stream::get()->waitForData(); - if ( !Stream::get()->isNewest(usr->myRing->b, allowedTracks)){ - usr->myRing->waiting = false; - usr->myRing->b = Stream::get()->getNext(usr->myRing->b, allowedTracks); - if ((Stream::get()->getPacket(usr->myRing->b).isMember("keyframe") && (usr->myRing->playCount > 0)) || (usr->playUntil && usr->playUntil <= Stream::get()->getPacket(usr->myRing->b)["time"].asInt())){ - usr->myRing->playCount--; - if (usr->myRing->playCount < 1 || usr->playUntil <= Stream::get()->getPacket(usr->myRing->b)["time"].asInt()){ - usr->myRing->playCount = 0; - JSON::Value pausemark; - pausemark["trackid"] = 0ll; - pausemark["mark"] = "pause"; - pausemark["time"] = Stream::get()->getPacket(usr->myRing->b)["time"].asInt(); - pausemark.sendTo(usr->S); - } - } - } - }else{ - //complete a send - Stream::get()->sendPacket(usr->myRing->b, usr->S); - if ( !usr->S.connected()){break;} - //switch to next buffer - if (Stream::get()->isNewest(usr->myRing->b, allowedTracks)){ - //no next buffer? go in waiting mode. - usr->myRing->waiting = true; - }else{ - usr->myRing->b = Stream::get()->getNext(usr->myRing->b, allowedTracks); - if ((Stream::get()->getPacket(usr->myRing->b).isMember("keyframe") && (usr->myRing->playCount > 0)) || (usr->playUntil && usr->playUntil <= Stream::get()->getPacket(usr->myRing->b)["time"].asInt())){ - usr->myRing->playCount--; - if (usr->myRing->playCount < 1 || usr->playUntil <= Stream::get()->getPacket(usr->myRing->b)["time"].asInt()){ - usr->myRing->playCount = 0; - JSON::Value pausemark; - pausemark["trackid"] = 0ll; - pausemark["mark"] = "pause"; - pausemark["time"] = Stream::get()->getPacket(usr->myRing->b)["time"].asInt(); - pausemark.sendTo(usr->S); - } - } - } - } - } - if (usr->S.spool()){ - while (usr->S.Received().size()){ - //delete anything that doesn't end with a newline - if ( !usr->S.Received().get().empty() && *(usr->S.Received().get().rbegin()) != '\n'){ - usr->S.Received().get().clear(); - continue; - } - usr->S.Received().get().resize(usr->S.Received().get().size() - 1); - if ( !usr->S.Received().get().empty()){ - switch (usr->S.Received().get()[0]){ - case 'P': { //Push - if (thisStream->checkWaitingIP(usr->S.Received().get().substr(2))){ - usr->S.Received().get().clear(); - Socket::Connection tmp = usr->S; - usr->S = Socket::Connection( -1); - thisStream->removeUser(usr); - thisStream->dropRing(usr->myRing); - delete usr; - return handlePushIn(tmp); - }else{ - usr->Disconnect("Push denied - invalid IP address!"); - } - break; - } - case 'S': { //Stats - usr->tmpStats = Stats(usr->S.Received().get().substr(2)); - unsigned int secs = usr->tmpStats.conntime - usr->lastStats.conntime; - if (secs < 1){ - secs = 1; - } - usr->curr_up = (usr->tmpStats.up - usr->lastStats.up) / secs; - usr->curr_down = (usr->tmpStats.down - usr->lastStats.down) / secs; - usr->lastStats = usr->tmpStats; - thisStream->saveStats(usr->sID, usr->tmpStats); - thisStream->sendMeta(usr->S); - break; - } - case 't': { - if (usr->S.Received().get().size() >= 3){ - allowedTracks.clear(); - std::string tmp = usr->S.Received().get().substr(2); - while (tmp != ""){ - allowedTracks.insert(atoi(tmp.substr(0,tmp.find(' ')).c_str())); - if (tmp.find(' ') != std::string::npos){ - tmp.erase(0,tmp.find(' ')+1); - }else{ - tmp = ""; - } - } - } - break; - } - case 's': { //second-seek - unsigned int ms = JSON::Value(usr->S.Received().get().substr(2)).asInt(); - usr->myRing->waiting = false; - usr->myRing->starved = false; - usr->myRing->b = thisStream->msSeek(ms, allowedTracks); - if (usr->myRing->playCount > 0){ - usr->myRing->playCount = 0; - } - break; - } - case 'p': { //play - usr->myRing->playCount = -1; - if (usr->S.Received().get().size() >= 2){ - usr->playUntil = atoi(usr->S.Received().get().substr(2).c_str()); - }else{ - usr->playUntil = 0; - } - break; - } - case 'o': { //once-play - if (usr->myRing->playCount >= 0){ - usr->myRing->playCount++; - } - break; - } - case 'q': { //quit-playing - usr->myRing->playCount = 0; - break; - } - } - usr->S.Received().get().clear(); - } - } - } - if (usr->myRing->waiting || !usr->myRing->playCount){ - Util::sleep(300); //sleep 300ms - } - } - usr->Disconnect("Socket closed."); - thisStream->dropRing(usr->myRing); - thisStream->removeUser(usr); - delete usr; - } - - ///\brief Starts a loop, waiting for connections to send data to. - ///\param argc The number of arguments to the program. - ///\param argv The arguments to the program. - ///\return The return code of the buffer. - int Start(int argc, char ** argv){ - Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); - conf.addOption("stream_name", - JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"Name of the stream this buffer will be providing.\"}")); - conf.addOption("awaiting_ip", - JSON::fromString( - "{\"arg_num\":2, \"arg\":\"string\", \"default\":\"\", \"help\":\"IP address to expect incoming data from. This will completely disable reading from standard input if used.\"}")); - conf.addOption("reportstats", - JSON::fromString("{\"default\":0, \"help\":\"Report stats to a controller process.\", \"short\":\"s\", \"long\":\"reportstats\"}")); - conf.addOption("time", - JSON::fromString( - "{\"default\":20000, \"arg\": \"integer\", \"help\":\"Buffer a specied amount of time in ms.\", \"short\":\"t\", \"long\":\"time\"}")); - conf.parseArgs(argc, argv); - - std::string name = conf.getString("stream_name"); - - SS = Util::Stream::makeLive(name); - if ( !SS.connected()){ - perror("Could not create stream socket"); - return 1; - } - SS.setBlocking(false); - conf.activate(); - #if defined(_TTHREAD_POSIX_) && defined(WITH_THREADNAMES) && !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) || defined(_WIN32) || defined(__CYGWIN__)) - pthread_setname_np(pthread_self(), "Main accepter"); - #endif - thisStream = Stream::get(); - thisStream->setName(name); - thisStream->setBufferTime(conf.getInteger("time")); - Socket::Connection incoming; - Socket::Connection std_input(fileno(stdin)); - - if (conf.getBool("reportstats")){ - tthread::thread StatsThread(handleStats, 0); - StatsThread.detach(); - } - std::string await_ip = conf.getString("awaiting_ip"); - if (await_ip == ""){ - tthread::thread StdinThread(handleStdin, 0); - StdinThread.detach(); - }else{ - thisStream->setWaitingIP(await_ip); - } - - unsigned int userId = 0; - SS.setBlocking(true); - while (buffer_running && SS.connected() && conf.is_active){ - //check for new connections, accept them if there are any - //starts a thread for every accepted connection - incoming = SS.accept(true); - if (incoming.connected()){ - tthread::thread thisUser(handleUser, (void *)new user(incoming, ++userId)); - thisUser.detach(); - } - } //main loop - - // disconnect listener - buffer_running = false; - SS.close(); - delete thisStream; - return 0; - } - -} //Buffer namespace - -///\brief Entry point for Buffer, simply calls Buffer::Start(). -int main(int argc, char ** argv){ - return Buffer::Start(argc, argv); -} //main diff --git a/src/buffer/buffer_stream.cpp b/src/buffer/buffer_stream.cpp deleted file mode 100644 index 0336ea0d..00000000 --- a/src/buffer/buffer_stream.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/// \file buffer_stream.cpp -/// Contains definitions for buffer streams. - -#include "buffer_stream.h" -#include <mist/timing.h> -#include <mist/defines.h> -#include <stdlib.h> - -namespace Buffer { - - static JSON::Value ctrl_log; - - void Stream::Log(std::string type, std::string message){ - JSON::Value l; - l.append(type); - l.append(message); - ctrl_log.append(l); - } - - /// Stores the singleton reference. - Stream * Stream::ref = 0; - - /// Returns a reference to the singleton instance of this class. - /// \return A reference to the class. - Stream * Stream::get(){ - static tthread::mutex creator; - if (ref == 0){ - //prevent creating two at the same time - creator.lock(); - if (ref == 0){ - ref = new Stream(); - ref->metadata.live = true; - } - creator.unlock(); - } - return ref; - } - - /// Creates a new DTSC::Stream object, private function so only one instance can exist. - Stream::Stream() : DTSC::Stream(5){} - - /// Do cleanup on delete. - Stream::~Stream(){ - tthread::lock_guard<tthread::recursive_mutex> guard(stats_mutex); - if (users.size() > 0){ - for (usersIt = users.begin(); usersIt != users.end(); usersIt++){ - if (( * *usersIt).S.connected()){ - ( * *usersIt).S.close(); - } - } - } - moreData.notify_all(); - } - - /// Calculate and return the current statistics. - /// \return The current statistics in JSON format. - std::string & Stream::getStats(){ - static std::string ret; - long long int now = Util::epoch(); - unsigned int tot_up = 0, tot_down = 0, tot_count = 0; - tthread::lock_guard<tthread::recursive_mutex> guard(stats_mutex); - if (users.size() > 0){ - for (usersIt = users.begin(); usersIt != users.end(); usersIt++){ - tot_down += ( * *usersIt).curr_down; - tot_up += ( * *usersIt).curr_up; - tot_count++; - } - } - Storage["totals"]["down"] = tot_down; - Storage["totals"]["up"] = tot_up; - Storage["totals"]["count"] = tot_count; - Storage["totals"]["now"] = now; - Storage["buffer"] = name; - - rw_mutex.lock(); - Storage["meta"] = metadata.toJSON(); - rw_mutex.unlock(); - if (Storage["meta"].isMember("tracks")){ - for (JSON::ObjIter oIt = Storage["meta"]["tracks"].ObjBegin(); oIt != Storage["meta"]["tracks"].ObjEnd(); ++oIt){ - oIt->second.removeMember("fragments"); - oIt->second.removeMember("keys"); - oIt->second.removeMember("parts"); - oIt->second.removeMember("idheader"); - oIt->second.removeMember("commentheader"); - } - } - - Storage["ctrl_log"] = ctrl_log; - ctrl_log.null(); - - ret = Storage.toString(); - Storage["log"].null(); - return ret; - } - - /// Set the IP address to accept push data from. - /// \param ip The new IP to accept push data from. - void Stream::setWaitingIP(std::string ip){ - waiting_ip = ip; - } - - ///\brief Check if this is the IP address to accept push data from. - ///\param push_request The IP address to check, followed by a space and the password to check. - ///\return True if it is the correct address or password, false otherwise. - bool Stream::checkWaitingIP(std::string push_request){ - std::string ip = push_request.substr(0, push_request.find(' ')); - std::string pass = push_request.substr(push_request.find(' ') + 1); - if (waiting_ip.length() > 0 && waiting_ip[0] == '@'){ - if (pass == waiting_ip.substr(1)){ - return true; - }else{ - Log("BUFF", "Push to stream " + name + " denied, incorrect password: "+pass); - return false; - } - }else{ - if (ip == waiting_ip || ip == "::ffff:" + waiting_ip){ - return true; - }else{ - Log("BUFF", "Push to stream " + name + " denied, wrong IP: "+ip+" != (::ffff:)"+waiting_ip); - return false; - } - } - } - - /// Stores intermediate statistics. - /// \param username The name of the user. - /// \param stats The final statistics to store. - void Stream::saveStats(std::string username, Stats & stats){ - tthread::lock_guard<tthread::recursive_mutex> guard(stats_mutex); - Storage["curr"][username]["connector"] = stats.connector; - Storage["curr"][username]["up"] = stats.up; - Storage["curr"][username]["down"] = stats.down; - Storage["curr"][username]["conntime"] = stats.conntime; - Storage["curr"][username]["host"] = stats.host; - Storage["curr"][username]["start"] = Util::epoch() - stats.conntime; - } - - /// Stores final statistics. - /// \param username The name of the user. - /// \param stats The final statistics to store. - /// \param reason The reason for disconnecting. - void Stream::clearStats(std::string username, Stats & stats, std::string reason){ - tthread::lock_guard<tthread::recursive_mutex> guard(stats_mutex); - if (Storage["curr"].isMember(username)){ - Storage["curr"].removeMember(username); - #if DEBUG >= 4 - std::cout << "Disconnected user " << username << ": " << reason << ". " << stats.connector << " transferred " << stats.up << " up and " - << stats.down << " down in " << stats.conntime << " seconds to " << stats.host << std::endl; - #endif - } - Storage["log"][username]["connector"] = stats.connector; - Storage["log"][username]["up"] = stats.up; - Storage["log"][username]["down"] = stats.down; - Storage["log"][username]["conntime"] = stats.conntime; - Storage["log"][username]["host"] = stats.host; - Storage["log"][username]["start"] = Util::epoch() - stats.conntime; - } - - /// The deletion callback override that will disconnect users - /// whom are currently receiving a tag that is being deleted. - void Stream::deletionCallback(DTSC::livePos deleting){ - tthread::lock_guard<tthread::recursive_mutex> guard(stats_mutex); - for (usersIt = users.begin(); usersIt != users.end(); usersIt++){ - if ((*usersIt)->myRing->playCount && (*usersIt)->myRing->b == deleting){ - (*usersIt)->Disconnect("Buffer underrun"); - } - } - } - - /// Sets the buffer name. - /// \param n The new name of the buffer. - void Stream::setName(std::string n){ - name = n; - } - - void Stream::sendPacket(DTSC::livePos & num, Socket::Connection & S){ - rw_mutex.lock(); - if (!getPacket(num) && buffers.size()){ - DEBUG_MSG(DLVL_DEVEL, "Oh noes, ran out of packets! Resetting to beginning..."); - num = buffers.rbegin()->first; - } - getPacket(num).sendTo(S); - rw_mutex.unlock(); - } - - /// parsePacket override that will lock the rw_mutex during parsing. - bool Stream::parsePacket(std::string & buffer){ - rw_mutex.lock(); - bool ret = DTSC::Stream::parsePacket(buffer); - rw_mutex.unlock(); - if (ret){ - rw_change.notify_all(); - moreData.notify_all(); - } - return ret; - } - - /// getNext override that will lock the rw_mutex during checking. - DTSC::livePos Stream::getNext(DTSC::livePos & pos, std::set<int> & allowedTracks){ - tthread::lock_guard<tthread::mutex> guard(rw_mutex); - return DTSC::Stream::getNext(pos, allowedTracks); - } - - /// endStream override that will lock the rw_mutex - void Stream::endStream(){ - tthread::lock_guard<tthread::mutex> guard(rw_mutex); - return DTSC::Stream::endStream(); - } - - /// Removes a track and all related buffers from the stream. - void Stream::removeTrack(int trackId){ - rw_mutex.lock(); - metadata.tracks.erase(trackId); - rw_mutex.unlock(); - std::set<DTSC::livePos> toDelete; - for (std::map<DTSC::livePos, JSON::Value >::iterator it = buffers.begin(); it != buffers.end(); it++){ - if (it->first.trackID == (unsigned long long int)trackId){ - toDelete.insert(it->first); - } - } - while (toDelete.size()){ - deletionCallback(*toDelete.begin()); - buffers.erase(*toDelete.begin()); - keyframes[trackId].erase(*toDelete.begin()); - toDelete.erase(toDelete.begin()); - } - } - - /// Calls removeTrack on all tracks that were streaming from this socket number. - void Stream::removeSocket(int sockNo){ - std::set<int> toDelete; - std::map<int,DTSC::Track>::iterator it; - rw_mutex.lock(); - for (it = metadata.tracks.begin(); it != metadata.tracks.end(); ++it){ - if ((it->first & (sockNo << 16)) == (sockNo << 16)){ - toDelete.insert(it->first); - Log("BUFF", "Stream "+name+" lost input for track: "+ it->second.getIdentifier()); - } - } - rw_mutex.unlock(); - while (toDelete.size()){ - removeTrack(*toDelete.begin()); - toDelete.erase(toDelete.begin()); - } - } - - /// parsePacket override that will lock the rw_mutex during parsing. - bool Stream::parsePacket(Socket::Connection & c){ - bool ret = false; - if (!c.spool()){ - return ret; - } - rw_mutex.lock(); - while (DTSC::Stream::parsePacket(c.Received())){ - ret = true; - } - rw_mutex.unlock(); - if (ret){ - rw_change.notify_all(); - moreData.notify_all(); - } - return ret; - } - - /// Metadata sender that locks the rw_mutex during sending. - void Stream::sendMeta(Socket::Connection & s){ - if (metadata){ - rw_mutex.lock(); - DTSC::Meta tmpMeta = metadata; - rw_mutex.unlock(); - tmpMeta.send(s); - } - } - - /// Add a user to the userlist. - /// \param newUser The user to be added. - void Stream::addUser(user * newUser){ - tthread::lock_guard<tthread::recursive_mutex> guard(stats_mutex); - users.insert(newUser); - } - - /// Removes a user from the userlist. - /// \param oldUser The user to be removed. - void Stream::removeUser(user * oldUser){ - tthread::lock_guard<tthread::recursive_mutex> guard(stats_mutex); - users.erase(oldUser); - } - - /// Blocks the thread until new data is available. - void Stream::waitForData(){ - tthread::lock_guard<tthread::recursive_mutex> guard(stats_mutex); - moreData.wait(stats_mutex); - } - - ///Creates a new user from a newly connected socket. - ///Also prints "User connected" text to stdout. - ///\param fd A connection to the user. - ///\param ID Unique ID of the user. - user::user(Socket::Connection fd, long long ID){ - sID = JSON::Value(ID).asString(); - S = fd; - curr_up = 0; - curr_down = 0; - myRing = 0; - } //constructor - - ///Disconnects the current user. Doesn't do anything if already disconnected. - ///Prints "Disconnected user" to stdout if disconnect took place. - ///\param reason The reason for disconnecting the user. - void user::Disconnect(std::string reason){ - S.close(); - Stream::get()->clearStats(sID, lastStats, reason); - } //Disconnect - - ///Default stats constructor. - ///Should not be used. - Stats::Stats(){ - up = 0; - down = 0; - conntime = 0; - } - - ///Stats constructor reading a string. - ///Reads a stats string and parses it to the internal representation. - ///\param s The string of stats. - Stats::Stats(std::string s){ - size_t f = s.find(' '); - if (f != std::string::npos){ - host = s.substr(0, f); - s.erase(0, f + 1); - } - f = s.find(' '); - if (f != std::string::npos){ - connector = s.substr(0, f); - s.erase(0, f + 1); - } - f = s.find(' '); - if (f != std::string::npos){ - conntime = atoi(s.substr(0, f).c_str()); - s.erase(0, f + 1); - } - f = s.find(' '); - if (f != std::string::npos){ - up = atoi(s.substr(0, f).c_str()); - s.erase(0, f + 1); - down = atoi(s.c_str()); - } - } - -} diff --git a/src/buffer/buffer_stream.h b/src/buffer/buffer_stream.h deleted file mode 100644 index 441ab3f1..00000000 --- a/src/buffer/buffer_stream.h +++ /dev/null @@ -1,106 +0,0 @@ -/// \file buffer_stream.h -/// Contains definitions for buffer streams. - -#pragma once -#include <string> -#include <mist/dtsc.h> -#include <mist/json.h> -#include <mist/socket.h> -#include <mist/tinythread.h> - -namespace Buffer { - - /// Converts a stats line to up, down, host, connector and conntime values. - class Stats{ - public: - unsigned int up;///<The amount of bytes sent upstream. - unsigned int down;///<The amount of bytes received downstream. - std::string host;///<The connected host. - std::string connector;///<The connector the user is connected with. - unsigned int conntime;///<The amount of time the user is connected. - Stats(std::string s); - Stats(); - }; - - ///\brief Keeps track of connected users. - /// - ///Keeps track of which buffer the user currently uses, - ///and its connection status. - class user{ - public: - DTSC::Ring * myRing; ///< Ring of the buffer for this user. - unsigned int playUntil; ///< Time until where is being played or zero if undefined. - Stats lastStats; ///< Holds last known stats for this connection. - Stats tmpStats; ///< Holds temporary stats for this connection. - std::string sID; ///< Holds the connection ID. - unsigned int curr_up; ///< Holds the current estimated transfer speed up. - unsigned int curr_down; ///< Holds the current estimated transfer speed down. - Socket::Connection S; ///< Connection to user - /// Creates a new user from a newly connected socket. - user(Socket::Connection fd, long long int ID); - /// Disconnects the current user. Doesn't do anything if already disconnected. - void Disconnect(std::string reason); - }; - - /// Keeps track of a single streams inputs and outputs, taking care of thread safety and all other related issues. - class Stream : public DTSC::Stream{ - public: - /// Get a reference to this Stream object. - static Stream * get(); - /// Get the current statistics in JSON format. - std::string & getStats(); - /// Set the IP address to accept push data from. - void setWaitingIP(std::string ip); - /// Check if this is the IP address to accept push data from. - bool checkWaitingIP(std::string ip); - /// Sets the current socket for push data. - bool setInput(Socket::Connection S); - /// Gets the current socket for push data. - Socket::Connection & getIPInput(); - /// Send a packet while locking the mutex. - void sendPacket(DTSC::livePos & num, Socket::Connection & S); - /// Stores intermediate statistics. - void saveStats(std::string username, Stats & stats); - /// Stores final statistics. - void clearStats(std::string username, Stats & stats, std::string reason); - /// Sets the buffer name. - void setName(std::string n); - /// Add a user to the userlist. - void addUser(user * newUser); - /// Delete a user from the userlist. - void removeUser(user * oldUser); - /// Blocks the thread until new data is available. - void waitForData(); - /// Sends the metadata to a specific socket - void sendMeta(Socket::Connection & s); - /// Cleanup function - ~Stream(); - /// Removes a track and all related buffers from the stream. - void removeTrack(int trackId); - /// Calls removeTrack on all tracks that were streaming from this socket number. - void removeSocket(int sockNo); - /// Thread-safe parsePacket override. - bool parsePacket(std::string & buffer); - /// Thread-safe parsePacket override. - bool parsePacket(Socket::Connection & c); - /// Logs a message to the controller. - void Log(std::string type, std::string message); - DTSC::livePos getNext(DTSC::livePos & pos, std::set<int> & allowedTracks); - void endStream(); - private: - void deletionCallback(DTSC::livePos deleting); - tthread::mutex rw_mutex; ///< Mutex for read/write locking. - tthread::condition_variable rw_change; ///< Triggered when reader/writer count changes. - static Stream * ref; - Stream(); - JSON::Value Storage; ///< Global storage of data. - std::string waiting_ip; ///< IP address for media push. - Socket::Connection ip_input; ///< Connection used for media push. - tthread::recursive_mutex stats_mutex; ///< Mutex for stats/users modifications. - std::set<user*> users; ///< All connected users. - std::set<user*>::iterator usersIt; ///< Iterator for all connected users. - std::string name; ///< Name for this buffer. - tthread::condition_variable moreData; ///< Triggered when more data becomes available. - }; -} -; diff --git a/src/buffer/player.cpp b/src/buffer/player.cpp deleted file mode 100644 index b211e8fe..00000000 --- a/src/buffer/player.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/// \file player.cpp -/// Holds all code for the MistPlayer application used for VoD streams. - -#include <iostream>//for std::cerr -#include <stdio.h> //for fileno -#include <stdlib.h> //for atoi -#include <sys/time.h> -#include <mist/dtsc.h> -#include <mist/json.h> -#include <mist/config.h> -#include <mist/socket.h> -#include <mist/timing.h> -#include <mist/procs.h> -#include <mist/stream.h> -#include <mist/defines.h> - -//under cygwin, recv blocks for ~15ms if no data is available. -//This is a hack to keep performance decent with that bug present. -#ifdef __CYGWIN__ -#define CYG_DEFI int cyg_count; -#define CYG_INCR cyg_count++; -#define CYG_LOOP (cyg_count % 20 == 0) && -#else -#define CYG_DEFI -#define CYG_INCR -#define CYG_LOOP -#endif - -///Converts a stats line to up, down, host, connector and conntime values. -class Stats{ - public: - unsigned int up;///<The amount of bytes sent upstream. - unsigned int down;///<The amount of bytes received downstream. - std::string host;///<The connected host. - std::string connector;///<The connector the user is connected with. - unsigned int conntime;///<The amount of time the user is connected. - ///\brief Default stats constructor. - /// - ///Should not be used. - Stats(){ - up = 0; - down = 0; - conntime = 0; - } - ; - ///\brief Stats constructor reading a string. - /// - ///Reads a stats string and parses it to the internal representation. - ///\param s The string of stats. - Stats(std::string s){ - size_t f = s.find(' '); - if (f != std::string::npos){ - host = s.substr(0, f); - s.erase(0, f + 1); - } - f = s.find(' '); - if (f != std::string::npos){ - connector = s.substr(0, f); - s.erase(0, f + 1); - } - f = s.find(' '); - if (f != std::string::npos){ - conntime = atoi(s.substr(0, f).c_str()); - s.erase(0, f + 1); - } - f = s.find(' '); - if (f != std::string::npos){ - up = atoi(s.substr(0, f).c_str()); - s.erase(0, f + 1); - down = atoi(s.c_str()); - } - } -}; - -std::string intToBin(long long int number){ - std::string result; - result.resize(8); - for (int i = 7; i >= 0; i--){ - result[i] = number & 0xFF; - number >>= 8; - } - return result; -} - -int main(int argc, char** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - conf.addOption("filename", - JSON::fromString("{\"arg_num\":1, \"help\":\"Name of the file to write to stdout.\"}")); - conf.addOption("streamname", - JSON::fromString("{\"arg\":\"string\",\"short\":\"s\",\"long\":\"stream\",\"help\":\"The name of the stream that this connector will transmit.\"}")); - conf.parseArgs(argc, argv); - conf.activate(); - int playing = 0; - - Socket::Connection in_out = Socket::Connection(fileno(stdout), fileno(stdin)); - - DTSC::File source = DTSC::File(conf.getString("filename")); - - if ( !source.getMeta().isFixed()){ - std::cerr << "Encountered a non-fixed file." << std::endl; - return 1; - } - - std::string streamname = conf.getString("streamname"); - source.getMeta().send(in_out); - - JSON::Value pausemark; - pausemark["trackid"] = 0ll; - pausemark["mark"] = "pause"; - pausemark["time"] = 0ll; - - Socket::Connection StatsSocket = Socket::Connection(Util::getTmpFolder() + "statistics", true); - int lastSent = Util::epoch(); //time last packet was sent - - JSON::Value last_pack; - - bool meta_sent = false; - int playUntil = -1; - long long max_lead_time = 7500;//maximum time in ms that the player can be faster than real-time - long long now, prevTimestamp = 0; //for timing of sending packets - std::set<int> newSelect; - Stats sts; - CYG_DEFI - - while (in_out.connected() && (Util::epoch() - lastSent < 60) && conf.is_active){ - CYG_INCR - if (CYG_LOOP in_out.spool()){ - while (in_out.Received().size()){ - //delete anything that doesn't end with a newline - if ( *(in_out.Received().get().rbegin()) != '\n'){ - in_out.Received().get().clear(); - continue; - } - in_out.Received().get().resize(in_out.Received().get().size() - 1); - if ( !in_out.Received().get().empty()){ - DEBUG_MSG(DLVL_HIGH, "Player received: %s", in_out.Received().get().c_str()); - switch (in_out.Received().get()[0]){ - case 'P': { //Push -#if DEBUG >= 4 - std::cerr << "Received push - ignoring (" << in_out.Received().get() << ")" << std::endl; -#endif - in_out.close(); //pushing to VoD makes no sense - break; - } - case 'S': { //Stats - if ( !StatsSocket.connected()){ - StatsSocket = Socket::Connection(Util::getTmpFolder() + "statistics", true); - } - if (StatsSocket.connected()){ - sts = Stats(in_out.Received().get().substr(2)); - JSON::Value json_sts; - json_sts["vod"]["down"] = (long long int)sts.down; - json_sts["vod"]["up"] = (long long int)sts.up; - json_sts["vod"]["time"] = (long long int)sts.conntime; - json_sts["vod"]["host"] = sts.host; - json_sts["vod"]["connector"] = sts.connector; - json_sts["vod"]["filename"] = conf.getString("filename"); - json_sts["vod"]["now"] = Util::epoch(); - json_sts["vod"]["start"] = Util::epoch() - sts.conntime; - if ( !meta_sent){ - json_sts["vod"]["meta"] = source.getMeta().toJSON(); - json_sts["vod"]["meta"]["is_fixed"] = 1; - for (JSON::ObjIter oIt = json_sts["vod"]["meta"]["tracks"].ObjBegin(); oIt != json_sts["vod"]["meta"]["tracks"].ObjEnd(); oIt++){ - oIt->second.removeMember("keys"); - oIt->second.removeMember("fragments"); - oIt->second.removeMember("parts"); - } - meta_sent = true; - } - StatsSocket.SendNow(json_sts.toString()); - StatsSocket.SendNow("\n\n", 2); - StatsSocket.flush(); - } - break; - } - case 's': { //second-seek - int ms = JSON::Value(in_out.Received().get().substr(2)).asInt(); - source.seek_time(ms); - lastSent = Util::epoch(); - prevTimestamp = 0; - playUntil = 0; - break; - } - case 'p': { //play - playing = -1; - lastSent = Util::epoch(); - in_out.setBlocking(false); - prevTimestamp = 0; - if (in_out.Received().get().size() >= 2){ - playUntil = atoi(in_out.Received().get().substr(2).c_str()); - }else{ - playUntil = 0; - } - break; - } - case 'o': { //once-play - if (playing <= 0){ - playing = 1; - } - prevTimestamp = 0; - ++playing; - in_out.setBlocking(false); - break; - } - case 'q': { //quit-playing - if (playing != 0){ - DEBUG_MSG(DLVL_HIGH, "Pausemark sent"); - pausemark["time"] = source.getJSON()["time"]; - pausemark.sendTo(in_out); - } - playing = 0; - in_out.setBlocking(true); - break; - } - case 't': { - newSelect.clear(); - std::string tmp = in_out.Received().get().substr(2); - while (tmp != ""){ - newSelect.insert(atoi(tmp.substr(0,tmp.find(' ')).c_str())); - if (tmp.find(' ') != std::string::npos){ - tmp.erase(0,tmp.find(' ')+1); - }else{ - tmp = ""; - } - } - source.selectTracks(newSelect); - break; - } -#if DEBUG >= 4 - default: { - std::cerr << "MistPlayer received an unknown command: " << in_out.Received().get() << std::endl; - break; - } -#endif - } - in_out.Received().get().clear(); - } - } - } - if (playing != 0){ - now = Util::getMS(); - source.seekNext(); - if ( !source.getJSON()){ - DEBUG_MSG(DLVL_HIGH, "Seek failed (end of file?) - stopping playback"); - playing = 0; - } - if (playing > 0 && source.atKeyframe()){ - --playing; - } - if (prevTimestamp == 0){ - prevTimestamp = now - source.getJSON()["time"].asInt(); - } - if (playing == -1 && playUntil == 0 && source.getJSON()["time"].asInt() > now - prevTimestamp + max_lead_time){ - Util::sleep(source.getJSON()["time"].asInt() - (now - prevTimestamp + max_lead_time)); - } - if ( playUntil && playUntil <= source.getJSON()["time"].asInt()){ - playing = 0; - } - if (playing == 0){ - DEBUG_MSG(DLVL_HIGH, "Pausemark sent"); - pausemark["time"] = source.getJSON()["time"]; - pausemark.sendTo(in_out); - in_out.setBlocking(true); - }else{ - lastSent = Util::epoch(); - DEBUG_MSG(DLVL_HIGH, "Playing %lliT%lli", source.getJSON()["trackid"].asInt(), source.getJSON()["time"].asInt()); - source.getJSON().sendTo(in_out); - } - }else{ - Util::sleep(10); - } - } - StatsSocket.close(); - in_out.close(); - return 0; -} diff --git a/src/connectors/conn_http.cpp b/src/connectors/conn_http.cpp index 8b01c7f6..2fdd401f 100644 --- a/src/connectors/conn_http.cpp +++ b/src/connectors/conn_http.cpp @@ -25,6 +25,7 @@ #include "embed.js.h" + /// Holds everything unique to HTTP Connectors. namespace Connector_HTTP { @@ -115,14 +116,15 @@ namespace Connector_HTTP { ///Displays a friendly error message. ///\param H The request that was being handled upon timeout. ///\param conn The connection to the client that issued the request. + ///\param msg The message to print to the client. ///\return A timestamp indicating when the request was parsed. - long long int proxyHandleTimeout(HTTP::Parser & H, Socket::Connection & conn){ + long long int proxyHandleTimeout(HTTP::Parser & H, Socket::Connection & conn, std::string msg){ H.Clean(); H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); H.SetBody( - "<!DOCTYPE html><html><head><title>Gateway timeout</title></head><body><h1>Gateway timeout</h1>Though the server understood your request and attempted to handle it, somehow handling it took longer than it should. Your request has been cancelled - please try again later.</body></html>"); + "<!DOCTYPE html><html><head><title>"+msg+"</title></head><body><h1>"+msg+"</h1>Though the server understood your request and attempted to handle it, somehow handling it took longer than it should. Your request has been cancelled - please try again later.</body></html>"); long long int ret = Util::getMS(); - conn.SendNow(H.BuildResponse("504", "Gateway Timeout")); + conn.SendNow(H.BuildResponse("504", msg)); return ret; } @@ -404,6 +406,7 @@ namespace Connector_HTTP { H.Clean(); ConnConn * myCConn = 0; + unsigned int counter = 0; //loop until a connection is available/created while (!myCConn){ //lock the connection mutex before trying anything @@ -412,6 +415,12 @@ namespace Connector_HTTP { if ( !connectorConnections.count(uid)){ connectorConnections[uid] = new ConnConn(new Socket::Connection(Util::getTmpFolder() + connector)); connectorConnections[uid]->conn->setBlocking(false); //do not block on spool() with no data + if (!connectorConnections[uid]->conn->spool() && !connectorConnections[uid]->conn){ + //unlock the connection mutex before exiting + connMutex.unlock(); + DEBUG_MSG(DLVL_FAIL, "Created new connection (%s) failed - aborting request!", uid.c_str()); + return Util::getMS(); + } DEBUG_MSG(DLVL_HIGH, "Created new connection %s", uid.c_str()); } @@ -420,11 +429,17 @@ namespace Connector_HTTP { myCConn = connectorConnections[uid]; //if the connection is dead, delete it and re-loop if (!myCConn->conn->spool() && !myCConn->conn->connected()){ + counter++; DEBUG_MSG(DLVL_HIGH, "Resetting existing connection %s", uid.c_str()); connectorConnections.erase(uid); myCConn->inUse.unlock(); delete myCConn; myCConn = 0; + if (counter++ > 2){ + connMutex.unlock(); + DEBUG_MSG(DLVL_FAIL, "Created new connection (%s) failed - aborting request!", uid.c_str()); + return Util::getMS(); + } }else{ DEBUG_MSG(DLVL_HIGH, "Using active connection %s", uid.c_str()); } @@ -477,7 +492,7 @@ namespace Connector_HTTP { myCConn->inUse.unlock(); //unset to only read headers H.headerOnly = false; - return proxyHandleTimeout(H, conn); + return proxyHandleTimeout(H, conn, "Timeout: fragment too new"); } myCConn->lastUse = 0; timeout = 0; @@ -495,9 +510,9 @@ namespace Connector_HTTP { myCConn->inUse.unlock(); //unset to only read headers H.headerOnly = false; - return proxyHandleTimeout(H, conn); + return proxyHandleTimeout(H, conn, "Gateway timeout while waiting for response"); }else{ - Util::sleep(5); + Util::sleep(100); } } //unset to only read headers @@ -506,7 +521,7 @@ namespace Connector_HTTP { //failure, disconnect and sent error to user myCConn->conn->close(); myCConn->inUse.unlock(); - return proxyHandleTimeout(H, conn); + return proxyHandleTimeout(H, conn, "Gateway connection dropped"); }else{ long long int ret = Util::getMS(); //success, check type of response @@ -699,6 +714,14 @@ int main(int argc, char ** argv){ Connector_HTTP::capabilities.removeMember((*it).substr(8)); } } + if ((*it).substr(0, 7) == "MistOut"){ + arg_one = Util::getMyPath() + (*it); + conn_args[0] = arg_one.c_str(); + Connector_HTTP::capabilities[(*it).substr(7)] = JSON::fromString(Util::Procs::getOutputOf((char**)conn_args)); + if (Connector_HTTP::capabilities[(*it).substr(7)].size() < 1){ + Connector_HTTP::capabilities.removeMember((*it).substr(7)); + } + } } return conf.serveThreadedSocket(Connector_HTTP::proxyHandleHTTPConnection); diff --git a/src/connectors/conn_http_dynamic.cpp b/src/connectors/conn_http_dynamic.cpp deleted file mode 100644 index cea7caa5..00000000 --- a/src/connectors/conn_http_dynamic.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/// \file conn_http_dynamic.cpp -/// Contains the main code for the HTTP Dynamic Connector - -#include <iostream> -#include <sstream> -#include <queue> -#include <cstdlib> -#include <cstdio> -#include <cmath> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <getopt.h> -#include <mist/socket.h> -#include <mist/http_parser.h> -#include <mist/json.h> -#include <mist/dtsc.h> -#include <mist/flv_tag.h> -#include <mist/base64.h> -#include <mist/amf.h> -#include <mist/mp4.h> -#include <mist/mp4_adobe.h> -#include <mist/config.h> -#include <sstream> -#include <mist/stream.h> -#include <mist/timing.h> - -/// Holds everything unique to HTTP Connectors. -namespace Connector_HTTP { - - std::set<int> videoTracks;///<< Holds valid video tracks for playback - long long int audioTrack = 0;///<< Holds audio track ID for playback - void getTracks(DTSC::Meta & metadata){ - videoTracks.clear(); - for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ - if (it->second.codec == "H264" || it->second.codec == "H263" || it->second.codec == "VP6"){ - videoTracks.insert(it->first); - } - if (it->second.codec == "AAC" || it->second.codec == "MP3"){ - audioTrack = it->first; - } - } - } - - - ///\brief Builds a bootstrap for use in HTTP Dynamic streaming. - ///\param streamName The name of the stream. - ///\param trackMeta The current metadata of this track, used to generate the index. - ///\param isLive Whether or not the stream is live. - ///\param fragnum The index of the current fragment. - ///\return The generated bootstrap. - std::string dynamicBootstrap(std::string & streamName, DTSC::Track & trackMeta, bool isLive = false, int fragnum = 0){ - std::string empty; - - MP4::ASRT asrt; - asrt.setUpdate(false); - asrt.setVersion(1); - //asrt.setQualityEntry(empty, 0); - if (isLive){ - asrt.setSegmentRun(1, 4294967295ul, 0); - }else{ - asrt.setSegmentRun(1, trackMeta.keys.size(), 0); - } - - MP4::AFRT afrt; - afrt.setUpdate(false); - afrt.setVersion(1); - afrt.setTimeScale(1000); - //afrt.setQualityEntry(empty, 0); - MP4::afrt_runtable afrtrun; - int i = 0; - for (std::deque<DTSC::Key>::iterator it = trackMeta.keys.begin(); it != trackMeta.keys.end(); it++){ - if (it->getLength()){ - afrtrun.firstFragment = it->getNumber(); - afrtrun.firstTimestamp = it->getTime(); - afrtrun.duration = it->getLength(); - afrt.setFragmentRun(afrtrun, i); - i++; - } - } - - MP4::ABST abst; - abst.setVersion(1); - abst.setBootstrapinfoVersion(1); - abst.setProfile(0); - abst.setUpdate(false); - abst.setTimeScale(1000); - abst.setLive(isLive); - abst.setCurrentMediaTime(trackMeta.lastms); - abst.setSmpteTimeCodeOffset(0); - abst.setMovieIdentifier(streamName); - abst.setSegmentRunTable(asrt, 0); - abst.setFragmentRunTable(afrt, 0); - - #if DEBUG >= 8 - std::cout << "Sending bootstrap:" << std::endl << abst.toPrettyString(0) << std::endl; - #endif - return std::string((char*)abst.asBox(), (int)abst.boxedSize()); - } - - ///\brief Builds an index file for HTTP Dynamic streaming. - ///\param streamName The name of the stream. - ///\param metadata The current metadata, used to generate the index. - ///\return The index file for HTTP Dynamic Streaming. - std::string dynamicIndex(std::string & streamName, DTSC::Meta & metadata){ - if ( !audioTrack){getTracks(metadata);} - std::stringstream Result; - Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl; - Result << " <manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">" << std::endl; - Result << " <id>" << streamName << "</id>" << std::endl; - Result << " <mimeType>video/mp4</mimeType>" << std::endl; - Result << " <deliveryType>streaming</deliveryType>" << std::endl; - if (metadata.vod){ - Result << " <duration>" << metadata.tracks[*videoTracks.begin()].lastms / 1000 << ".000</duration>" << std::endl; - Result << " <streamType>recorded</streamType>" << std::endl; - }else{ - Result << " <duration>0.00</duration>" << std::endl; - Result << " <streamType>live</streamType>" << std::endl; - } - for (std::set<int>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){ - Result << " <bootstrapInfo " - "profile=\"named\" " - "id=\"boot" << (*it) << "\" " - "url=\"" << (*it) << ".abst\">" - "</bootstrapInfo>" << std::endl; - } - for (std::set<int>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){ - Result << " <media " - "url=\"" << (*it) << "-\" " - "bitrate=\"" << metadata.tracks[(*it)].bps * 8 << "\" " - "bootstrapInfoId=\"boot" << (*it) << "\" " - "width=\"" << metadata.tracks[(*it)].width << "\" " - "height=\"" << metadata.tracks[(*it)].height << "\">" << std::endl; - Result << " <metadata>AgAKb25NZXRhRGF0YQMAAAk=</metadata>" << std::endl; - Result << " </media>" << std::endl; - } - Result << "</manifest>" << std::endl; - #if DEBUG >= 8 - std::cerr << "Sending this manifest:" << std::endl << Result.str() << std::endl; - #endif - return Result.str(); - } //BuildManifest - - ///\brief Main function for the HTTP Dynamic Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int dynamicConnector(Socket::Connection & conn){ - FLV::Tag tmp; //temporary tag - - DTSC::Stream Strm; //Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S; //HTTP Receiver en HTTP Sender. - - Socket::Connection ss( -1); - std::string streamname; - bool handlingRequest = false; - - int Quality = 0; - int ReqFragment = -1; - long long mstime = 0; - long long mslen = 0; - unsigned int lastStats = 0; - conn.setBlocking(false); //do not block on conn.spool() when no data is available - - while (conn.connected()){ - if ( !handlingRequest){ - if (conn.spool() && HTTP_R.Read(conn)){ - #if DEBUG >= 5 - std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; - #endif - conn.setHost(HTTP_R.GetHeader("X-Origin")); - streamname = HTTP_R.GetHeader("X-Stream"); - if ( !ss){ - ss = Util::Stream::getStream(streamname); - if ( !ss.connected()){ - HTTP_S.Clean(); - HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); - HTTP_S.SendResponse("404", "Not found", conn); - continue; - } - Strm.waitForMeta(ss); - } - if (HTTP_R.url.find(".abst") != std::string::npos){ - std::string streamID = HTTP_R.url.substr(streamname.size() + 10); - streamID = streamID.substr(0, streamID.find(".abst")); - HTTP_S.Clean(); - HTTP_S.SetBody(dynamicBootstrap(streamname, Strm.metadata.tracks[atoll(streamID.c_str())], Strm.metadata.live)); - HTTP_S.SetHeader("Content-Type", "binary/octet"); - HTTP_S.SetHeader("Cache-Control", "no-cache"); - HTTP_S.SendResponse("200", "OK", conn); - HTTP_R.Clean(); //clean for any possible next requests - continue; - } - if (HTTP_R.url.find("f4m") == std::string::npos){ - std::string tmp_qual = HTTP_R.url.substr(HTTP_R.url.find("/", 10) + 1); - Quality = atoi(tmp_qual.substr(0, tmp_qual.find("Seg") - 1).c_str()); - int temp; - temp = HTTP_R.url.find("Seg") + 3; - temp = HTTP_R.url.find("Frag") + 4; - ReqFragment = atoi(HTTP_R.url.substr(temp).c_str()); - #if DEBUG >= 5 - printf("Video track %d, fragment %d\n", Quality, ReqFragment); - #endif - if (!audioTrack){getTracks(Strm.metadata);} - DTSC::Track & vidTrack = Strm.metadata.tracks[Quality]; - mstime = 0; - mslen = 0; - for (std::deque<DTSC::Key>::iterator it = vidTrack.keys.begin(); it != vidTrack.keys.end(); it++){ - if (it->getNumber() >= ReqFragment){ - mstime = it->getTime(); - mslen = it->getLength(); - if (Strm.metadata.live){ - if (it == vidTrack.keys.end() - 2){ - HTTP_S.Clean(); - HTTP_S.SetBody("Proxy, re-request this in a second or two.\n"); - HTTP_S.SendResponse("208", "Ask again later", conn); - HTTP_R.Clean(); //clean for any possible next requests - std::cout << "Fragment after fragment " << ReqFragment << " not available yet" << std::endl; - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){} - } - } - } - break; - } - } - if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead. - if (Strm.metadata.live){ - if (mstime == 0 && ReqFragment > 1){ - HTTP_S.Clean(); - HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); - HTTP_S.SendResponse("412", "Fragment out of range", conn); - HTTP_R.Clean(); //clean for any possible next requests - std::cout << "Fragment " << ReqFragment << " too old" << std::endl; - continue; - } - } - std::stringstream sstream; - sstream << "t " << Quality << " " << audioTrack << "\ns " << mstime << "\np " << (mstime + mslen) << "\n"; - ss.SendNow(sstream.str().c_str()); - - HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type", "video/mp4"); - HTTP_S.StartResponse(HTTP_R, conn); - //send the bootstrap - std::string bootstrap = dynamicBootstrap(streamname, Strm.metadata.tracks[Quality], Strm.metadata.live, ReqFragment); - HTTP_S.Chunkify(bootstrap, conn); - //send a zero-size mdat, meaning it stretches until end of file. - HTTP_S.Chunkify("\000\000\000\000mdat", 8, conn); - //send init data, if needed. - if (audioTrack > 0){ - tmp.DTSCAudioInit(Strm.metadata.tracks[audioTrack]); - tmp.tagTime(mstime); - HTTP_S.Chunkify(tmp.data, tmp.len, conn); - } - if (Quality > 0){ - tmp.DTSCVideoInit(Strm.metadata.tracks[Quality]); - tmp.tagTime(mstime); - HTTP_S.Chunkify(tmp.data, tmp.len, conn); - } - handlingRequest = true; - }else{ - HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type", "text/xml"); - HTTP_S.SetHeader("Cache-Control", "no-cache"); - HTTP_S.SetBody(dynamicIndex(streamname, Strm.metadata)); - HTTP_S.SendResponse("200", "OK", conn); - } - HTTP_R.Clean(); //clean for any possible next requests - }else{ - //sleep for 250ms before next attempt - Util::sleep(250); - } - } - if (ss.connected()){ - unsigned int now = Util::epoch(); - if (now != lastStats){ - lastStats = now; - ss.SendNow(conn.getStats("HTTP_Dynamic").c_str()); - } - if (handlingRequest && ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - if (Strm.lastType() == DTSC::PAUSEMARK){ - //send an empty chunk to signify request is done - HTTP_S.Chunkify("", 0, conn); - handlingRequest = false; - } - if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){ - //send a chunk with the new data - tmp.DTSCLoader(Strm); - HTTP_S.Chunkify(tmp.data, tmp.len, conn); - } - } - } - if ( !ss.connected()){ - break; - } - } - } - conn.close(); - ss.SendNow(conn.getStats("HTTP_Dynamic").c_str()); - ss.close(); - return 0; - } //Connector_HTTP_Dynamic main function - -} //Connector_HTTP_Dynamic namespace - -///\brief The standard process-spawning main function. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables HTTP protocol Adobe-specific dynamic streaming (also known as HDS)."; - capa["deps"] = "HTTP"; - capa["url_rel"] = "/dynamic/$/manifest.f4m"; - capa["url_prefix"] = "/dynamic/$/"; - capa["socket"] = "http_dynamic"; - capa["codecs"][0u][0u].append("H264"); - capa["codecs"][0u][0u].append("H263"); - capa["codecs"][0u][0u].append("VP6"); - capa["codecs"][0u][1u].append("AAC"); - capa["codecs"][0u][1u].append("MP3"); - capa["methods"][0u]["handler"] = "http"; - capa["methods"][0u]["type"] = "flash/11"; - capa["methods"][0u]["priority"] = 7ll; - conf.addBasicConnectorOptions(capa); - conf.parseArgs(argc, argv); - - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - - return conf.serveForkedSocket(Connector_HTTP::dynamicConnector); -} //main diff --git a/src/connectors/conn_http_json.cpp b/src/connectors/conn_http_json.cpp deleted file mode 100644 index 5c36e107..00000000 --- a/src/connectors/conn_http_json.cpp +++ /dev/null @@ -1,200 +0,0 @@ -///\file conn_http_json.cpp -///\brief Contains the main code for the HTTP JSON Connector - -#include <iostream> -#include <queue> -#include <sstream> -#include <iomanip> - -#include <cstdlib> -#include <cstdio> -#include <cmath> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <getopt.h> - -#include <mist/socket.h> -#include <mist/http_parser.h> -#include <mist/dtsc.h> -#include <mist/flv_tag.h> -#include <mist/amf.h> -#include <mist/config.h> -#include <mist/stream.h> -#include <mist/timing.h> - -///\brief Holds everything unique to HTTP Connectors. -namespace Connector_HTTP { - ///\brief Main function for the HTTP Progressive Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int JSONConnector(Socket::Connection & conn){ - DTSC::Stream Strm; //Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender. - bool inited = false;//Whether the stream is initialized - Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream. - std::string streamname;//Will contain the name of the stream. - - unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket. - unsigned int seek_sec = 0;//Seek position in ms - unsigned int seek_byte = 0;//Seek position in bytes - - std::stringstream jsondata; - - while (conn.connected()){ - //Only attempt to parse input when not yet init'ed. - if ( !inited){ - if (conn.spool() && HTTP_R.Read(conn)){ -#if DEBUG >= 5 - std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; -#endif - conn.setHost(HTTP_R.GetHeader("X-Origin")); - streamname = HTTP_R.GetHeader("X-Stream"); - - int start = 0; - if ( !HTTP_R.GetVar("start").empty()){ - start = atoi(HTTP_R.GetVar("start").c_str()); - } - if ( !HTTP_R.GetVar("starttime").empty()){ - start = atoi(HTTP_R.GetVar("starttime").c_str()); - } - if ( !HTTP_R.GetVar("apstart").empty()){ - start = atoi(HTTP_R.GetVar("apstart").c_str()); - } - if ( !HTTP_R.GetVar("ec_seek").empty()){ - start = atoi(HTTP_R.GetVar("ec_seek").c_str()); - } - if ( !HTTP_R.GetVar("fs").empty()){ - start = atoi(HTTP_R.GetVar("fs").c_str()); - } - //under 3 hours we assume seconds, otherwise byte position - if (start < 10800){ - seek_byte = start * 1000; //ms, not s - }else{ - seek_byte = start * 1000; //divide by 1mbit, then *1000 for ms. - } - // ready4data = true; - HTTP_R.Clean(); //clean for any possible next requests - jsondata.clear(); - jsondata << "["; - - //we are ready, connect the socket! - if ( !ss.connected()){ - ss = Util::Stream::getStream(streamname); - } - if ( !ss.connected()){ - #if DEBUG >= 1 - fprintf(stderr, "Could not connect to server for %s!\n", streamname.c_str()); - #endif - ss.close(); - HTTP_S.Clean(); - HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); - conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); - //ready4data = false; - inited = false; - continue; - } - - //wait until we have a header - while ( !Strm.metadata && ss.connected()){ - if (ss.spool()){ - Strm.parsePacket(ss.Received()); //read the metadata - }else{ - Util::sleep(5); - } - } - - seek_sec = seek_byte; - - std::stringstream cmd; - cmd << "t"; - - int tid = -1; - for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){ - if (it->second.type == "meta" ){ - if (tid == -1){ - tid = it->second.trackID; - } - cmd << " " << it->second.trackID; - } - } - - if( cmd.str() == "t" ){ - cmd.str(""); - cmd.clear(); - } - - int maxTime = Strm.metadata.tracks[tid].lastms; - - cmd << "\ns " << seek_sec << "\np " << maxTime << "\n"; - ss.SendNow(cmd.str().c_str(), cmd.str().size()); - inited = true; - - } - } - if (inited){ - - unsigned int now = Util::epoch(); - if (now != lastStats){ - lastStats = now; - ss.SendNow(conn.getStats("HTTP_JSON").c_str()); - } - - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - if(Strm.lastType() == DTSC::PAUSEMARK){ - HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers - HTTP_S.SetHeader("Content-Type", "application/json"); //Send the correct content-type for FLV files - jsondata << "]"; - HTTP_S.SetBody(jsondata.str()); - conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file - inited = false; - jsondata.str(""); // totally do this - jsondata.clear(); - break; - } - - if (jsondata.str().length() > 1){ - jsondata << ","; - } - - jsondata << Strm.getPacket().toString(); - } - }else{ - Util::sleep(1); - } - if ( !ss.connected()){ - break; - } - } - - } - conn.close(); - ss.SendNow(conn.getStats("HTTP_JSON").c_str()); - ss.close(); - return 0; - } //SRT main function - -} //Connector_HTTP namespace - -///\brief The standard process-spawning main function. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables HTTP protocol JSON streaming."; - capa["deps"] = "HTTP"; - capa["url_rel"] = "/$.json"; - capa["url_match"] = "/$.json"; - capa["url_handler"] = "http"; - capa["url_type"] = "json"; - capa["socket"] = "http_json"; - conf.addBasicConnectorOptions(capa); - conf.parseArgs(argc, argv); - - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - - return conf.serveForkedSocket(Connector_HTTP::JSONConnector); -} //main diff --git a/src/connectors/conn_http_live.cpp b/src/connectors/conn_http_live.cpp deleted file mode 100644 index a0956c9f..00000000 --- a/src/connectors/conn_http_live.cpp +++ /dev/null @@ -1,354 +0,0 @@ -/// \file conn_http_dynamic.cpp -/// Contains the main code for the HTTP Dynamic Connector - -#include <iostream> -#include <iomanip> -#include <sstream> -#include <queue> -#include <cstdlib> -#include <cstdio> -#include <cmath> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <getopt.h> -#include <mist/socket.h> -#include <mist/http_parser.h> -#include <mist/json.h> -#include <mist/dtsc.h> -#include <mist/mp4.h> -#include <mist/mp4_generic.h> -#include <mist/config.h> -#include <sstream> -#include <mist/stream.h> -#include <mist/timing.h> -#include <mist/ts_packet.h> - -/// Holds everything unique to HTTP Connectors. -namespace Connector_HTTP { - ///\brief Builds an index file for HTTP Live streaming. - ///\param metadata The current metadata, used to generate the index. - ///\param isLive Whether or not the stream is live. - ///\return The index file for HTTP Live Streaming. - std::string liveIndex(DTSC::Meta & metadata, bool isLive){ - std::stringstream result; - result << "#EXTM3U\r\n"; - int audioId = -1; - std::string audioName; - for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ - if (it->second.codec == "AAC"){ - audioId = it->first; - audioName = it->second.getIdentifier(); - break; - } - } - for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ - if (it->second.codec == "H264"){ - int bWidth = it->second.bps * 2; - if (audioId != -1){ - bWidth += metadata.tracks[audioId].bps * 2; - } - result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << bWidth * 10 << "\r\n"; - result << it->first; - if (audioId != -1){ - result << "_" << audioId; - } - result << "/index.m3u8\r\n"; - } - } -#if DEBUG >= 8 - std::cerr << "Sending this index:" << std::endl << result.str() << std::endl; -#endif - return result.str(); - } - - std::string liveIndex(DTSC::Track & metadata, bool isLive){ - std::stringstream result; - //parse single track - int longestFragment = 0; - for (std::deque<DTSC::Fragment>::iterator it = metadata.fragments.begin(); (it + 1) != metadata.fragments.end(); it++){ - if (it->getDuration() > longestFragment){ - longestFragment = it->getDuration(); - } - } - result << "#EXTM3U\r\n" - "#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n" - "#EXT-X-MEDIA-SEQUENCE:" << metadata.missedFrags << "\r\n"; - for (std::deque<DTSC::Fragment>::iterator it = metadata.fragments.begin(); it != metadata.fragments.end(); it++){ - long long int starttime = metadata.getKey(it->getNumber()).getTime(); - - if (it != (metadata.fragments.end() - 1)){ - result << "#EXTINF:" << ((it->getDuration() + 500) / 1000) << ", no desc\r\n" << starttime << "_" << it->getDuration() + starttime << ".ts\r\n"; - } - } - if ( !isLive){ - result << "#EXT-X-ENDLIST\r\n"; - } -#if DEBUG >= 8 - std::cerr << "Sending this index:" << std::endl << result.str() << std::endl; -#endif - return result.str(); - } //liveIndex - - ///\brief Main function for the HTTP Live Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int liveConnector(Socket::Connection & conn){ - DTSC::Stream Strm; //Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S; //HTTP Receiver en HTTP Sender. - - bool ready4data = false; //Set to true when streaming is to begin. - bool AppleCompat = false; //Set to true when Apple device detected. - Socket::Connection ss( -1); - std::string streamname; - bool handlingRequest = false; - std::string recBuffer = ""; - - TS::Packet PackData; - int PacketNumber = 0; - long long unsigned int TimeStamp = 0; - unsigned int ThisNaluSize; - char VideoCounter = 0; - char AudioCounter = 0; - long long unsigned int lastVid = 0; - bool IsKeyFrame = false; - MP4::AVCC avccbox; - bool haveAvcc = false; - - std::vector<int> fragIndices; - - std::string manifestType; - - int Segment = -1; - int temp; - int trackID = 0; - int audioTrackID = 0; - unsigned int lastStats = 0; - conn.setBlocking(false); //do not block on conn.spool() when no data is available - - while (conn.connected()){ - if ( !handlingRequest){ - if (conn.spool() && HTTP_R.Read(conn)){ - #if DEBUG >= 5 - std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; - #endif - conn.setHost(HTTP_R.GetHeader("X-Origin")); - AppleCompat = (HTTP_R.GetHeader("User-Agent").find("Apple") != std::string::npos); - streamname = HTTP_R.GetHeader("X-Stream"); - if ( !ss){ - ss = Util::Stream::getStream(streamname); - if ( !ss.connected()){ - #if DEBUG >= 1 - fprintf(stderr, "Could not connect to server!\n"); - #endif - HTTP_S.Clean(); - HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); - conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); - ready4data = false; - continue; - } - ss.setBlocking(false); - Strm.waitForMeta(ss); - } - if (HTTP_R.url.find(".m3u") == std::string::npos){ - temp = HTTP_R.url.find("/", 5) + 1; - std::string allTracks = HTTP_R.url.substr(temp, HTTP_R.url.find("/", temp) - temp); - trackID = atoi(allTracks.c_str()); - audioTrackID = atoi(allTracks.substr(allTracks.find("_")+1).c_str()); - temp = HTTP_R.url.find("/", temp) + 1; - Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find("_", temp) - temp).c_str()); - lastVid = Segment * 90; - temp = HTTP_R.url.find("_", temp) + 1; - int frameCount = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find(".ts", temp) - temp).c_str()); - if (Strm.metadata.live){ - int seekable = Strm.canSeekms(Segment); - if (seekable < 0){ - HTTP_S.Clean(); - HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); - conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range")); - HTTP_R.Clean(); //clean for any possible next requests - std::cout << "Fragment @ " << Segment << " too old" << std::endl; - continue; - } - if (seekable > 0){ - HTTP_S.Clean(); - HTTP_S.SetBody("Proxy, re-request this in a second or two.\n"); - conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later")); - HTTP_R.Clean(); //clean for any possible next requests - std::cout << "Fragment @ " << Segment << " not available yet" << std::endl; - continue; - } - } - for (unsigned int i = 0; i < allTracks.size(); i++){ - if (allTracks[i] == '_'){ - allTracks[i] = ' '; - } - } - std::stringstream sstream; - sstream << "t " << allTracks << "\n"; - sstream << "s " << Segment << "\n"; - sstream << "p " << frameCount << "\n"; - ss.SendNow(sstream.str().c_str()); - - HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type", "video/mp2t"); - HTTP_S.StartResponse(HTTP_R, conn); - handlingRequest = true; - }else{ - std::string request = HTTP_R.url.substr(HTTP_R.url.find("/", 5) + 1); - if (HTTP_R.url.find(".m3u8") != std::string::npos){ - manifestType = "audio/x-mpegurl"; - }else{ - manifestType = "audio/mpegurl"; - } - HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type", manifestType); - HTTP_S.SetHeader("Cache-Control", "no-cache"); - std::string manifest; - if (request.find("/") == std::string::npos){ - manifest = liveIndex(Strm.metadata, Strm.metadata.live); - }else{ - int selectId = atoi(request.substr(0,request.find("/")).c_str()); - manifest = liveIndex(Strm.metadata.tracks[selectId], Strm.metadata.live); - } - HTTP_S.SetBody(manifest); - conn.SendNow(HTTP_S.BuildResponse("200", "OK")); - } - ready4data = true; - HTTP_R.Clean(); //clean for any possible next requests - }else{ - Util::sleep(250); - } - } - if (ready4data){ - unsigned int now = Util::epoch(); - if (now != lastStats){ - lastStats = now; - ss.SendNow(conn.getStats("HTTP_Live").c_str()); - } - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - if (Strm.lastType() == DTSC::PAUSEMARK){ - HTTP_S.Chunkify("", 0, conn); - handlingRequest = false; - } - if ( !haveAvcc){ - avccbox.setPayload(Strm.metadata.tracks[trackID].init); - haveAvcc = true; - } - if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){ - Socket::Buffer ToPack; - //write PAT and PMT TS packets - if (PacketNumber % 42 == 0){ - PackData.DefaultPAT(); - HTTP_S.Chunkify(PackData.ToString(), 188, conn); - PackData.DefaultPMT(); - HTTP_S.Chunkify(PackData.ToString(), 188, conn); - PacketNumber += 2; - } - - int PIDno = 0; - char * ContCounter = 0; - if (Strm.lastType() == DTSC::VIDEO){ - IsKeyFrame = Strm.getPacket().isMember("keyframe"); - if (IsKeyFrame){ - TimeStamp = (Strm.getPacket()["time"].asInt() * 27000); - } - ToPack.append(avccbox.asAnnexB()); - while (Strm.lastData().size() > 4){ - ThisNaluSize = (Strm.lastData()[0] << 24) + (Strm.lastData()[1] << 16) + (Strm.lastData()[2] << 8) + Strm.lastData()[3]; - Strm.lastData().replace(0, 4, "\000\000\000\001", 4); - if (ThisNaluSize + 4 == Strm.lastData().size()){ - ToPack.append(Strm.lastData()); - break; - }else{ - ToPack.append(Strm.lastData().c_str(), ThisNaluSize + 4); - Strm.lastData().erase(0, ThisNaluSize + 4); - } - } - ToPack.prepend(TS::Packet::getPESVideoLeadIn(0ul, Strm.getPacket()["time"].asInt() * 90)); - PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt(); - ContCounter = &VideoCounter; - }else if (Strm.lastType() == DTSC::AUDIO){ - ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.metadata.tracks[audioTrackID].init)); - ToPack.append(Strm.lastData()); - if (AppleCompat){ - ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), lastVid)); - }else{ - ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket()["time"].asInt() * 90)); - } - PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt(); - ContCounter = &AudioCounter; - IsKeyFrame = false; - } - - //initial packet - PackData.Clear(); - PackData.PID(PIDno); - PackData.ContinuityCounter(( *ContCounter)++); - PackData.UnitStart(1); - if (IsKeyFrame){ - PackData.RandomAccess(1); - PackData.PCR(TimeStamp); - } - unsigned int toSend = PackData.AddStuffing(ToPack.bytes(184)); - std::string gonnaSend = ToPack.remove(toSend); - PackData.FillFree(gonnaSend); - HTTP_S.Chunkify(PackData.ToString(), 188, conn); - PacketNumber++; - - //rest of packets - while (ToPack.size()){ - PackData.Clear(); - PackData.PID(PIDno); - PackData.ContinuityCounter(( *ContCounter)++); - toSend = PackData.AddStuffing(ToPack.bytes(184)); - gonnaSend = ToPack.remove(toSend); - PackData.FillFree(gonnaSend); - HTTP_S.Chunkify(PackData.ToString(), 188, conn); - PacketNumber++; - } - - } - } - } - if ( !ss.connected()){ - break; - } - } - } - conn.close(); - ss.SendNow(conn.getStats("HTTP_Live").c_str()); - ss.close(); -#if DEBUG >= 5 - fprintf(stderr, "HLS: User %i disconnected.\n", conn.getSocket()); -#endif - return 0; - } //HLS_Connector main function - -} //Connector_HTTP namespace - -///\brief The standard process-spawning main function. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables HTTP protocol Apple-specific streaming (also known as HLS)."; - capa["deps"] = "HTTP"; - capa["url_rel"] = "/hls/$/index.m3u8"; - capa["url_prefix"] = "/hls/$/"; - capa["socket"] = "http_live"; - capa["codecs"][0u][0u].append("H264"); - capa["codecs"][0u][1u].append("AAC"); - capa["methods"][0u]["handler"] = "http"; - capa["methods"][0u]["type"] = "html5/application/vnd.apple.mpegurl"; - capa["methods"][0u]["priority"] = 9ll; - conf.addBasicConnectorOptions(capa); - conf.parseArgs(argc, argv); - - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - - return conf.serveForkedSocket(Connector_HTTP::liveConnector); -} //main diff --git a/src/connectors/conn_http_progressive_flv.cpp b/src/connectors/conn_http_progressive_flv.cpp deleted file mode 100644 index 9ec8ff23..00000000 --- a/src/connectors/conn_http_progressive_flv.cpp +++ /dev/null @@ -1,217 +0,0 @@ -///\file conn_http_progressive_flv.cpp -///\brief Contains the main code for the HTTP Progressive FLV Connector - -#include <iostream> -#include <queue> -#include <sstream> - -#include <cstdlib> -#include <cstdio> -#include <cmath> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include <mist/socket.h> -#include <mist/http_parser.h> -#include <mist/dtsc.h> -#include <mist/flv_tag.h> -#include <mist/amf.h> -#include <mist/config.h> -#include <mist/stream.h> -#include <mist/timing.h> - -///\brief Holds everything unique to HTTP Connectors. -namespace Connector_HTTP { - ///\brief Main function for the HTTP Progressive Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int progressiveConnector(Socket::Connection & conn){ - bool progressive_has_sent_header = false;//Indicates whether we have sent a header. - bool ready4data = false; //Set to true when streaming is to begin. - DTSC::Stream Strm; //Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender. - bool inited = false;//Whether the stream is initialized - Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream. - std::string streamname;//Will contain the name of the stream. - FLV::Tag tag;//Temporary tag buffer. - - unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket. - unsigned int seek_sec = 0;//Seek position in ms - unsigned int seek_byte = 0;//Seek position in bytes - - int videoID = -1; - int audioID = -1; - - while (conn.connected()){ - //Only attempt to parse input when not yet init'ed. - if ( !inited){ - if (conn.spool() && HTTP_R.Read(conn)){ -#if DEBUG >= 5 - std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; -#endif - conn.setHost(HTTP_R.GetHeader("X-Origin")); - streamname = HTTP_R.GetHeader("X-Stream"); - int start = 0; - if ( !HTTP_R.GetVar("start").empty()){ - start = atoi(HTTP_R.GetVar("start").c_str()); - } - if ( !HTTP_R.GetVar("starttime").empty()){ - start = atoi(HTTP_R.GetVar("starttime").c_str()); - } - if ( !HTTP_R.GetVar("apstart").empty()){ - start = atoi(HTTP_R.GetVar("apstart").c_str()); - } - if ( !HTTP_R.GetVar("ec_seek").empty()){ - start = atoi(HTTP_R.GetVar("ec_seek").c_str()); - } - if ( !HTTP_R.GetVar("fs").empty()){ - start = atoi(HTTP_R.GetVar("fs").c_str()); - } - //under 3 hours we assume seconds, otherwise byte position - if (start < 10800){ - seek_sec = start * 1000; //ms, not s - seek_byte = 0; - }else{ - seek_byte = start; //divide by 1mbit, then *1000 for ms. - seek_sec = 0; - } - ready4data = true; - HTTP_R.Clean(); //clean for any possible next requests - } - } - if (ready4data){ - if ( !inited){ - //we are ready, connect the socket! - ss = Util::Stream::getStream(streamname); - if ( !ss.connected()){ -#if DEBUG >= 1 - fprintf(stderr, "Could not connect to server for %s!\n", streamname.c_str()); -#endif - ss.close(); - HTTP_S.Clean(); - HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); - conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); - ready4data = false; - continue; - } - Strm.waitForMeta(ss); - int byterate = 0; - for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){ - if (videoID == -1 && (it->second.codec == "H264" || it->second.codec == "H263" || it->second.codec == "VP6")){ - videoID = it->second.trackID; - } - if (audioID == -1 && (it->second.codec == "AAC" || it->second.codec == "MP3")){ - audioID = it->second.trackID; - } - } - if (videoID != -1){ - byterate += Strm.metadata.tracks[videoID].bps; - } - if (audioID != -1){ - byterate += Strm.metadata.tracks[audioID].bps; - } - if ( !byterate){byterate = 1;} - if (seek_byte){ - seek_sec = (seek_byte / byterate) * 1000; - } - std::stringstream cmd; - cmd << "t"; - if (videoID != -1){ - cmd << " " << videoID; - } - if (audioID != -1){ - cmd << " " << audioID; - } - cmd << "\ns " << seek_sec << "\np\n"; - ss.SendNow(cmd.str().c_str(), cmd.str().size()); - inited = true; - } - unsigned int now = Util::epoch(); - if (now != lastStats){ - lastStats = now; - ss.SendNow(conn.getStats("HTTP_Progressive_FLV")); - } - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - if ( !progressive_has_sent_header){ - HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers - HTTP_S.SetHeader("Content-Type", "video/x-flv"); //Send the correct content-type for FLV files - //HTTP_S.SetHeader("Transfer-Encoding", "chunked"); - HTTP_S.protocol = "HTTP/1.0"; - conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file - conn.SendNow(FLV::Header, 13); //write FLV header - //write metadata - tag.DTSCMetaInit(Strm, Strm.metadata.tracks[videoID], Strm.metadata.tracks[audioID]); - conn.SendNow(tag.data, tag.len); - //write video init data, if needed - if (videoID != -1){ - tag.DTSCVideoInit(Strm.metadata.tracks[videoID]); - conn.SendNow(tag.data, tag.len); - } - //write audio init data, if needed - if (audioID != -1){ - tag.DTSCAudioInit(Strm.metadata.tracks[audioID]); - conn.SendNow(tag.data, tag.len); - } - progressive_has_sent_header = true; - } - if (Strm.lastType() == DTSC::PAUSEMARK){ - conn.close(); - } - if (Strm.lastType() == DTSC::INVALID){ - #if DEBUG >= 3 - fprintf(stderr, "Invalid packet received - closing connection.\n"); - #endif - conn.close(); - } - if (Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){ - std::string codec = Strm.metadata.tracks[Strm.getPacket()["trackid"].asInt()].codec; - if (codec == "AAC" || codec == "MP3" || codec == "H264" || codec == "H263" || codec == "VP6"){ - tag.DTSCLoader(Strm); - conn.SendNow(tag.data, tag.len); //write the tag contents - } - } - } - }else{ - Util::sleep(1); - } - if ( !ss.connected()){ - break; - } - } - } - conn.close(); - ss.SendNow(conn.getStats("HTTP_Progressive_FLV").c_str()); - ss.close(); - return 0; - } //Progressive_Connector main function - -} //Connector_HTTP namespace - -///\brief The standard process-spawning main function. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables HTTP protocol progressive streaming."; - capa["deps"] = "HTTP"; - capa["url_rel"] = "/$.flv"; - capa["url_match"] = "/$.flv"; - capa["socket"] = "http_progressive_flv"; - capa["codecs"][0u][0u].append("H264"); - capa["codecs"][0u][0u].append("H263"); - capa["codecs"][0u][0u].append("VP6"); - capa["codecs"][0u][1u].append("AAC"); - capa["codecs"][0u][1u].append("MP3"); - capa["methods"][0u]["handler"] = "http"; - capa["methods"][0u]["type"] = "flash/7"; - capa["methods"][0u]["priority"] = 5ll; - conf.addBasicConnectorOptions(capa); - conf.parseArgs(argc, argv); - - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - return conf.serveForkedSocket(Connector_HTTP::progressiveConnector); -} //main diff --git a/src/connectors/conn_http_progressive_mp3.cpp b/src/connectors/conn_http_progressive_mp3.cpp deleted file mode 100644 index 21a335f6..00000000 --- a/src/connectors/conn_http_progressive_mp3.cpp +++ /dev/null @@ -1,184 +0,0 @@ -///\file conn_http_progressive_mp3.cpp -///\brief Contains the main code for the HTTP Progressive MP3 Connector - -#include <iostream> -#include <queue> -#include <sstream> - -#include <cstdlib> -#include <cstdio> -#include <cmath> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include <mist/socket.h> -#include <mist/http_parser.h> -#include <mist/dtsc.h> -#include <mist/flv_tag.h> -#include <mist/amf.h> -#include <mist/config.h> -#include <mist/stream.h> -#include <mist/timing.h> - -///\brief Holds everything unique to HTTP Connectors. -namespace Connector_HTTP { - ///\brief Main function for the HTTP Progressive Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int progressiveConnector(Socket::Connection & conn){ - bool progressive_has_sent_header = false;//Indicates whether we have sent a header. - bool ready4data = false; //Set to true when streaming is to begin. - DTSC::Stream Strm; //Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender. - bool inited = false;//Whether the stream is initialized - Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream. - std::string streamname;//Will contain the name of the stream. - FLV::Tag tag;//Temporary tag buffer. - - unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket. - unsigned int seek_sec = 0;//Seek position in ms - unsigned int seek_byte = 0;//Seek position in bytes - - int audioID = -1; - - while (conn.connected()){ - //Only attempt to parse input when not yet init'ed. - if ( !inited){ - if (conn.spool() && HTTP_R.Read(conn)){ -#if DEBUG >= 5 - std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; -#endif - conn.setHost(HTTP_R.GetHeader("X-Origin")); - streamname = HTTP_R.GetHeader("X-Stream"); - int start = 0; - if ( !HTTP_R.GetVar("start").empty()){ - start = atoi(HTTP_R.GetVar("start").c_str()); - } - if ( !HTTP_R.GetVar("starttime").empty()){ - start = atoi(HTTP_R.GetVar("starttime").c_str()); - } - if ( !HTTP_R.GetVar("apstart").empty()){ - start = atoi(HTTP_R.GetVar("apstart").c_str()); - } - if ( !HTTP_R.GetVar("ec_seek").empty()){ - start = atoi(HTTP_R.GetVar("ec_seek").c_str()); - } - if ( !HTTP_R.GetVar("fs").empty()){ - start = atoi(HTTP_R.GetVar("fs").c_str()); - } - //under 3 hours we assume seconds, otherwise byte position - if (start < 10800){ - seek_sec = start * 1000; //ms, not s - }else{ - seek_byte = start; //divide by 1mbit, then *1000 for ms. - } - ready4data = true; - HTTP_R.Clean(); //clean for any possible next requests - } - } - if (ready4data){ - if ( !inited){ - //we are ready, connect the socket! - ss = Util::Stream::getStream(streamname); - if ( !ss.connected()){ -#if DEBUG >= 1 - fprintf(stderr, "Could not connect to server for %s!\n", streamname.c_str()); -#endif - ss.close(); - HTTP_S.Clean(); - HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); - conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); - ready4data = false; - continue; - } - Strm.waitForMeta(ss); - int byterate = 0; - for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){ - if (audioID == -1 && it->second.codec == "MP3"){ - audioID = it->second.trackID; - } - } - if (audioID != -1){ - byterate += Strm.metadata.tracks[audioID].bps; - } - if ( !byterate){byterate = 1;} - if (seek_byte){ - seek_sec = (seek_byte / byterate) * 1000; - } - std::stringstream cmd; - cmd << "t"; - if (audioID != -1){ - cmd << " " << audioID; - } - cmd << "\ns " << seek_sec << "\np\n"; - ss.SendNow(cmd.str().c_str(), cmd.str().size()); - inited = true; - } - unsigned int now = Util::epoch(); - if (now != lastStats){ - lastStats = now; - ss.SendNow(conn.getStats("HTTP_Progressive").c_str()); - } - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - if ( !progressive_has_sent_header){ - HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers - HTTP_S.SetHeader("Content-Type", "audio/mpeg"); //Send the correct content-type for MP3 files - //HTTP_S.SetHeader("Transfer-Encoding", "chunked"); - HTTP_S.protocol = "HTTP/1.0"; - conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file - progressive_has_sent_header = true; - } - if (Strm.lastType() == DTSC::PAUSEMARK){ - conn.close(); - } - if (Strm.lastType() == DTSC::INVALID){ - #if DEBUG >= 3 - fprintf(stderr, "Invalid packet received - closing connection.\n"); - #endif - conn.close(); - } - if (Strm.lastType() == DTSC::AUDIO){ - conn.SendNow(Strm.lastData()); //write the MP3 contents - } - } - }else{ - Util::sleep(1); - } - if ( !ss.connected()){ - break; - } - } - } - conn.close(); - ss.SendNow(conn.getStats("HTTP_Dynamic").c_str()); - ss.close(); - return 0; - } //Progressive_Connector main function - -} //Connector_HTTP namespace - -///\brief The standard process-spawning main function. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables HTTP protocol progressive streaming."; - capa["deps"] = "HTTP"; - capa["codecs"][0u][0u].append("MP3"); - capa["url_rel"] = "/$.mp3"; - capa["url_match"] = "/$.mp3"; - capa["socket"] = "http_progressive_mp3"; - capa["methods"][0u]["handler"] = "http"; - capa["methods"][0u]["type"] = "mp3"; - capa["methods"][0u]["priority"] = 8ll; - conf.addBasicConnectorOptions(capa); - conf.parseArgs(argc, argv); - - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - - return conf.serveForkedSocket(Connector_HTTP::progressiveConnector); -} //main diff --git a/src/connectors/conn_http_progressive_mp4.cpp b/src/connectors/conn_http_progressive_mp4.cpp deleted file mode 100644 index 5e8dbf79..00000000 --- a/src/connectors/conn_http_progressive_mp4.cpp +++ /dev/null @@ -1,656 +0,0 @@ -///\file conn_http_progressive_mp4.cpp -///\brief Contains the main code for the HTTP Progressive MP4 Connector - -#include <iostream> -#include <queue> -#include <sstream> - -#include <cstdlib> -#include <cstdio> -#include <cmath> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include <mist/socket.h> -#include <mist/http_parser.h> -#include <mist/dtsc.h> -#include <mist/mp4.h> -#include <mist/mp4_generic.h> -#include <mist/amf.h> -#include <mist/config.h> -#include <mist/stream.h> -#include <mist/timing.h> -#include <mist/defines.h> - -///\brief Holds everything unique to HTTP Connectors. -namespace Connector_HTTP { - - struct keyPart{ - public: - bool operator < (const keyPart& rhs) const { - if (time < rhs.time){ - return true; - } - if (time == rhs.time){ - if (trackID < rhs.trackID){ - return true; - } - } - return false; - } - long unsigned int trackID; - long unsigned int size; - long long unsigned int time; - long long unsigned int endTime; - long unsigned int index; - }; - - std::string DTSCMeta2MP4Header(DTSC::Meta & metaData, std::set<int> & tracks, long long & size){ - std::stringstream header; - //ftyp box - MP4::FTYP ftypBox; - header << std::string(ftypBox.asBox(),ftypBox.boxedSize()); - - uint64_t mdatSize = 0; - //moov box - MP4::MOOV moovBox; - unsigned int moovOffset = 0; - { - //calculating longest duration - long long int firstms = -1; - long long int lastms = -1; - for (std::set<int>::iterator it = tracks.begin(); it != tracks.end(); it++) { - if (lastms == -1 || lastms < metaData.tracks[*it].lastms){ - lastms = metaData.tracks[*it].lastms; - } - if (firstms == -1 || firstms > metaData.tracks[*it].firstms){ - firstms = metaData.tracks[*it].firstms; - } - } - MP4::MVHD mvhdBox(lastms - firstms); - moovBox.setContent(mvhdBox, moovOffset++); - } - for (std::set<int>::iterator it = tracks.begin(); it != tracks.end(); it++) { - MP4::TRAK trakBox; - { - { - MP4::TKHD tkhdBox(*it, metaData.tracks[*it].lastms - metaData.tracks[*it].firstms, metaData.tracks[*it].width, metaData.tracks[*it].height); - trakBox.setContent(tkhdBox, 0); - }{ - MP4::MDIA mdiaBox; - unsigned int mdiaOffset = 0; - { - MP4::MDHD mdhdBox(metaData.tracks[*it].lastms - metaData.tracks[*it].firstms); - mdiaBox.setContent(mdhdBox, mdiaOffset++); - }//MDHD box - { - MP4::HDLR hdlrBox(metaData.tracks[*it].type, metaData.tracks[*it].getIdentifier()); - mdiaBox.setContent(hdlrBox, mdiaOffset++); - }//hdlr box - { - MP4::MINF minfBox; - unsigned int minfOffset = 0; - if (metaData.tracks[*it].type== "video"){ - MP4::VMHD vmhdBox; - vmhdBox.setFlags(1); - minfBox.setContent(vmhdBox,minfOffset++); - }else if (metaData.tracks[*it].type == "audio"){ - MP4::SMHD smhdBox; - minfBox.setContent(smhdBox,minfOffset++); - }//type box - { - MP4::DINF dinfBox; - MP4::DREF drefBox; - dinfBox.setContent(drefBox,0); - minfBox.setContent(dinfBox,minfOffset++); - }//dinf box - { - MP4::STBL stblBox; - unsigned int offset = 0; - { - MP4::STSD stsdBox; - stsdBox.setVersion(0); - if (metaData.tracks[*it].type == "video"){//boxname = codec - MP4::VisualSampleEntry vse; - if (metaData.tracks[*it].codec == "H264"){ - vse.setCodec("avc1"); - } - vse.setDataReferenceIndex(1); - vse.setWidth(metaData.tracks[*it].width); - vse.setHeight(metaData.tracks[*it].height); - MP4::AVCC avccBox; - avccBox.setPayload(metaData.tracks[*it].init); - vse.setCLAP(avccBox); - stsdBox.setEntry(vse,0); - }else if(metaData.tracks[*it].type == "audio"){//boxname = codec - MP4::AudioSampleEntry ase; - if (metaData.tracks[*it].codec == "AAC"){ - ase.setCodec("mp4a"); - ase.setDataReferenceIndex(1); - } - ase.setSampleRate(metaData.tracks[*it].rate); - ase.setChannelCount(metaData.tracks[*it].channels); - ase.setSampleSize(metaData.tracks[*it].size); - //MP4::ESDS esdsBox(metaData.tracks[*it].init, metaData.tracks[*it].bps); - MP4::ESDS esdsBox; - - //outputting these values first, so malloc isn't called as often. - esdsBox.setESHeaderStartCodes(metaData.tracks[*it].init); - esdsBox.setSLValue(2); - - esdsBox.setESDescriptorTypeLength(32+metaData.tracks[*it].init.size()); - esdsBox.setESID(2); - esdsBox.setStreamPriority(0); - esdsBox.setDecoderConfigDescriptorTypeLength(18 + metaData.tracks[*it].init.size()); - esdsBox.setByteObjectTypeID(0x40); - esdsBox.setStreamType(5); - esdsBox.setReservedFlag(1); - esdsBox.setBufferSize(1250000); - esdsBox.setMaximumBitRate(10000000); - esdsBox.setAverageBitRate(metaData.tracks[*it].bps * 8); - esdsBox.setConfigDescriptorTypeLength(5); - esdsBox.setSLConfigDescriptorTypeTag(0x6); - esdsBox.setSLConfigExtendedDescriptorTypeTag(0x808080); - esdsBox.setSLDescriptorTypeLength(1); - ase.setCodecBox(esdsBox); - stsdBox.setEntry(ase,0); - } - stblBox.setContent(stsdBox,offset++); - }//stsd box - { - MP4::STTS sttsBox; - sttsBox.setVersion(0); - if (metaData.tracks[*it].parts.size()){ - for (unsigned int part = 0; part < metaData.tracks[*it].parts.size(); part++){ - MP4::STTSEntry newEntry; - newEntry.sampleCount = 1; - newEntry.sampleDelta = metaData.tracks[*it].parts[part].getDuration(); - sttsBox.setSTTSEntry(newEntry, part); - } - } - stblBox.setContent(sttsBox,offset++); - }//stts box - if (metaData.tracks[*it].type == "video"){ - //STSS Box here - MP4::STSS stssBox; - stssBox.setVersion(0); - int tmpCount = 1; - int tmpItCount = 0; - for ( std::deque< DTSC::Key>::iterator tmpIt = metaData.tracks[*it].keys.begin(); tmpIt != metaData.tracks[*it].keys.end(); tmpIt ++) { - stssBox.setSampleNumber(tmpCount,tmpItCount); - tmpCount += tmpIt->getParts(); - tmpItCount ++; - } - stblBox.setContent(stssBox,offset++); - }//stss box - { - MP4::STSC stscBox; - stscBox.setVersion(0); - MP4::STSCEntry stscEntry; - stscEntry.firstChunk = 1; - stscEntry.samplesPerChunk = 1; - stscEntry.sampleDescriptionIndex = 1; - stscBox.setSTSCEntry(stscEntry, 0); - stblBox.setContent(stscBox,offset++); - }//stsc box - { - uint32_t total = 0; - MP4::STSZ stszBox; - stszBox.setVersion(0); - total = 0; - for (std::deque< DTSC::Part>::iterator partIt = metaData.tracks[*it].parts.begin(); partIt != metaData.tracks[*it].parts.end(); partIt ++) { - stszBox.setEntrySize(partIt->getSize(), total);//in bytes in file - size += partIt->getSize(); - total++; - } - stblBox.setContent(stszBox,offset++); - }//stsz box - //add STCO boxes here - { - MP4::STCO stcoBox; - stcoBox.setVersion(1); - //Inserting empty values on purpose here, will be fixed later. - if (metaData.tracks[*it].parts.size() != 0){ - stcoBox.setChunkOffset(0, metaData.tracks[*it].parts.size() - 1);//this inserts all empty entries at once - } - stblBox.setContent(stcoBox,offset++); - }//stco box - minfBox.setContent(stblBox,minfOffset++); - }//stbl box - mdiaBox.setContent(minfBox, mdiaOffset++); - }//minf box - trakBox.setContent(mdiaBox, 1); - } - }//trak Box - moovBox.setContent(trakBox, moovOffset++); - } - //initial offset length ftyp, length moov + 8 - unsigned long long int byteOffset = ftypBox.boxedSize() + moovBox.boxedSize() + 8; - //update all STCO from the following map; - std::map <int, MP4::STCO> checkStcoBoxes; - //for all tracks - for (unsigned int i = 1; i < moovBox.getContentCount(); i++){ - //10 lines to get the STCO box. - MP4::TRAK checkTrakBox; - MP4::Box checkMdiaBox; - MP4::Box checkTkhdBox; - MP4::MINF checkMinfBox; - MP4::STBL checkStblBox; - //MP4::STCO checkStcoBox; - checkTrakBox = ((MP4::TRAK&)moovBox.getContent(i)); - for (unsigned int j = 0; j < checkTrakBox.getContentCount(); j++){ - if (checkTrakBox.getContent(j).isType("mdia")){ - checkMdiaBox = checkTrakBox.getContent(j); - break; - } - if (checkTrakBox.getContent(j).isType("tkhd")){ - checkTkhdBox = checkTrakBox.getContent(j); - } - } - for (unsigned int j = 0; j < ((MP4::MDIA&)checkMdiaBox).getContentCount(); j++){ - if (((MP4::MDIA&)checkMdiaBox).getContent(j).isType("minf")){ - checkMinfBox = ((MP4::MINF&)((MP4::MDIA&)checkMdiaBox).getContent(j)); - break; - } - } - for (unsigned int j = 0; j < checkMinfBox.getContentCount(); j++){ - if (checkMinfBox.getContent(j).isType("stbl")){ - checkStblBox = ((MP4::STBL&)checkMinfBox.getContent(j)); - break; - } - } - for (unsigned int j = 0; j < checkStblBox.getContentCount(); j++){ - if (checkStblBox.getContent(j).isType("stco")){ - checkStcoBoxes.insert( std::pair<int, MP4::STCO>(((MP4::TKHD&)checkTkhdBox).getTrackID(), ((MP4::STCO&)checkStblBox.getContent(j)) )); - break; - } - } - } - //inserting right values in the STCO box header - //total = 0; - long long unsigned int totalByteOffset = 0; - //Current values are actual byte offset without header-sized offset - std::set <keyPart> sortSet;//filling sortset for interleaving parts - for (std::set<int>::iterator subIt = tracks.begin(); subIt != tracks.end(); subIt++) { - keyPart temp; - temp.trackID = *subIt; - temp.time = metaData.tracks[*subIt].firstms;//timeplace of frame - temp.endTime = metaData.tracks[*subIt].firstms + metaData.tracks[*subIt].parts[0].getDuration(); - temp.size = metaData.tracks[*subIt].parts[0].getSize();//bytesize of frame (alle parts all together) - temp.index = 0; - sortSet.insert(temp); - } - while (!sortSet.empty()){ - //setting the right STCO size in the STCO box - checkStcoBoxes[sortSet.begin()->trackID].setChunkOffset(totalByteOffset + byteOffset, sortSet.begin()->index); - totalByteOffset += sortSet.begin()->size; - //add keyPart to sortSet - keyPart temp; - temp.index = sortSet.begin()->index + 1; - temp.trackID = sortSet.begin()->trackID; - if(temp.index < metaData.tracks[temp.trackID].parts.size() ){//only insert when there are parts left - temp.time = sortSet.begin()->endTime;//timeplace of frame - temp.endTime = sortSet.begin()->endTime + metaData.tracks[temp.trackID].parts[temp.index].getDuration(); - temp.size = metaData.tracks[temp.trackID].parts[temp.index].getSize();//bytesize of frame - sortSet.insert(temp); - } - //remove highest keyPart - sortSet.erase(sortSet.begin()); - } - - mdatSize = totalByteOffset+8; - - header << std::string(moovBox.asBox(),moovBox.boxedSize()); - - header << (char)((mdatSize>>24) & 0xFF) << (char)((mdatSize>>16) & 0xFF) << (char)((mdatSize>>8) & 0xFF) << (char)(mdatSize & 0xFF) << "mdat"; - //end of header - - size += header.str().size(); - return header.str(); - } - - /// Calculate a seekPoint, based on byteStart, metadata, tracks and headerSize. - /// The seekPoint will be set to the timestamp of the first packet to send. - void findSeekPoint(long long byteStart, long long & seekPoint, DTSC::Meta & metadata, std::set<int> & tracks, unsigned int headerSize){ - seekPoint = 0; - //if we're starting in the header, seekPoint is always zero. - if (byteStart <= headerSize){return;} - //okay, we're past the header. Substract the headersize from the starting postion. - byteStart -= headerSize; - //initialize a list of sorted parts that this file contains - std::set <keyPart> sortSet; - for (std::set<int>::iterator subIt = tracks.begin(); subIt != tracks.end(); subIt++) { - keyPart temp; - temp.trackID = *subIt; - temp.time = metadata.tracks[*subIt].firstms;//timeplace of frame - temp.endTime = metadata.tracks[*subIt].firstms + metadata.tracks[*subIt].parts[0].getDuration(); - temp.size = metadata.tracks[*subIt].parts[0].getSize();//bytesize of frame (alle parts all together) - temp.index = 0; - sortSet.insert(temp); - } - //forward through the file by headers, until we reach the point where we need to be - while (!sortSet.empty()){ - //substract the size of this fragment from byteStart - byteStart -= sortSet.begin()->size; - //if that put us past the point where we wanted to be, return right now - if (byteStart < 0){return;} - //otherwise, set seekPoint to where we are now - seekPoint = sortSet.begin()->time; - //then find the next part - keyPart temp; - temp.index = sortSet.begin()->index + 1; - temp.trackID = sortSet.begin()->trackID; - if(temp.index < metadata.tracks[temp.trackID].parts.size() ){//only insert when there are parts left - temp.time = sortSet.begin()->endTime;//timeplace of frame - temp.endTime = sortSet.begin()->endTime + metadata.tracks[temp.trackID].parts[temp.index].getDuration(); - temp.size = metadata.tracks[temp.trackID].parts[temp.index].getSize();//bytesize of frame - sortSet.insert(temp); - } - //remove highest keyPart - sortSet.erase(sortSet.begin()); - } - //If we're here, we're in the last fragment. - //That's technically legal, of course. - } - - /// Parses a "Range: " header, setting byteStart, byteEnd and seekPoint using data from metadata and tracks to do - /// the calculations. - /// On error, byteEnd is set to zero. - void parseRange(std::string header, long long & byteStart, long long & byteEnd, long long & seekPoint, DTSC::Meta & metadata, std::set<int> & tracks, unsigned int headerSize){ - if (header.size() < 6 || header.substr(0, 6) != "bytes="){ - byteEnd = 0; - DEBUG_MSG(DLVL_WARN, "Invalid range header: %s", header.c_str()); - return; - } - header.erase(0, 6); - if (header.size() && header[0] == '-'){ - //negative range = count from end - byteStart = 0; - for (unsigned int i = 1; i < header.size(); ++i){ - if (header[i] >= '0' && header[i] <= '9'){ - byteStart *= 10; - byteStart += header[i] - '0'; - continue; - } - break; - } - if (byteStart > byteEnd){ - //entire file if starting before byte zero - byteStart = 0; - DEBUG_MSG(DLVL_DEVEL, "Full negative range: %lli-%lli", byteStart, byteEnd); - findSeekPoint(byteStart, seekPoint, metadata, tracks, headerSize); - return; - }else{ - //start byteStart bytes before byteEnd - byteStart = byteEnd - byteStart; - DEBUG_MSG(DLVL_DEVEL, "Partial negative range: %lli-%lli", byteStart, byteEnd); - findSeekPoint(byteStart, seekPoint, metadata, tracks, headerSize); - return; - } - }else{ - long long size = byteEnd; - byteEnd = 0; - byteStart = 0; - unsigned int i = 0; - for ( ; i < header.size(); ++i){ - if (header[i] >= '0' && header[i] <= '9'){ - byteStart *= 10; - byteStart += header[i] - '0'; - continue; - } - break; - } - if (header[i] != '-'){ - DEBUG_MSG(DLVL_WARN, "Invalid range header: %s", header.c_str()); - byteEnd = 0; - return; - } - ++i; - if (i < header.size()){ - for ( ; i < header.size(); ++i){ - if (header[i] >= '0' && header[i] <= '9'){ - byteEnd *= 10; - byteEnd += header[i] - '0'; - continue; - } - break; - } - if (byteEnd > size-1){byteEnd = size;} - }else{ - byteEnd = size; - } - DEBUG_MSG(DLVL_DEVEL, "Range request: %lli-%lli (%s)", byteStart, byteEnd, header.c_str()); - findSeekPoint(byteStart, seekPoint, metadata, tracks, headerSize); - return; - } - }//parseRange - - ///\brief Main function for the HTTP Progressive Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int progressiveConnector(Socket::Connection & conn){ - DTSC::Stream Strm; //Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender. - long long byteStart = 0; - long long leftOver = 0; - long long currPos = 0; - bool inited = false;//Whether the stream is initialized - Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream. - std::string streamname;//Will contain the name of the stream. - std::set <keyPart> sortSet;//filling sortset for interleaving parts - - unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket. - - while (conn.connected()){ - //Only attempt to parse input when not yet init'ed. - if ( !inited){ - if (conn.spool() && HTTP_R.Read(conn)){ - DEBUG_MSG(DLVL_DEVEL, "Received request: %s", HTTP_R.getUrl().c_str()); - conn.setHost(HTTP_R.GetHeader("X-Origin")); - streamname = HTTP_R.GetHeader("X-Stream"); - if (!ss){ - ss = Util::Stream::getStream(streamname); - if (ss){ - Strm.waitForMeta(ss); - } - if (!ss){ - DEBUG_MSG(DLVL_FAIL, "Could not connect to stream %s!", streamname.c_str()); - ss.close(); - HTTP_S.Clean(); - HTTP_R.Clean(); - HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); - HTTP_S.SendResponse("404", "Not found", conn); - continue; - } - } - int videoID = -1; - int audioID = -1; - if (HTTP_R.GetVar("audio") != ""){ - audioID = JSON::Value(HTTP_R.GetVar("audio")).asInt(); - } - if (HTTP_R.GetVar("video") != ""){ - videoID = JSON::Value(HTTP_R.GetVar("video")).asInt(); - } - for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){ - if (videoID == -1 && it->second.type == "video" && it->second.codec == "H264"){ - videoID = it->first; - } - if (audioID == -1 && it->second.type == "audio" && it->second.codec == "AAC"){ - audioID = it->first; - } - } - - std::set<int> tracks; - if (videoID > 0){tracks.insert(videoID);} - if (audioID > 0){tracks.insert(audioID);} - - HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers - HTTP_S.SetHeader("Content-Type", "video/MP4"); //Send the correct content-type for MP4 files - HTTP_S.SetHeader("Accept-Ranges", "bytes, parsec"); - long long size = 0; - std::string headerData = DTSCMeta2MP4Header(Strm.metadata, tracks, size); - byteStart = 0; - long long byteEnd = size-1; - long long seekPoint = 0; - if (HTTP_R.GetHeader("Range") != ""){ - parseRange(HTTP_R.GetHeader("Range"), byteStart, byteEnd, seekPoint, Strm.metadata, tracks, headerData.size()); - if (!byteEnd){ - if (HTTP_R.GetHeader("Range")[0] == 'p'){ - HTTP_S.SetBody("Starsystem not in communications range"); - HTTP_S.SendResponse("416", "Starsystem not in communications range", conn); - HTTP_R.Clean(); //clean for any possible next requests - continue; - }else{ - HTTP_S.SetBody("Requested Range Not Satisfiable"); - HTTP_S.SendResponse("416", "Requested Range Not Satisfiable", conn); - HTTP_R.Clean(); //clean for any possible next requests - continue; - } - }else{ - std::stringstream rangeReply; - rangeReply << "bytes " << byteStart << "-" << byteEnd << "/" << size; - HTTP_S.SetHeader("Content-Length", byteEnd - byteStart + 1); - //do not multiplex requests that are > 1MiB - if (byteEnd - byteStart + 1 > 1024*1024){ - HTTP_S.SetHeader("MistMultiplex", "No"); - } - HTTP_S.SetHeader("Content-Range", rangeReply.str()); - /// \todo Switch to chunked? - HTTP_S.SendResponse("206", "Partial content", conn); - //HTTP_S.StartResponse("206", "Partial content", HTTP_R, conn); - } - }else{ - HTTP_S.SetHeader("Content-Length", byteEnd - byteStart + 1); - //do not multiplex requests that aren't ranged - HTTP_S.SetHeader("MistMultiplex", "No"); - /// \todo Switch to chunked? - HTTP_S.SendResponse("200", "OK", conn); - //HTTP_S.StartResponse(HTTP_R, conn); - } - leftOver = byteEnd - byteStart + 1;//add one byte, because range "0-0" = 1 byte of data - currPos = 0; - if (byteStart < (long long)headerData.size()){ - /// \todo Switch to chunked? - //HTTP_S.Chunkify(headerData.data()+byteStart, std::min((long long)headerData.size(), byteEnd) - byteStart, conn);//send MP4 header - conn.SendNow(headerData.data()+byteStart, std::min((long long)headerData.size(), byteEnd) - byteStart);//send MP4 header - leftOver -= std::min((long long)headerData.size(), byteEnd) - byteStart; - } - currPos = headerData.size();//we're now guaranteed to be past the header point, no matter what - HTTP_R.Clean(); //clean for any possible next requests - {//using scope to have cmd not declared after action - std::stringstream cmd; - cmd << "t"; - for (std::set<int>::iterator it = tracks.begin(); it != tracks.end(); it++) { - cmd << " " << *it; - } - cmd << "\ns " << seekPoint << "\np\n"; - ss.SendNow(cmd.str()); - } - sortSet.clear(); - for (std::set<int>::iterator subIt = tracks.begin(); subIt != tracks.end(); subIt++) { - keyPart temp; - temp.trackID = *subIt; - temp.time = Strm.metadata.tracks[*subIt].firstms;//timeplace of frame - temp.endTime = Strm.metadata.tracks[*subIt].firstms + Strm.metadata.tracks[*subIt].parts[0].getDuration(); - temp.size = Strm.metadata.tracks[*subIt].parts[0].getSize();//bytesize of frame (alle parts all together) - temp.index = 0; - sortSet.insert(temp); - } - inited = true; - } - }else{ - unsigned int now = Util::epoch(); - if (now != lastStats){ - lastStats = now; - ss.SendNow(conn.getStats("HTTP_Progressive_MP4").c_str()); - } - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - if (Strm.lastType() == DTSC::PAUSEMARK){ - conn.close(); - }else if(Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){ - //keep track of where we are - fast-forward until where we are now - while (!sortSet.empty() && ((long long)sortSet.begin()->trackID != Strm.getPacket()["trackid"].asInt() || (long long)sortSet.begin()->time != Strm.getPacket()["time"].asInt())){ - keyPart temp; - temp.index = sortSet.begin()->index + 1; - temp.trackID = sortSet.begin()->trackID; - if(temp.index < Strm.metadata.tracks[temp.trackID].parts.size() ){//only insert when there are parts left - temp.time = sortSet.begin()->endTime;//timeplace of frame - temp.endTime = sortSet.begin()->endTime + Strm.metadata.tracks[temp.trackID].parts[temp.index].getDuration(); - temp.size = Strm.metadata.tracks[temp.trackID].parts[temp.index].getSize();//bytesize of frame - sortSet.insert(temp); - } - currPos += sortSet.begin()->size; - //remove highest keyPart - sortSet.erase(sortSet.begin()); - } - if (currPos >= byteStart){ - sortSet.clear();//we don't need you anymore! - if (leftOver < (long long)Strm.lastData().size()){ - conn.SendNow(Strm.lastData().data(), leftOver); - }else{ - conn.SendNow(Strm.lastData()); - } - //HTTP_S.Chunkify(Strm.lastData().data(), Strm.lastData().size(), conn); - leftOver -= Strm.lastData().size(); - }else{ - if (currPos + (long long)Strm.lastData().size() > byteStart){ - conn.SendNow(Strm.lastData().data()+(byteStart-currPos), Strm.lastData().size()-(byteStart-currPos)); - leftOver -= Strm.lastData().size()-(byteStart-currPos); - currPos = byteStart; - sortSet.clear();//we don't need you anymore! - } - } - if (leftOver < 1){ - ss.SendNow("q\n");//stop playback - Strm.waitForPause(ss);//sync the stream - inited = false; - } - } - if (Strm.lastType() == DTSC::INVALID){ - DEBUG_MSG(DLVL_FAIL, "Invalid packet received - closing connection"); - conn.close(); - } - } - }else{ - Util::sleep(10); - } - if ( !ss.connected()){ - break; - } - } - } - conn.close(); - ss.SendNow(conn.getStats("HTTP_Progressive_MP4").c_str()); - ss.close(); - return 0; - } //Progressive_Connector main function - -} //Connector_HTTP namespace - -///\brief The standard process-spawning main function. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables HTTP protocol progressive streaming."; - capa["deps"] = "HTTP"; - capa["url_rel"] = "/$.mp4"; - capa["url_match"] = "/$.mp4"; - capa["codecs"][0u][0u].append("H264"); - capa["codecs"][0u][1u].append("AAC"); - capa["methods"][0u]["handler"] = "http"; - capa["methods"][0u]["type"] = "html5/video/mp4"; - capa["methods"][0u]["priority"] = 8ll; - capa["methods"][0u]["nolive"] = 1; - capa["socket"] = "http_progressive_mp4"; - conf.addBasicConnectorOptions(capa); - conf.parseArgs(argc, argv); - - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - - return conf.serveForkedSocket(Connector_HTTP::progressiveConnector); -} //main diff --git a/src/connectors/conn_http_progressive_ogg.cpp b/src/connectors/conn_http_progressive_ogg.cpp deleted file mode 100644 index 6f12d2c8..00000000 --- a/src/connectors/conn_http_progressive_ogg.cpp +++ /dev/null @@ -1,186 +0,0 @@ -///\file conn_http_progressive_ogg.cpp -///\brief Contains the main code for the HTTP Progressive OGG Connector - -#include <iostream> -#include <queue> -#include <sstream> - -#include <cstdlib> -#include <cstdio> -#include <cmath> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include <mist/socket.h> -#include <mist/http_parser.h> -#include <mist/dtsc.h> -#include <mist/ogg.h> -#include <mist/amf.h> -#include <mist/config.h> -#include <mist/stream.h> -#include <mist/timing.h> -#include "../converters/oggconv.h" - -///\brief Holds everything unique to HTTP Connectors. -namespace Connector_HTTP { - ///\brief Main function for the HTTP Progressive Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int progressiveConnector(Socket::Connection & conn){ - bool progressive_has_sent_header = false;//Indicates whether we have sent a header. - bool ready4data = false; //Set to true when streaming is to begin. - DTSC::Stream Strm; //Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender. - bool inited = false;//Whether the stream is initialized - Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream. - std::string streamname;//Will contain the name of the stream. - - //OGG specific variables - //OGG::headerPages oggMeta; - //OGG::Page curOggPage; - OGG::converter oggConv; - std::map <long long unsigned int, std::vector<JSON::Value> > DTSCBuffer; - //std::map <long long unsigned int, long long unsigned int> prevGran; - std::vector<unsigned int> curSegTable; - std::string sendBuffer; - - unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket. - - int videoID = -1; - int audioID = -1; - - while (conn.connected()){ - //Only attempt to parse input when not yet init'ed. - if ( !inited){ - if (conn.spool() && HTTP_R.Read(conn)){ -#if DEBUG >= 5 - std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; -#endif - conn.setHost(HTTP_R.GetHeader("X-Origin")); - streamname = HTTP_R.GetHeader("X-Stream"); - ready4data = true; - HTTP_R.Clean(); //clean for any possible next requests - } - } - if (ready4data){ - if ( !inited){ - //we are ready, connect the socket! - ss = Util::Stream::getStream(streamname); - if ( !ss.connected()){ -#if DEBUG >= 1 - fprintf(stderr, "Could not connect to server for %s!\n", streamname.c_str()); -#endif - ss.close(); - HTTP_S.Clean(); - HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); - conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); - ready4data = false; - continue; - } - Strm.waitForMeta(ss); - for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){ - if (videoID == -1 && it->second.codec == "theora"){ - videoID = it->second.trackID; - } - if (audioID == -1 && it->second.codec == "vorbis"){ - audioID = it->second.trackID; - } - } - if (videoID == -1 && audioID == -1){ - HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers - HTTP_S.SetBody("This stream contains no OGG compatible codecs"); - HTTP_S.SendResponse("406", "Not acceptable",conn); - HTTP_R.Clean(); - continue; - } - std::stringstream cmd; - cmd << "t"; - if (videoID != -1){ - cmd << " " << videoID; - } - if (audioID != -1){ - cmd << " " << audioID; - } - cmd << "\np\n"; - ss.SendNow(cmd.str().c_str(), cmd.str().size()); - inited = true; - } - unsigned int now = Util::epoch(); - if (now != lastStats){ - lastStats = now; - ss.SendNow(conn.getStats("HTTP_Progressive_Ogg").c_str()); - } - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - - if ( !progressive_has_sent_header){ - HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers - HTTP_S.SetHeader("Content-Type", "video/ogg"); //Send the correct content-type for FLV files - HTTP_S.protocol = "HTTP/1.0"; - conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file - //Fill in ogg header here - oggConv.readDTSCHeader(Strm.metadata); - conn.SendNow((char*)oggConv.parsedPages.c_str(), oggConv.parsedPages.size()); - progressive_has_sent_header = true; - } - //parse DTSC to Ogg here - if (Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){ - std::string tmpString; - oggConv.readDTSCVector(Strm.getPacket(), tmpString); - conn.SendNow(tmpString); - - } - if (Strm.lastType() == DTSC::PAUSEMARK){ - conn.close(); - ss.close(); - //last page output - } - if (Strm.lastType() == DTSC::INVALID){ - #if DEBUG >= 3 - fprintf(stderr, "Invalid packet received - closing connection.\n"); - #endif - conn.close(); - } - } - }else{ - Util::sleep(100); - } - if ( !ss.connected()){ - break; - } - } - } - conn.close(); - ss.SendNow(conn.getStats("HTTP_Dynamic").c_str()); - ss.close(); - return 0; - } //Progressive_Connector main function - -} //Connector_HTTP namespace - -///\brief The standard process-spawning main function. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables HTTP protocol progressive streaming."; - capa["deps"] = "HTTP"; - capa["url_rel"] = "/$.ogg"; - capa["url_match"] = "/$.ogg"; - capa["socket"] = "http_progressive_ogg"; - capa["codecs"][0u][0u].append("theora"); - capa["codecs"][0u][1u].append("vorbis"); - capa["methods"][0u]["handler"] = "http"; - capa["methods"][0u]["type"] = "html5/video/ogg"; - capa["methods"][0u]["priority"] = 8ll; - capa["methods"][0u]["nolive"] = 1; - conf.addBasicConnectorOptions(capa); - conf.parseArgs(argc, argv); - - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - - return conf.serveForkedSocket(Connector_HTTP::progressiveConnector); -} //main diff --git a/src/connectors/conn_http_smooth.cpp b/src/connectors/conn_http_smooth.cpp deleted file mode 100644 index c269c7da..00000000 --- a/src/connectors/conn_http_smooth.cpp +++ /dev/null @@ -1,516 +0,0 @@ -///\file conn_http_smooth.cpp -///\brief Contains the main code for the HTTP Smooth Connector - -#include <iostream> -#include <iomanip> -#include <queue> -#include <sstream> - -#include <cstdlib> -#include <cstdio> -#include <cmath> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <getopt.h> - -#include <mist/socket.h> -#include <mist/http_parser.h> -#include <mist/json.h> -#include <mist/dtsc.h> -#include <mist/base64.h> -#include <mist/amf.h> -#include <mist/mp4.h> -#include <mist/mp4_ms.h> -#include <mist/mp4_generic.h> -#include <mist/config.h> -#include <mist/stream.h> -#include <mist/timing.h> - -long long unsigned int binToInt(std::string & binary){ - long long int result = 0; - for ( int i = 0; i < 8; i++){ - result <<= 8; - result += binary[i]; - } - return result; -} - -std::string intToBin(long long unsigned int number){ - std::string result; - result.resize(8); - for( int i = 7; i >= 0; i--){ - result[i] = number & 0xFF; - number >>= 8; - } - return result; -} - -std::string toUTF16(std::string original){ - std::string result; - result += (char)0xFF; - result += (char)0xFE; - for (std::string::iterator it = original.begin(); it != original.end(); it++){ - result += (*it); - result += (char)0x00; - } - return result; -} - -///\brief Holds everything unique to HTTP Connectors. -namespace Connector_HTTP { - ///\brief Builds an index file for HTTP Smooth streaming. - ///\param metadata The current metadata, used to generate the index. - ///\return The index file for HTTP Smooth Streaming. - std::string smoothIndex(DTSC::Meta & metadata){ - std::stringstream Result; - Result << "<?xml version=\"1.0\" encoding=\"utf-16\"?>\n"; - Result << "<SmoothStreamingMedia " - "MajorVersion=\"2\" " - "MinorVersion=\"0\" " - "TimeScale=\"10000000\" "; - std::deque<std::map<int,DTSC::Track>::iterator> audioIters; - std::deque<std::map<int,DTSC::Track>::iterator> videoIters; - long long int maxWidth = 0; - long long int maxHeight = 0; - long long int minWidth = 99999999; - long long int minHeight = 99999999; - for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ - if (it->second.codec == "AAC"){ - audioIters.push_back(it); - } - if (it->second.type == "video" && it->second.codec == "H264"){ - videoIters.push_back(it); - if (it->second.width > maxWidth){maxWidth = it->second.width;} - if (it->second.width < minWidth){minWidth = it->second.width;} - if (it->second.height > maxHeight){maxHeight = it->second.height;} - if (it->second.height < minHeight){minHeight = it->second.height;} - } - } - if (metadata.vod){ - Result << "Duration=\"" << (*videoIters.begin())->second.lastms << "0000\""; - }else{ - Result << "Duration=\"0\" " - "IsLive=\"TRUE\" " - "LookAheadFragmentCount=\"2\" " - "DVRWindowLength=\"" << metadata.bufferWindow << "0000\" " - "CanSeek=\"TRUE\" " - "CanPause=\"TRUE\" "; - } - Result << ">\n"; - - //Add audio entries - if (audioIters.size()){ - Result << "<StreamIndex " - "Type=\"audio\" " - "QualityLevels=\"" << audioIters.size() << "\" " - "Name=\"audio\" " - "Chunks=\"" << (*audioIters.begin())->second.keys.size() << "\" " - "Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n"; - int index = 0; - for (std::deque<std::map<int,DTSC::Track>::iterator>::iterator it = audioIters.begin(); it != audioIters.end(); it++){ - Result << "<QualityLevel " - "Index=\"" << index << "\" " - "Bitrate=\"" << (*it)->second.bps * 8 << "\" " - "CodecPrivateData=\"" << std::hex; - for (unsigned int i = 0; i < (*it)->second.init.size(); i++){ - Result << std::setfill('0') << std::setw(2) << std::right << (int)(*it)->second.init[i]; - } - Result << std::dec << "\" " - "SamplingRate=\"" << (*it)->second.rate << "\" " - "Channels=\"2\" " - "BitsPerSample=\"16\" " - "PacketSize=\"4\" " - "AudioTag=\"255\" " - "FourCC=\"AACL\" >\n"; - Result << "<CustomAttributes>\n" - "<Attribute Name = \"TrackID\" Value = \"" << (*it)->first << "\" />" - "</CustomAttributes>"; - Result << "</QualityLevel>\n"; - index++; - } - if ((*audioIters.begin())->second.keys.size()){ - for (std::deque<DTSC::Key>::iterator it = (*audioIters.begin())->second.keys.begin(); it != (((*audioIters.begin())->second.keys.end()) - 1); it++){ - Result << "<c "; - if (it == (*audioIters.begin())->second.keys.begin()){ - Result << "t=\"" << it->getTime() * 10000 << "\" "; - } - Result << "d=\"" << it->getLength() * 10000 << "\" />\n"; - } - } - Result << "</StreamIndex>\n"; - } - //Add video entries - if (videoIters.size()){ - Result << "<StreamIndex " - "Type=\"video\" " - "QualityLevels=\"" << videoIters.size() << "\" " - "Name=\"video\" " - "Chunks=\"" << (*videoIters.begin())->second.keys.size() << "\" " - "Url=\"Q({bitrate},{CustomAttributes})/V({start time})\" " - "MaxWidth=\"" << maxWidth << "\" " - "MaxHeight=\"" << maxHeight << "\" " - "DisplayWidth=\"" << maxWidth << "\" " - "DisplayHeight=\"" << maxHeight << "\">\n"; - int index = 0; - for (std::deque<std::map<int,DTSC::Track>::iterator>::iterator it = videoIters.begin(); it != videoIters.end(); it++){ - //Add video qualities - Result << "<QualityLevel " - "Index=\"" << index << "\" " - "Bitrate=\"" << (*it)->second.bps * 8 << "\" " - "CodecPrivateData=\"" << std::hex; - MP4::AVCC avccbox; - avccbox.setPayload((*it)->second.init); - std::string tmpString = avccbox.asAnnexB(); - for (unsigned int i = 0; i < tmpString.size(); i++){ - Result << std::setfill('0') << std::setw(2) << std::right << (int)tmpString[i]; - } - Result << std::dec << "\" " - "MaxWidth=\"" << (*it)->second.width << "\" " - "MaxHeight=\"" << (*it)->second.height << "\" " - "FourCC=\"AVC1\" >\n"; - Result << "<CustomAttributes>\n" - "<Attribute Name = \"TrackID\" Value = \"" << (*it)->first << "\" />" - "</CustomAttributes>"; - Result << "</QualityLevel>\n"; - index++; - } - if ((*videoIters.begin())->second.keys.size()){ - for (std::deque<DTSC::Key>::iterator it = (*videoIters.begin())->second.keys.begin(); it != (((*videoIters.begin())->second.keys.end()) - 1); it++){ - Result << "<c "; - if (it == (*videoIters.begin())->second.keys.begin()){ - Result << "t=\"" << it->getTime() * 10000 << "\" "; - } - Result << "d=\"" << it->getLength() * 10000 << "\" />\n"; - } - } - Result << "</StreamIndex>\n"; - } - Result << "</SmoothStreamingMedia>\n"; - -#if DEBUG >= 8 - std::cerr << "Sending this manifest:" << std::endl << Result << std::endl; -#endif - return toUTF16(Result.str()); - } //smoothIndex - - ///\brief Main function for the HTTP Smooth Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int smoothConnector(Socket::Connection & conn){ - std::deque<std::string> dataBuffer;//A buffer for the data that needs to be sent to the client. - - DTSC::Stream Strm;//Incoming stream buffer. - HTTP::Parser HTTP_R;//HTTP Receiver - HTTP::Parser HTTP_S;//HTTP Sender. - - bool ready4data = false;//Set to true when streaming is to begin. - Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream. - std::string streamname;//Will contain the name of the stream. - bool handlingRequest = false; - - std::string Quality;//Indicates the request quality of the movie. - long long int requestedTime = -1;//Indicates the fragment requested. - std::string parseString;//A string used for parsing different aspects of the request. - unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket. - conn.setBlocking(false);//Set the client socket to non-blocking - - while (conn.connected()){ - if ( !handlingRequest){ - if (conn.spool() && HTTP_R.Read(conn)){ - #if DEBUG >= 5 - std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; - #endif - //Get data set by the proxy. - conn.setHost(HTTP_R.GetHeader("X-Origin")); - streamname = HTTP_R.GetHeader("X-Stream"); - if ( !ss){ - //initiate Stream Socket - ss = Util::Stream::getStream(streamname); - if ( !ss.connected()){ - #if DEBUG >= 1 - fprintf(stderr, "Could not connect to server!\n"); - #endif - HTTP_S.Clean(); - HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); - conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); - ready4data = false; - continue; - } - ss.setBlocking(false); - Strm.waitForMeta(ss); - } - - - if (HTTP_R.url.find(".xap") != std::string::npos){ -#include "xap.h" - - HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type", "application/siverlight"); - HTTP_S.SetHeader("Cache-Control", "cache"); - HTTP_S.SetBody(""); - HTTP_S.SetHeader("Content-Length", xap_len); - HTTP_S.SendResponse("200", "OK", conn); - conn.SendNow((const char *)xap_data, xap_len); - }else{ - if (HTTP_R.url.find("Manifest") == std::string::npos){ - //We have a non-manifest request, parse it. - - Quality = HTTP_R.url.substr(HTTP_R.url.find("TrackID=", 8) + 8); - Quality = Quality.substr(0, Quality.find(")")); - parseString = HTTP_R.url.substr(HTTP_R.url.find(")/") + 2); - parseString = parseString.substr(parseString.find("(") + 1); - requestedTime = atoll(parseString.substr(0, parseString.find(")")).c_str()); - long long int selectedQuality = atoll(Quality.c_str()); - DTSC::Track & myRef = Strm.metadata.tracks[selectedQuality]; - if (Strm.metadata.live){ - int seekable = Strm.canSeekms(requestedTime / 10000); - if (seekable == 0){ - // iff the fragment in question is available, check if the next is available too - for (std::deque<DTSC::Key>::iterator it = myRef.keys.begin(); it != myRef.keys.end(); it++){ - if (it->getTime() >= (requestedTime / 10000)){ - if ((it + 1) == myRef.keys.end()){ - seekable = 1; - } - break; - } - } - } - if (seekable < 0){ - HTTP_S.Clean(); - HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); - conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range")); - HTTP_R.Clean(); //clean for any possible next requests - std::cout << "Fragment @ " << requestedTime / 10000 << "ms too old (" << myRef.keys.begin()->getTime() << " - " << myRef.keys.rbegin()->getTime() << " ms)" << std::endl; - continue; - } - if (seekable > 0){ - HTTP_S.Clean(); - HTTP_S.SetBody("Proxy, re-request this in a second or two.\n"); - conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later")); - HTTP_R.Clean(); //clean for any possible next requests - std::cout << "Fragment @ " << requestedTime / 10000 << "ms not available yet (" << myRef.keys.begin()->getTime() << " - " << myRef.keys.rbegin()->getTime() << " ms)" << std::endl; - continue; - } - } - //Seek to the right place and send a play-once for a single fragment. - std::stringstream sstream; - - long long mstime = 0; - int partOffset = 0; - int keyDur = 0; - DTSC::Key keyObj; - for (std::deque<DTSC::Key>::iterator it = myRef.keys.begin(); it != myRef.keys.end(); it++){ - if (it->getTime() >= (requestedTime / 10000)){ - mstime = it->getTime(); - keyObj = (*it); - std::deque<DTSC::Key>::iterator nextIt = it; - nextIt++; - if (nextIt != myRef.keys.end()){ - keyDur = nextIt->getTime() - it->getTime(); - }else{ - keyDur = -1; - if (Strm.metadata.live){ - HTTP_S.Clean(); - HTTP_S.SetBody("Proxy, re-request this in a second or two.\n"); - conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later")); - HTTP_R.Clean(); //clean for any possible next requests - std::cout << "Fragment after fragment @ " << (requestedTime / 10000) << " not available yet" << std::endl; - } - } - break; - } - partOffset += it->getParts(); - } - if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead. - if (Strm.metadata.live){ - if (mstime == 0 && (requestedTime / 10000) > 1){ - HTTP_S.Clean(); - HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); - conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range")); - HTTP_R.Clean(); //clean for any possible next requests - std::cout << "Fragment @ " << (requestedTime / 10000) << " too old" << std::endl; - continue; - } - } - - sstream << "t " << myRef.trackID << "\n"; - sstream << "s " << keyObj.getTime() << "\n"; - if (keyDur != -1){ - sstream << "p " << keyObj.getTime() + keyDur << "\n"; - }else{ - sstream << "p\n"; - } - - ss.SendNow(sstream.str().c_str()); - - //Wrap everything in mp4 boxes - MP4::MFHD mfhd_box; - mfhd_box.setSequenceNumber(((keyObj.getNumber() - 1) * 2) + myRef.trackID); - - MP4::TFHD tfhd_box; - tfhd_box.setFlags(MP4::tfhdSampleFlag); - tfhd_box.setTrackID(myRef.trackID); - if (myRef.type == "video"){ - tfhd_box.setDefaultSampleFlags(0x00004001); - }else{ - tfhd_box.setDefaultSampleFlags(0x00008002); - } - - MP4::TRUN trun_box; - trun_box.setDataOffset(42); - unsigned int keySize = 0; - if (myRef.type == "video"){ - trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize | MP4::trunsampleOffsets); - }else{ - trun_box.setFlags(MP4::trundataOffset | MP4::trunsampleDuration | MP4::trunsampleSize); - } - trun_box.setFirstSampleFlags(0x00004002); - for (int i = 0; i < keyObj.getParts(); i++){ - MP4::trunSampleInformation trunSample; - trunSample.sampleSize = Strm.metadata.tracks[myRef.trackID].parts[i + partOffset].getSize(); - keySize += Strm.metadata.tracks[myRef.trackID].parts[i + partOffset].getSize(); - trunSample.sampleDuration = Strm.metadata.tracks[myRef.trackID].parts[i + partOffset].getDuration() * 10000; - if (myRef.type == "video"){ - trunSample.sampleOffset = Strm.metadata.tracks[myRef.trackID].parts[i + partOffset].getOffset() * 10000; - } - trun_box.setSampleInformation(trunSample, i); - } - - MP4::SDTP sdtp_box; - sdtp_box.setVersion(0); - if (myRef.type == "video"){ - sdtp_box.setValue(36, 4); - for (int i = 1; i < keyObj.getParts(); i++){ - sdtp_box.setValue(20, 4 + i); - } - }else{ - sdtp_box.setValue(40, 4); - for (int i = 1; i < keyObj.getParts(); i++){ - sdtp_box.setValue(40, 4 + i); - } - } - - MP4::TRAF traf_box; - traf_box.setContent(tfhd_box, 0); - traf_box.setContent(trun_box, 1); - traf_box.setContent(sdtp_box, 2); - - //If the stream is live, we want to have a fragref box if possible - if (Strm.metadata.live){ - MP4::UUID_TrackFragmentReference fragref_box; - fragref_box.setVersion(1); - fragref_box.setFragmentCount(0); - int fragCount = 0; - for (unsigned int i = 0; fragCount < 2 && i < myRef.keys.size() - 1; i++){ - if (myRef.keys[i].getTime() > (requestedTime / 10000)){ - fragref_box.setTime(fragCount, myRef.keys[i].getTime() * 10000); - fragref_box.setDuration(fragCount, myRef.keys[i].getLength() * 10000); - fragref_box.setFragmentCount(++fragCount); - } - } - traf_box.setContent(fragref_box, 3); - } - - MP4::MOOF moof_box; - moof_box.setContent(mfhd_box, 0); - //Setting the correct offsets. - moof_box.setContent(traf_box, 1); - trun_box.setDataOffset(moof_box.boxedSize() + 8); - traf_box.setContent(trun_box, 1); - moof_box.setContent(traf_box, 1); - - HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type", "video/mp4"); - HTTP_S.StartResponse(HTTP_R, conn); - HTTP_S.Chunkify(moof_box.asBox(), moof_box.boxedSize(), conn); - int size = htonl(keySize + 8); - HTTP_S.Chunkify((char*)&size, 4, conn); - HTTP_S.Chunkify("mdat", 4, conn); - handlingRequest = true; - }else{ - //We have a request for a Manifest, generate and send it. - - HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type", "text/xml"); - HTTP_S.SetHeader("Cache-Control", "no-cache"); - std::string manifest = smoothIndex(Strm.metadata); - HTTP_S.SetBody(manifest); - HTTP_S.SendResponse("200", "OK", conn); - } - } - ready4data = true; - //Clean for any possible next requests - HTTP_R.Clean(); - }else{ - //Wait 250ms before checking for new data. - Util::sleep(250); - } - }else{ - if (!ready4data){ - //Wait 250ms before checking for new data. - Util::sleep(250); - } - } - if (ready4data){ - unsigned int now = Util::epoch(); - if (now != lastStats){ - //Send new stats. - lastStats = now; - ss.SendNow(conn.getStats("HTTP_Smooth")); - } - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - if (Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){ - HTTP_S.Chunkify(Strm.lastData(), conn); - } - if (Strm.lastType() == DTSC::PAUSEMARK){ - HTTP_S.Chunkify("", 0, conn); - handlingRequest = false; - } - } - }else{ - Util::sleep(10); - } - if ( !ss.connected()){ - break; - } - } - } - conn.close(); - ss.SendNow(conn.getStats("HTTP_Smooth").c_str()); - ss.close(); - return 0; - }//Smooth_Connector main function - -}//Connector_HTTP namespace - -///\brief The standard process-spawning main function. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables HTTP protocol Microsoft-specific smooth streaming through silverlight (also known as HSS)."; - capa["deps"] = "HTTP"; - capa["url_rel"] = "/smooth/$.ism/Manifest"; - capa["url_prefix"] = "/smooth/$.ism/"; - capa["socket"] = "http_smooth"; - capa["codecs"][0u][0u].append("H264"); - capa["codecs"][0u][1u].append("AAC"); - capa["methods"][0u]["handler"] = "http"; - capa["methods"][0u]["type"] = "html5/application/vnd.ms-ss"; - capa["methods"][0u]["priority"] = 9ll; - capa["methods"][0u]["nolive"] = 1; - capa["methods"][1u]["handler"] = "http"; - capa["methods"][1u]["type"] = "silverlight"; - capa["methods"][1u]["priority"] = 1ll; - capa["methods"][1u]["nolive"] = 1; - conf.addBasicConnectorOptions(capa); - conf.parseArgs(argc, argv); - - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - - return conf.serveForkedSocket(Connector_HTTP::smoothConnector); -} //main diff --git a/src/connectors/conn_http_srt.cpp b/src/connectors/conn_http_srt.cpp deleted file mode 100644 index cbb8513e..00000000 --- a/src/connectors/conn_http_srt.cpp +++ /dev/null @@ -1,223 +0,0 @@ -///\file conn_http_srt.cpp -///\brief Contains the main code for the HTTP SRT Connector - -#include <iostream> -#include <queue> -#include <sstream> -#include <iomanip> - -#include <cstdlib> -#include <cstdio> -#include <cmath> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <getopt.h> - -#include <mist/socket.h> -#include <mist/http_parser.h> -#include <mist/dtsc.h> -#include <mist/flv_tag.h> -#include <mist/amf.h> -#include <mist/config.h> -#include <mist/stream.h> -#include <mist/timing.h> - -///\brief Holds everything unique to HTTP Connectors. -namespace Connector_HTTP { - ///\brief Main function for the HTTP Progressive Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int SRTConnector(Socket::Connection & conn){ - DTSC::Stream Strm; //Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender. - bool inited = false;//Whether the stream is initialized - Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream. - std::string streamname;//Will contain the name of the stream. - - unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket. - unsigned int seek_time = 0;//Seek position in ms - int trackID = -1; // the track to be selected - int curIndex = 0; // SRT index - bool subtitleTrack = false; // check whether the requested track is a srt track - bool isWebVTT = false; - - std::stringstream srtdata; // ss output data - - while (conn.connected()){ - //Only attempt to parse input when not yet init'ed. - if ( !inited){ - if (conn.spool() && HTTP_R.Read(conn)){ -#if DEBUG >= 5 - std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; -#endif - conn.setHost(HTTP_R.GetHeader("X-Origin")); - streamname = HTTP_R.GetHeader("X-Stream"); - - int start = 0; - if ( !HTTP_R.GetVar("start").empty()){ - start = atoi(HTTP_R.GetVar("start").c_str()); - } - if ( !HTTP_R.GetVar("starttime").empty()){ - start = atoi(HTTP_R.GetVar("starttime").c_str()); - } - if ( !HTTP_R.GetVar("apstart").empty()){ - start = atoi(HTTP_R.GetVar("apstart").c_str()); - } - if ( !HTTP_R.GetVar("ec_seek").empty()){ - start = atoi(HTTP_R.GetVar("ec_seek").c_str()); - } - if ( !HTTP_R.GetVar("fs").empty()){ - start = atoi(HTTP_R.GetVar("fs").c_str()); - } - if ( !HTTP_R.GetVar("trackid").empty()){ - trackID = atoi(HTTP_R.GetVar("trackid").c_str()); - } - if ( !HTTP_R.GetVar("webvtt").empty()){ - isWebVTT = true; - }else{ - isWebVTT = false; - } - //under 3 hours we assume seconds, otherwise byte position - if (start < 10800){ - seek_time = start * 1000; //ms, not s - }else{ - seek_time = start * 1000; //divide by 1mbit, then *1000 for ms. - } - - //we are ready, connect the socket! - if ( !ss.connected()){ - ss = Util::Stream::getStream(streamname); - } - if ( !ss.connected()){ - #if DEBUG >= 1 - fprintf(stderr, "Could not connect to server for %s!\n", streamname.c_str()); - #endif - ss.close(); - HTTP_S.Clean(); - HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); - conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); - inited = false; - continue; - } - - Strm.waitForMeta(ss); - - if(trackID == -1){ - // no track was given. Fetch the first track that has SRT data - for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){ - if (it->second.codec == "srt"){ - trackID = it->second.trackID; - subtitleTrack = true; - break; - } - } - }else{ - // track *was* given, but we have to check whether it's an actual srt track - subtitleTrack = Strm.metadata.tracks[trackID].codec == "srt"; - } - - if(!subtitleTrack){ - HTTP_S.Clean(); - HTTP_S.SetBody("# This track doesn't contain subtitle data.\n"); - conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); - subtitleTrack = false; - HTTP_R.Clean(); - continue; - } - - std::stringstream cmd; - - cmd << "t " << trackID; - - int maxTime = Strm.metadata.tracks[trackID].lastms; - - cmd << "\ns " << seek_time << "\np " << maxTime << "\n"; - ss.SendNow(cmd.str().c_str(), cmd.str().size()); - - inited = true; - - HTTP_R.Clean(); //clean for any possible next requests - srtdata.clear(); - curIndex = 1; // set to 1, first srt 'track' - } - } - - unsigned int now = Util::epoch(); - if (now != lastStats){ - lastStats = now; - ss.SendNow(conn.getStats("HTTP_SRT").c_str()); - } - - if (inited){ - - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - - if(Strm.lastType() == DTSC::META){ - - if(!isWebVTT) - { - srtdata << curIndex++ << std::endl; - } - long long unsigned int time = Strm.getPacket()["time"].asInt(); - srtdata << std::setfill('0') << std::setw(2) << (time / 3600000) << ":"; - srtdata << std::setfill('0') << std::setw(2) << ((time % 3600000) / 60000) << ":"; - srtdata << std::setfill('0') << std::setw(2) << (((time % 3600000) % 60000) / 1000) << ","; - srtdata << std::setfill('0') << std::setw(3) << time % 1000 << " --> "; - time += Strm.getPacket()["duration"].asInt(); - srtdata << std::setfill('0') << std::setw(2) << (time / 3600000) << ":"; - srtdata << std::setfill('0') << std::setw(2) << ((time % 3600000) / 60000) << ":"; - srtdata << std::setfill('0') << std::setw(2) << (((time % 3600000) % 60000) / 1000) << ","; - srtdata << std::setfill('0') << std::setw(3) << time % 1000 << std::endl; - srtdata << Strm.lastData() << std::endl; - } - - if( Strm.lastType() == DTSC::PAUSEMARK){ - HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers - HTTP_S.SetHeader("Content-Type", "text/plain"); //Send the correct content-type for FLV files - HTTP_S.SetBody( (isWebVTT ? "WEBVTT\n\n" : "") + srtdata.str()); - conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file - inited = false; - - srtdata.str(""); - srtdata.clear(); - } - } - }else{ - Util::sleep(200); - } - if ( !ss.connected()){ - break; - } - } - } - conn.close(); - ss.SendNow(conn.getStats("HTTP_SRT").c_str()); - ss.close(); - return 0; - } //SRT main function - -} //Connector_HTTP namespace - -///\brief The standard process-spawning main function. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables HTTP protocol subtitle streaming."; - capa["deps"] = "HTTP"; - capa["url_rel"] = "/$.srt"; - capa["url_match"] = "/$.srt"; - capa["url_handler"] = "http"; - capa["url_type"] = "subtitle"; - capa["socket"] = "http_srt"; - conf.addBasicConnectorOptions(capa); - conf.parseArgs(argc, argv); - - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - - return conf.serveForkedSocket(Connector_HTTP::SRTConnector); -} //main diff --git a/src/connectors/conn_raw.cpp b/src/connectors/conn_raw.cpp deleted file mode 100644 index 646e9f51..00000000 --- a/src/connectors/conn_raw.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/// \file conn_raw.cpp -/// Contains the main code for the RAW connector. - -#include <iostream> -#include <sstream> -#include <mist/config.h> -#include <mist/socket.h> -#include <mist/stream.h> -#include <mist/timing.h> - -///\brief Contains the main code for the RAW connector. -/// -///Expects a single commandline argument telling it which stream to connect to, -///then outputs the raw stream to stdout. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - conf.addBasicConnectorOptions(capa); - conf.addOption("stream_name", JSON::fromString("{\"arg_num\":1, \"help\":\"Name of the stream to write to stdout.\"}")); - conf.parseArgs(argc, argv); - - if (conf.getBool("json")){ - std::cout << "null" << std::endl; - return -1; - } - - //connect to the proper stream - Socket::Connection S = Util::Stream::getStream(conf.getString("stream_name")); - S.setBlocking(false); - if ( !S.connected()){ - std::cout << "Could not open stream " << conf.getString("stream_name") << std::endl; - return 1; - } - long long int lastStats = 0; - long long int started = Util::epoch(); - while (std::cout.good() && S.connected()){ - if (S.spool()){ - while (S.Received().size()){ - std::cout.write(S.Received().get().c_str(), S.Received().get().size()); - S.Received().get().clear(); - } - }else{ - Util::sleep(500); //sleep 500ms if no data - } - unsigned int now = Util::epoch(); - if (now != lastStats){ - lastStats = now; - std::stringstream st; - st << "S localhost RAW " << (Util::epoch() - started) << " " << S.dataDown() << " " << S.dataUp() << "\n"; - S.SendNow(st.str().c_str()); - } - } - std::stringstream st; - st << "S localhost RAW " << (Util::epoch() - started) << " " << S.dataDown() << " " << S.dataUp() << "\n"; - S.SendNow(st.str().c_str()); - S.close(); - return 0; -} diff --git a/src/connectors/conn_rtmp.cpp b/src/connectors/conn_rtmp.cpp deleted file mode 100644 index ce4f5ba4..00000000 --- a/src/connectors/conn_rtmp.cpp +++ /dev/null @@ -1,700 +0,0 @@ -/// \file conn_rtmp.cpp -/// Contains the main code for the RTMP Connector - -#include <iostream> -#include <sstream> - -#include <cstdlib> -#include <cstdio> -#include <cmath> -#include <unistd.h> -#include <signal.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <getopt.h> - -#include <mist/socket.h> -#include <mist/config.h> -#include <mist/flv_tag.h> -#include <mist/amf.h> -#include <mist/rtmpchunks.h> -#include <mist/stream.h> -#include <mist/timing.h> - -///\brief Holds everything unique to the RTMP Connector -namespace Connector_RTMP { - - //for connection to server - bool ready4data = false; ///< Indicates whether streaming can start. - bool inited = false; ///< Indicates whether we are ready to connect to the Buffer. - bool noStats = false; ///< Indicates when no stats should be sent anymore. Used in push mode. - bool stopParsing = false; ///< Indicates when to stop all parsing. - bool streamReset = false; - - //for reply to play command - int playTransaction = -1;///<The transaction number of the reply. - int playStreamId = -1;///<The stream id of the reply. - int playMessageType = -1;///<The message type of the reply. - - //generic state keeping - bool streamInited = false;///<Indicates whether init data for audio/video was sent. - int videoID = -1; - int audioID = -1; - - Socket::Connection Socket; ///< A copy of the user socket to allow helper functions to directly send data. - Socket::Connection ss; ///< Socket connected to server. - std::string streamName; ///< Stream that will be opened. - std::string app_name; ///< Name of the application that was opened - - ///\brief Sends a RTMP command either in AMF or AMF3 mode. - ///\param amfReply The data to be sent over RTMP. - ///\param messageType The type of message. - ///\param streamId The ID of the AMF stream. - void sendCommand(AMF::Object & amfReply, int messageType, int streamId){ - #if DEBUG >= 8 - std::cerr << amfReply.Print() << std::endl; - #endif - if (messageType == 17){ - Socket.SendNow(RTMPStream::SendChunk(3, messageType, streamId, (char)0 + amfReply.Pack())); - }else{ - Socket.SendNow(RTMPStream::SendChunk(3, messageType, streamId, amfReply.Pack())); - } - } //sendCommand - - ///\brief Parses a single AMF command message, and sends a direct response through sendCommand(). - ///\param amfData The received request. - ///\param messageType The type of message. - ///\param streamId The ID of the AMF stream. - void parseAMFCommand(AMF::Object & amfData, int messageType, int streamId){ - #if DEBUG >= 5 - fprintf(stderr, "Received command: %s\n", amfData.Print().c_str()); - #endif - #if DEBUG >= 8 - fprintf(stderr, "AMF0 command: %s\n", amfData.getContentP(0)->StrValue().c_str()); - #endif - if (amfData.getContentP(0)->StrValue() == "connect"){ - double objencoding = 0; - if (amfData.getContentP(2)->getContentP("objectEncoding")){ - objencoding = amfData.getContentP(2)->getContentP("objectEncoding")->NumValue(); - } - #if DEBUG >= 6 - int tmpint; - if (amfData.getContentP(2)->getContentP("videoCodecs")){ - tmpint = (int)amfData.getContentP(2)->getContentP("videoCodecs")->NumValue(); - if (tmpint & 0x04){ - fprintf(stderr, "Sorensen video support detected\n"); - } - if (tmpint & 0x80){ - fprintf(stderr, "H264 video support detected\n"); - } - } - if (amfData.getContentP(2)->getContentP("audioCodecs")){ - tmpint = (int)amfData.getContentP(2)->getContentP("audioCodecs")->NumValue(); - if (tmpint & 0x04){ - fprintf(stderr, "MP3 audio support detected\n"); - } - if (tmpint & 0x400){ - fprintf(stderr, "AAC audio support detected\n"); - } - } - #endif - app_name = amfData.getContentP(2)->getContentP("tcUrl")->StrValue(); - app_name = app_name.substr(app_name.find('/', 7) + 1); - RTMPStream::chunk_snd_max = 4096; - Socket.Send(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1) - Socket.Send(RTMPStream::SendCTL(5, RTMPStream::snd_window_size)); //send window acknowledgement size (msg 5) - Socket.Send(RTMPStream::SendCTL(6, RTMPStream::rec_window_size)); //send rec window acknowledgement size (msg 6) - Socket.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 - //send a _result reply - AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "_result")); //result success - amfReply.addContent(amfData.getContent(1)); //same transaction ID - amfReply.addContent(AMF::Object("")); //server properties - amfReply.getContentP(2)->addContent(AMF::Object("fmsVer", "FMS/3,5,5,2004")); - amfReply.getContentP(2)->addContent(AMF::Object("capabilities", (double)31)); - amfReply.getContentP(2)->addContent(AMF::Object("mode", (double)1)); - amfReply.addContent(AMF::Object("")); //info - amfReply.getContentP(3)->addContent(AMF::Object("level", "status")); - amfReply.getContentP(3)->addContent(AMF::Object("code", "NetConnection.Connect.Success")); - amfReply.getContentP(3)->addContent(AMF::Object("description", "Connection succeeded.")); - amfReply.getContentP(3)->addContent(AMF::Object("clientid", 1337)); - amfReply.getContentP(3)->addContent(AMF::Object("objectEncoding", objencoding)); - //amfReply.getContentP(3)->addContent(AMF::Object("data", AMF::AMF0_ECMA_ARRAY)); - //amfReply.getContentP(3)->getContentP(4)->addContent(AMF::Object("version", "3,5,4,1004")); - sendCommand(amfReply, messageType, streamId); - //send onBWDone packet - no clue what it is, but real server sends it... - //amfReply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER); - //amfReply.addContent(AMF::Object("", "onBWDone"));//result - //amfReply.addContent(amfData.getContent(1));//same transaction ID - //amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - //sendCommand(amfReply, messageType, streamId); - return; - } //connect - if (amfData.getContentP(0)->StrValue() == "createStream"){ - //send a _result reply - AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "_result")); //result success - amfReply.addContent(amfData.getContent(1)); //same transaction ID - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfReply.addContent(AMF::Object("", (double)1)); //stream ID - we use 1 - sendCommand(amfReply, messageType, streamId); - Socket.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 - return; - } //createStream - if ((amfData.getContentP(0)->StrValue() == "closeStream") || (amfData.getContentP(0)->StrValue() == "deleteStream")){ - if (ss.connected()){ - ss.close(); - } - return; - } - if ((amfData.getContentP(0)->StrValue() == "FCUnpublish") || (amfData.getContentP(0)->StrValue() == "releaseStream")){ - // ignored - return; - } - if ((amfData.getContentP(0)->StrValue() == "FCPublish")){ - //send a FCPublic reply - AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "onFCPublish")); //status reply - amfReply.addContent(AMF::Object("", 0, AMF::AMF0_NUMBER)); //same transaction ID - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfReply.addContent(AMF::Object("")); //info - amfReply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Publish.Start")); - amfReply.getContentP(3)->addContent(AMF::Object("description", "Please followup with publish command...")); - sendCommand(amfReply, messageType, streamId); - return; - } //FCPublish - if (amfData.getContentP(0)->StrValue() == "releaseStream"){ - //send a _result reply - AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "_result")); //result success - amfReply.addContent(amfData.getContent(1)); //same transaction ID - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfReply.addContent(AMF::Object("", AMF::AMF0_UNDEFINED)); //stream ID? - sendCommand(amfReply, messageType, streamId); - return; - }//releaseStream - if ((amfData.getContentP(0)->StrValue() == "getStreamLength") || (amfData.getContentP(0)->StrValue() == "getMovLen")){ - //send a _result reply - AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "_result")); //result success - amfReply.addContent(amfData.getContent(1)); //same transaction ID - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfReply.addContent(AMF::Object("", (double)0)); //zero length - sendCommand(amfReply, messageType, streamId); - return; - } //getStreamLength - if ((amfData.getContentP(0)->StrValue() == "publish")){ - if (amfData.getContentP(3)){ - streamName = amfData.getContentP(3)->StrValue(); - /// \todo implement push for MistPlayer or restrict and change to getLive - ss = Util::Stream::getStream(streamName); - if ( !ss.connected()){ - #if DEBUG >= 1 - fprintf(stderr, "Could not connect to server!\n"); - #endif - Socket.close(); //disconnect user - return; - } - DTSC::Stream Strm; - Strm.waitForMeta(ss); - ss.Send("P "); - ss.Send(Socket.getHost().c_str()); - ss.Send(" "); - ss.Send(app_name); - ss.SendNow("\n"); - streamReset = true; - noStats = true; - } - //send a _result reply - AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "_result")); //result success - amfReply.addContent(amfData.getContent(1)); //same transaction ID - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfReply.addContent(AMF::Object("", 1, AMF::AMF0_BOOL)); //publish success? - sendCommand(amfReply, messageType, streamId); - Socket.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 - //send a status reply - amfReply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "onStatus")); //status reply - amfReply.addContent(AMF::Object("", 0, AMF::AMF0_NUMBER)); //same transaction ID - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfReply.addContent(AMF::Object("")); //info - amfReply.getContentP(3)->addContent(AMF::Object("level", "status")); - amfReply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Publish.Start")); - amfReply.getContentP(3)->addContent(AMF::Object("description", "Stream is now published!")); - amfReply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); - sendCommand(amfReply, messageType, streamId); - return; - } //getStreamLength - if (amfData.getContentP(0)->StrValue() == "checkBandwidth"){ - //send a _result reply - AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "_result")); //result success - amfReply.addContent(amfData.getContent(1)); //same transaction ID - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - sendCommand(amfReply, messageType, streamId); - return; - } //checkBandwidth - if ((amfData.getContentP(0)->StrValue() == "play") || (amfData.getContentP(0)->StrValue() == "play2")){ - //set reply number and stream name, actual reply is sent up in the ss.spool() handler - playTransaction = amfData.getContentP(1)->NumValue(); - playMessageType = messageType; - playStreamId = streamId; - streamName = amfData.getContentP(3)->StrValue(); - Connector_RTMP::ready4data = true; //start sending video data! - return; - } //play - if ((amfData.getContentP(0)->StrValue() == "seek")){ - //set reply number and stream name, actual reply is sent up in the ss.spool() handler - playTransaction = amfData.getContentP(1)->NumValue(); - playMessageType = messageType; - playStreamId = streamId; - streamInited = false; - - AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "onStatus")); //status reply - amfReply.addContent(amfData.getContent(1)); //same transaction ID - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfReply.addContent(AMF::Object("")); //info - amfReply.getContentP(3)->addContent(AMF::Object("level", "status")); - amfReply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Seek.Notify")); - amfReply.getContentP(3)->addContent(AMF::Object("description", "Seeking to the specified time")); - amfReply.getContentP(3)->addContent(AMF::Object("details", "DDV")); - amfReply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); - sendCommand(amfReply, playMessageType, playStreamId); - ss.Send("s "); - ss.Send(JSON::Value((long long int)amfData.getContentP(3)->NumValue()).asString().c_str()); - ss.SendNow("\n"); - return; - } //seek - if ((amfData.getContentP(0)->StrValue() == "pauseRaw") || (amfData.getContentP(0)->StrValue() == "pause")){ - if (amfData.getContentP(3)->NumValue()){ - ss.Send("q\n"); //quit playing - //send a status reply - AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "onStatus")); //status reply - amfReply.addContent(amfData.getContent(1)); //same transaction ID - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfReply.addContent(AMF::Object("")); //info - amfReply.getContentP(3)->addContent(AMF::Object("level", "status")); - amfReply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Pause.Notify")); - amfReply.getContentP(3)->addContent(AMF::Object("description", "Pausing playback")); - amfReply.getContentP(3)->addContent(AMF::Object("details", "DDV")); - amfReply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); - sendCommand(amfReply, playMessageType, playStreamId); - }else{ - ss.SendNow("p\n"); //start playing - //send a status reply - AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); - amfReply.addContent(AMF::Object("", "onStatus")); //status reply - amfReply.addContent(amfData.getContent(1)); //same transaction ID - amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfReply.addContent(AMF::Object("")); //info - amfReply.getContentP(3)->addContent(AMF::Object("level", "status")); - amfReply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Unpause.Notify")); - amfReply.getContentP(3)->addContent(AMF::Object("description", "Resuming playback")); - amfReply.getContentP(3)->addContent(AMF::Object("details", "DDV")); - amfReply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); - sendCommand(amfReply, playMessageType, playStreamId); - } - return; - } //seek - - #if DEBUG >= 2 - fprintf(stderr, "AMF0 command not processed!\n%s\n", amfData.Print().c_str()); - #endif - } //parseAMFCommand - - ///\brief Gets and parses one RTMP chunk at a time. - ///\param inputBuffer A buffer filled with chunk data. - void parseChunk(Socket::Buffer & inputBuffer){ - //for DTSC conversion - static DTSC::Meta meta_out; - static std::stringstream prebuffer; // Temporary buffer before sending real data - static bool sending = false; - static unsigned int counter = 0; - //for chunk parsing - static RTMPStream::Chunk next; - static FLV::Tag F; - static AMF::Object amfdata("empty", AMF::AMF0_DDV_CONTAINER); - static AMF::Object amfelem("empty", AMF::AMF0_DDV_CONTAINER); - static AMF::Object3 amf3data("empty", AMF::AMF3_DDV_CONTAINER); - static AMF::Object3 amf3elem("empty", AMF::AMF3_DDV_CONTAINER); - - while (next.Parse(inputBuffer)){ - - //send ACK if we received a whole window - if ((RTMPStream::rec_cnt - RTMPStream::rec_window_at > RTMPStream::rec_window_size)){ - RTMPStream::rec_window_at = RTMPStream::rec_cnt; - Socket.Send(RTMPStream::SendCTL(3, RTMPStream::rec_cnt)); //send ack (msg 3) - } - - switch (next.msg_type_id){ - case 0: //does not exist - #if DEBUG >= 2 - fprintf(stderr, "UNKN: Received a zero-type message. Possible data corruption? Aborting!\n"); - #endif - while (inputBuffer.size()){ - inputBuffer.get().clear(); - } - ss.close(); - Socket.close(); - break; //happens when connection breaks unexpectedly - case 1: //set chunk size - RTMPStream::chunk_rec_max = ntohl(*(int*)next.data.c_str()); - #if DEBUG >= 5 - fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max); - #endif - break; - case 2: //abort message - we ignore this one - #if DEBUG >= 5 - fprintf(stderr, "CTRL: Abort message\n"); - #endif - //4 bytes of stream id to drop - break; - case 3: //ack - #if DEBUG >= 8 - fprintf(stderr, "CTRL: Acknowledgement\n"); - #endif - RTMPStream::snd_window_at = ntohl(*(int*)next.data.c_str()); - RTMPStream::snd_window_at = RTMPStream::snd_cnt; - break; - case 4: { - //2 bytes event type, rest = event data - //types: - //0 = stream begin, 4 bytes ID - //1 = stream EOF, 4 bytes ID - //2 = stream dry, 4 bytes ID - //3 = setbufferlen, 4 bytes ID, 4 bytes length - //4 = streamisrecorded, 4 bytes ID - //6 = pingrequest, 4 bytes data - //7 = pingresponse, 4 bytes data - //we don't need to process this - #if DEBUG >= 5 - short int ucmtype = ntohs(*(short int*)next.data.c_str()); - switch (ucmtype){ - case 0: - fprintf(stderr, "CTRL: UCM StreamBegin %i\n", ntohl(*((int*)(next.data.c_str()+2)))); - break; - case 1: - fprintf(stderr, "CTRL: UCM StreamEOF %i\n", ntohl(*((int*)(next.data.c_str()+2)))); - break; - case 2: - fprintf(stderr, "CTRL: UCM StreamDry %i\n", ntohl(*((int*)(next.data.c_str()+2)))); - break; - case 3: - fprintf(stderr, "CTRL: UCM SetBufferLength %i %i\n", ntohl(*((int*)(next.data.c_str()+2))), ntohl(*((int*)(next.data.c_str()+6)))); - break; - case 4: - fprintf(stderr, "CTRL: UCM StreamIsRecorded %i\n", ntohl(*((int*)(next.data.c_str()+2)))); - break; - case 6: - fprintf(stderr, "CTRL: UCM PingRequest %i\n", ntohl(*((int*)(next.data.c_str()+2)))); - break; - case 7: - fprintf(stderr, "CTRL: UCM PingResponse %i\n", ntohl(*((int*)(next.data.c_str()+2)))); - break; - default: - fprintf(stderr, "CTRL: UCM Unknown (%hi)\n", ucmtype); - break; - } - #endif - } - break; - case 5: //window size of other end - #if DEBUG >= 5 - fprintf(stderr, "CTRL: Window size\n"); - #endif - RTMPStream::rec_window_size = ntohl(*(int*)next.data.c_str()); - RTMPStream::rec_window_at = RTMPStream::rec_cnt; - Socket.Send(RTMPStream::SendCTL(3, RTMPStream::rec_cnt)); //send ack (msg 3) - break; - case 6: - #if DEBUG >= 5 - fprintf(stderr, "CTRL: Set peer bandwidth\n"); - #endif - //4 bytes window size, 1 byte limit type (ignored) - RTMPStream::snd_window_size = ntohl(*(int*)next.data.c_str()); - Socket.Send(RTMPStream::SendCTL(5, RTMPStream::snd_window_size)); //send window acknowledgement size (msg 5) - break; - case 8: //audio data - case 9: //video data - case 18: //meta data - if (ss.connected()){ - if (streamReset){ - //reset push data to empty, in case stream properties change - meta_out.reset(); - prebuffer.str(""); - sending = false; - counter = 0; - streamReset = false; - } - F.ChunkLoader(next); - JSON::Value pack_out = F.toJSON(meta_out); - if ( !pack_out.isNull()){ - if ( !sending){ - counter++; - if (counter > 8){ - sending = true; - meta_out.send(ss); - ss.SendNow(prebuffer.str()); //write buffer - prebuffer.str(""); //clear buffer - pack_out.sendTo(ss); - }else{ - prebuffer << pack_out.toNetPacked(); - } - }else{ - pack_out.sendTo(ss); - } - } - }else{ - #if DEBUG >= 5 - fprintf(stderr, "Received useless media data\n"); - #endif - Socket.close(); - } - break; - case 15: - #if DEBUG >= 5 - fprintf(stderr, "Received AFM3 data message\n"); - #endif - break; - case 16: - #if DEBUG >= 5 - fprintf(stderr, "Received AFM3 shared object\n"); - #endif - break; - case 17: { - #if DEBUG >= 5 - fprintf(stderr, "Received AFM3 command message\n"); - #endif - if (next.data[0] != 0){ - next.data = next.data.substr(1); - amf3data = AMF::parse3(next.data); - #if DEBUG >= 5 - amf3data.Print(); - #endif - }else{ - #if DEBUG >= 5 - fprintf(stderr, "Received AFM3-0 command message\n"); - #endif - next.data = next.data.substr(1); - amfdata = AMF::parse(next.data); - parseAMFCommand(amfdata, 17, next.msg_stream_id); - } //parsing AMF0-style - } - break; - case 19: - #if DEBUG >= 5 - fprintf(stderr, "Received AFM0 shared object\n"); - #endif - break; - case 20: { //AMF0 command message - amfdata = AMF::parse(next.data); - parseAMFCommand(amfdata, 20, next.msg_stream_id); - } - break; - case 22: - #if DEBUG >= 5 - fprintf(stderr, "Received aggregate message\n"); - #endif - break; - default: - #if DEBUG >= 1 - fprintf(stderr, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.\n"); - #endif - stopParsing = true; - break; - } - } - } //parseChunk - - ///\brief Main function for the RTMP Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int rtmpConnector(Socket::Connection & conn){ - Socket = conn; - Socket.setBlocking(false); - FLV::Tag tag, init_tag; - DTSC::Stream Strm; - - while ( !Socket.Received().available(1537) && Socket.connected()){ - Socket.spool(); - Util::sleep(5); - } - RTMPStream::handshake_in = Socket.Received().remove(1537); - RTMPStream::rec_cnt += 1537; - - if (RTMPStream::doHandshake()){ - Socket.SendNow(RTMPStream::handshake_out); - while ( !Socket.Received().available(1536) && Socket.connected()){ - Socket.spool(); - Util::sleep(5); - } - Socket.Received().remove(1536); - RTMPStream::rec_cnt += 1536; - #if DEBUG >= 5 - fprintf(stderr, "Handshake succcess!\n"); - #endif - }else{ - fprintf(stderr, "RTMP: Handshake fail!\n"); - return 0; - } - - unsigned int lastStats = 0; - bool firsttime = true; - - while (Socket.connected()){ - if (Socket.spool() || firsttime){ - parseChunk(Socket.Received()); - firsttime = false; - }else{ - Util::sleep(1); //sleep 1ms to prevent high CPU usage - } - if (ready4data){ - if ( !inited){ - //we are ready, connect the socket! - ss = Util::Stream::getStream(streamName); - if ( !ss.connected()){ - #if DEBUG >= 1 - fprintf(stderr, "Could not connect to server!\n"); - #endif - Socket.close(); //disconnect user - break; - } - ss.setBlocking(false); - Strm.waitForMeta(ss); - //find first audio and video tracks - for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){ - if (videoID == -1 && (it->second.codec == "H264" || it->second.codec == "H263" || it->second.codec == "VP6")){ - videoID = it->second.trackID; - } - if (audioID == -1 && (it->second.codec == "AAC" || it->second.codec == "MP3")){ - audioID = it->second.trackID; - } - } - //select the tracks and play - std::stringstream cmd; - cmd << "t"; - if (videoID != -1){ - cmd << " " << videoID; - } - if (audioID != -1){ - cmd << " " << audioID; - } - cmd << "\np\n"; - ss.SendNow(cmd.str().c_str()); - inited = true; - } - if (inited && !noStats){ - long long int now = Util::epoch(); - if (now != lastStats){ - lastStats = now; - ss.SendNow(Socket.getStats("RTMP")); - } - } - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - if (playTransaction != -1){ - //send a status reply - AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "onStatus")); //status reply - amfreply.addContent(AMF::Object("", (double)playTransaction)); //same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfreply.addContent(AMF::Object("")); //info - amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); - amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Reset")); - amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing and resetting...")); - amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV")); - amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); - sendCommand(amfreply, playMessageType, playStreamId); - //send streamisrecorded if stream, well, is recorded. - if (Strm.metadata.vod){//isMember("length") && Strm.metadata["length"].asInt() > 0){ - Socket.Send(RTMPStream::SendUSR(4, 1)); //send UCM StreamIsRecorded (4), stream 1 - } - //send streambegin - Socket.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 - //and more reply - amfreply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "onStatus")); //status reply - amfreply.addContent(AMF::Object("", (double)playTransaction)); //same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info - amfreply.addContent(AMF::Object("")); //info - amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); - amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Start")); - amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing!")); - amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV")); - amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); - sendCommand(amfreply, playMessageType, playStreamId); - RTMPStream::chunk_snd_max = 102400; //100KiB - Socket.Send(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1) - //send dunno? - Socket.Send(RTMPStream::SendUSR(32, 1)); //send UCM no clue?, stream 1 - playTransaction = -1; - } - - //sent init data if needed - if ( !streamInited){ - init_tag.DTSCMetaInit(Strm, Strm.metadata.tracks[videoID], Strm.metadata.tracks[audioID]); - if (init_tag.len){ - Socket.SendNow(RTMPStream::SendMedia(init_tag)); - } - if (audioID != -1){ - init_tag.DTSCAudioInit(Strm.metadata.tracks[audioID]); - if (init_tag.len){ - Socket.SendNow(RTMPStream::SendMedia(init_tag)); - } - } - if (videoID != -1){ - init_tag.DTSCVideoInit(Strm.metadata.tracks[videoID]); - if (init_tag.len){ - Socket.SendNow(RTMPStream::SendMedia(init_tag)); - } - } - streamInited = true; - } - //sent a tag - if (tag.DTSCLoader(Strm)){ - if (tag.len){ - Socket.SendNow(RTMPStream::SendMedia(tag)); - #if DEBUG >= 8 - fprintf(stderr, "Sent tag to %i: [%u] %s\n", Socket.getSocket(), tag.tagTime(), tag.tagType().c_str()); - #endif - } - } - } - } - } - } - Socket.close(); - ss.SendNow(Socket.getStats("RTMP").c_str()); - ss.close(); - return 0; - } //Connector_RTMP -} - -///\brief The standard process-spawning main function. -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables the RTMP protocol which is used by Adobe Flash Player."; - capa["deps"] = ""; - capa["url_rel"] = "/play/$"; - capa["codecs"][0u][0u].append("H264"); - capa["codecs"][0u][0u].append("H263"); - capa["codecs"][0u][0u].append("VP6"); - capa["codecs"][0u][1u].append("AAC"); - capa["codecs"][0u][1u].append("MP3"); - capa["methods"][0u]["handler"] = "rtmp"; - capa["methods"][0u]["type"] = "flash/10"; - capa["methods"][0u]["priority"] = 6ll; - conf.addConnectorOptions(1935, capa); - conf.parseArgs(argc, argv); - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - - return conf.serveForkedSocket(Connector_RTMP::rtmpConnector); -} //main diff --git a/src/connectors/conn_ts.cpp b/src/connectors/conn_ts.cpp deleted file mode 100644 index 0809257b..00000000 --- a/src/connectors/conn_ts.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/// \file conn_ts.cpp -/// Contains the main code for the TS Connector - -#include <queue> -#include <string> -#include <iostream> - -#include <cmath> -#include <ctime> -#include <cstdio> -#include <cstdlib> -#include <cstring> -#include <unistd.h> -#include <getopt.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <sys/types.h> - -#include <mist/socket.h> -#include <mist/config.h> -#include <mist/stream.h> -#include <mist/ts_packet.h> //TS support -#include <mist/dtsc.h> //DTSC support -#include <mist/mp4.h> //For initdata conversion -#include <mist/mp4_generic.h> - -///\brief Holds everything unique to the TS Connector -namespace Connector_TS { - std::string streamName; - std::string trackIDs; - - ///\brief Main function for the TS Connector - ///\param conn A socket describing the connection the client. - ///\return The exit code of the connector. - int tsConnector(Socket::Connection & conn){ - std::string ToPack; - TS::Packet PackData; - std::string DTMIData; - int PacketNumber = 0; - long long unsigned int TimeStamp = 0; - unsigned int ThisNaluSize; - char VideoCounter = 0; - char AudioCounter = 0; - bool IsKeyFrame = false; - MP4::AVCC avccbox; - bool haveAvcc = false; - - DTSC::Stream Strm; - bool inited = false; - Socket::Connection ss; - - while (conn.connected()){ - if ( !inited){ - ss = Util::Stream::getStream(streamName); - if ( !ss.connected()){ - #if DEBUG >= 1 - fprintf(stderr, "Could not connect to server!\n"); - #endif - conn.close(); - break; - } - - if(trackIDs == ""){ - std::stringstream tmpTracks; - // no track ids given? Find the first video and first audio track (if available) and use those! - int videoID = -1; - int audioID = -1; - - Strm.waitForMeta(ss); - for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){ - if (audioID == -1 && it->second.codec == "AAC"){ - audioID = it->first; - tmpTracks << " " << it->first; - } - - if (videoID == -1 && it->second.codec == "H264"){ - videoID = it->first; - tmpTracks << " " << it->first; - } - - } // for iterator - trackIDs += tmpTracks.str(); - } // if trackIDs == "" - - std::string cmd = "t " + trackIDs + "\ns 0\np\n"; - ss.SendNow( cmd ); - inited = true; - } - if (ss.spool()){ - while (Strm.parsePacket(ss.Received())){ - - std::stringstream TSBuf; - Socket::Buffer ToPack; - //write PAT and PMT TS packets - if (PacketNumber == 0){ - PackData.DefaultPAT(); - TSBuf.write(PackData.ToString(), 188); - PackData.DefaultPMT(); - TSBuf.write(PackData.ToString(), 188); - PacketNumber += 2; - } - - int PIDno = 0; - char * ContCounter = 0; - if (Strm.lastType() == DTSC::VIDEO){ - if ( !haveAvcc){ - avccbox.setPayload(Strm.metadata.tracks[Strm.getPacket()["trackid"].asInt()].init); - haveAvcc = true; - } - - IsKeyFrame = Strm.getPacket().isMember("keyframe"); - if (IsKeyFrame){ - TimeStamp = (Strm.getPacket()["time"].asInt() * 27000); - } - ToPack.append(avccbox.asAnnexB()); - while (Strm.lastData().size() > 4){ - ThisNaluSize = (Strm.lastData()[0] << 24) + (Strm.lastData()[1] << 16) + (Strm.lastData()[2] << 8) + Strm.lastData()[3]; - Strm.lastData().replace(0, 4, "\000\000\000\001", 4); - if (ThisNaluSize + 4 == Strm.lastData().size()){ - ToPack.append(Strm.lastData()); - break; - }else{ - ToPack.append(Strm.lastData().c_str(), ThisNaluSize + 4); - Strm.lastData().erase(0, ThisNaluSize + 4); - } - } - ToPack.prepend(TS::Packet::getPESVideoLeadIn(0ul, Strm.getPacket()["time"].asInt() * 90)); - PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt(); - ContCounter = &VideoCounter; - }else if (Strm.lastType() == DTSC::AUDIO){ - ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.metadata.tracks[Strm.getPacket()["trackid"].asInt()].init)); - ToPack.append(Strm.lastData()); - ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket()["time"].asInt() * 90)); - PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt(); - ContCounter = &AudioCounter; - IsKeyFrame = false; - } - - //initial packet - PackData.Clear(); - PackData.PID(PIDno); - PackData.ContinuityCounter(( *ContCounter)++); - PackData.UnitStart(1); - if (IsKeyFrame){ - PackData.RandomAccess(1); - PackData.PCR(TimeStamp); - } - unsigned int toSend = PackData.AddStuffing(ToPack.bytes(184)); - std::string gonnaSend = ToPack.remove(toSend); - PackData.FillFree(gonnaSend); - TSBuf.write(PackData.ToString(), 188); - PacketNumber++; - - //rest of packets - while (ToPack.size()){ - PackData.Clear(); - PackData.PID(PIDno); - PackData.ContinuityCounter(( *ContCounter)++); - toSend = PackData.AddStuffing(ToPack.bytes(184)); - gonnaSend = ToPack.remove(toSend); - PackData.FillFree(gonnaSend); - TSBuf.write(PackData.ToString(), 188); - PacketNumber++; - } - - TSBuf.flush(); - if (TSBuf.str().size()){ - conn.SendNow(TSBuf.str().c_str(), TSBuf.str().size()); - TSBuf.str(""); - } - TSBuf.str(""); - PacketNumber = 0; - } - }else{ - Util::sleep(1000); - conn.spool(); - } - } - return 0; - } -} - -int main(int argc, char ** argv){ - Util::Config conf(argv[0], PACKAGE_VERSION); - JSON::Value capa; - capa["desc"] = "Enables the raw MPEG Transport Stream protocol over TCP."; - capa["deps"] = ""; - capa["required"]["streamname"]["name"] = "Stream"; - capa["required"]["streamname"]["help"] = "What streamname to serve. For multiple streams, add this protocol multiple times using different ports."; - capa["required"]["streamname"]["type"] = "str"; - capa["required"]["streamname"]["option"] = "--stream"; - capa["optional"]["tracks"]["name"] = "Tracks"; - capa["optional"]["tracks"]["help"] = "The track IDs of the stream that this connector will transmit separated by spaces"; - capa["optional"]["tracks"]["type"] = "str"; - capa["optional"]["tracks"]["option"] = "--tracks"; - conf.addOption("streamname", - JSON::fromString("{\"arg\":\"string\",\"short\":\"s\",\"long\":\"stream\",\"help\":\"The name of the stream that this connector will transmit.\"}")); - conf.addOption("tracks", - JSON::fromString("{\"arg\":\"string\",\"value\":[\"\"],\"short\": \"t\",\"long\":\"tracks\",\"help\":\"The track IDs of the stream that this connector will transmit separated by spaces.\"}")); - conf.addConnectorOptions(8888, capa); - bool ret = conf.parseArgs(argc, argv); - if (conf.getBool("json")){ - std::cout << capa.toString() << std::endl; - return -1; - } - if (!ret){ - std::cerr << "Usage error: missing argument(s)." << std::endl; - conf.printHelp(std::cout); - return 1; - } - - Connector_TS::streamName = conf.getString("streamname"); - Connector_TS::trackIDs = conf.getString("tracks"); - return conf.serveForkedSocket(Connector_TS::tsConnector); -} //main diff --git a/src/controller/controller.cpp b/src/controller/controller.cpp index bea98ceb..d36bf76b 100644 --- a/src/controller/controller.cpp +++ b/src/controller/controller.cpp @@ -20,8 +20,14 @@ #include "controller_connectors.h" #include "controller_streams.h" #include "controller_capabilities.h" +#include "controller_statistics.h" #include "server.html.h" + +#include <mist/tinythread.h> +#include <mist/shared_memory.h> + + #define UPLINK_INTERVAL 30 #ifndef COMPILED_USERNAME @@ -31,7 +37,8 @@ ///\brief Holds everything unique to the controller. namespace Controller { - + Util::Config conf; + Secure::Auth keychecker; ///< Checks key authorization. ///\brief A class storing information about a connected user. @@ -133,42 +140,23 @@ namespace Controller { out = in; } - ///\brief Parse received statistics. - ///\param stats The statistics to be parsed. - void CheckStats(JSON::Value & stats){ - long long int currTime = Util::epoch(); - for (JSON::ObjIter jit = stats.ObjBegin(); jit != stats.ObjEnd(); jit++){ - if (currTime - lastBuffer[jit->first] > 120){ - stats.removeMember(jit->first); - return; - }else{ - if (jit->second.isMember("curr") && jit->second["curr"].size() > 0){ - for (JSON::ObjIter u_it = jit->second["curr"].ObjBegin(); u_it != jit->second["curr"].ObjEnd(); ++u_it){ - if (u_it->second.isMember("now") && u_it->second["now"].asInt() < currTime - 3){ - jit->second["log"].append(u_it->second); - jit->second["curr"].removeMember(u_it->first); - if ( !jit->second["curr"].size()){ - break; - } - u_it = jit->second["curr"].ObjBegin(); - } - } - } - } - } - } } //Controller namespace /// the following function is a simple check if the user wants to proceed to fix (y), ignore (n) or abort on (a) a question -char yna(std::string user_input){ - if(user_input == "y" || user_input == "Y"){ - return 'y'; - }else if(user_input == "n" || user_input == "N"){ - return 'n'; - }else if(user_input == "a" || user_input == "A"){ - return 'a'; - }else{ - return 'x';//when no valid option is found, yna returns x +char yna(std::string & user_input){ + switch (user_input[0]){ + case 'y': case 'Y': + return 'y'; + break; + case 'n': case 'N': + return 'n'; + break; + case 'a': case 'A': + return 'a'; + break; + default: + return 'x'; + break; } } @@ -210,37 +198,37 @@ int main(int argc, char ** argv){ if ( !stored_user["default"]){ stored_user["default"] = "root"; } - Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION " / " RELEASE); - conf.addOption("listen_port", stored_port); - conf.addOption("listen_interface", stored_interface); - conf.addOption("username", stored_user); - conf.addOption("daemonize", + Controller::conf = Util::Config(argv[0], PACKAGE_VERSION " / " RELEASE); + Controller::conf.addOption("listen_port", stored_port); + Controller::conf.addOption("listen_interface", stored_interface); + Controller::conf.addOption("username", stored_user); + Controller::conf.addOption("daemonize", JSON::fromString( "{\"long\":\"daemon\", \"short\":\"d\", \"default\":0, \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Turns deamon mode on (-d) or off (-n). -d runs quietly in background, -n (default) enables verbose in foreground.\"}")); - conf.addOption("account", + Controller::conf.addOption("account", JSON::fromString( "{\"long\":\"account\", \"short\":\"a\", \"arg\":\"string\" \"default\":\"\", \"help\":\"A username:password string to create a new account with.\"}")); - conf.addOption("logfile", + Controller::conf.addOption("logfile", JSON::fromString( "{\"long\":\"logfile\", \"short\":\"L\", \"arg\":\"string\" \"default\":\"\",\"help\":\"Redirect all standard output to a log file, provided with an argument\"}")); - conf.addOption("configFile", + Controller::conf.addOption("configFile", JSON::fromString( "{\"long\":\"config\", \"short\":\"c\", \"arg\":\"string\" \"default\":\"config.json\", \"help\":\"Specify a config file other than default.\"}")); - conf.addOption("uplink", + Controller::conf.addOption("uplink", JSON::fromString( "{\"default\":\"\", \"arg\":\"string\", \"help\":\"MistSteward uplink host and port.\", \"short\":\"U\", \"long\":\"uplink\"}")); - conf.addOption("uplink-name", + Controller::conf.addOption("uplink-name", JSON::fromString( "{\"default\":\"" COMPILED_USERNAME "\", \"arg\":\"string\", \"help\":\"MistSteward uplink username.\", \"short\":\"N\", \"long\":\"uplink-name\"}")); - conf.addOption("uplink-pass", + Controller::conf.addOption("uplink-pass", JSON::fromString( "{\"default\":\"" COMPILED_PASSWORD "\", \"arg\":\"string\", \"help\":\"MistSteward uplink password.\", \"short\":\"P\", \"long\":\"uplink-pass\"}")); - conf.parseArgs(argc, argv); - if(conf.getString("logfile")!= ""){ + Controller::conf.parseArgs(argc, argv); + if(Controller::conf.getString("logfile")!= ""){ //open logfile, dup stdout to logfile - int output = open(conf.getString("logfile").c_str(),O_APPEND|O_CREAT|O_WRONLY,S_IRWXU); + int output = open(Controller::conf.getString("logfile").c_str(),O_APPEND|O_CREAT|O_WRONLY,S_IRWXU); if(output < 0){ - DEBUG_MSG(DLVL_ERROR, "Could not redirect output to %s: %s",conf.getString("logfile").c_str(),strerror(errno)); + DEBUG_MSG(DLVL_ERROR, "Could not redirect output to %s: %s",Controller::conf.getString("logfile").c_str(),strerror(errno)); return 7; }else{ dup2(output,STDOUT_FILENO); @@ -255,27 +243,25 @@ int main(int argc, char ** argv){ } } //Input custom config here - Controller::Storage = JSON::fromFile(conf.getString("configFile")); + Controller::Storage = JSON::fromFile(Controller::conf.getString("configFile")); //check for port, interface and username in arguments //if they are not there, take them from config file, if there - if (conf.getOption("listen_port", true).size() <= 1){ + if (Controller::conf.getOption("listen_port", true).size() <= 1){ if (Controller::Storage["config"]["controller"]["port"]){ - conf.getOption("listen_port") = Controller::Storage["config"]["controller"]["port"]; + Controller::conf.getOption("listen_port") = Controller::Storage["config"]["controller"]["port"]; } } - if (conf.getOption("listen_interface", true).size() <= 1){ + if (Controller::conf.getOption("listen_interface", true).size() <= 1){ if (Controller::Storage["config"]["controller"]["interface"]){ - conf.getOption("listen_interface") = Controller::Storage["config"]["controller"]["interface"]; + Controller::conf.getOption("listen_interface") = Controller::Storage["config"]["controller"]["interface"]; } } - if (conf.getOption("username", true).size() <= 1){ + if (Controller::conf.getOption("username", true).size() <= 1){ if (Controller::Storage["config"]["controller"]["username"]){ - conf.getOption("username") = Controller::Storage["config"]["controller"]["username"]; + Controller::conf.getOption("username") = Controller::Storage["config"]["controller"]["username"]; } } - - JSON::Value capabilities; //list available protocols and report about them std::deque<std::string> execs; @@ -284,6 +270,10 @@ int main(int argc, char ** argv){ char const * conn_args[] = {0, "-j", 0}; for (std::deque<std::string>::iterator it = execs.begin(); it != execs.end(); it++){ if ((*it).substr(0, 8) == "MistConn"){ + //skip if an MistOut already existed - MistOut takes precedence! + if (capabilities["connectors"].isMember((*it).substr(8))){ + continue; + } arg_one = Util::getMyPath() + (*it); conn_args[0] = arg_one.c_str(); capabilities["connectors"][(*it).substr(8)] = JSON::fromString(Util::Procs::getOutputOf((char**)conn_args)); @@ -291,9 +281,17 @@ int main(int argc, char ** argv){ capabilities["connectors"].removeMember((*it).substr(8)); } } + if ((*it).substr(0, 7) == "MistOut"){ + arg_one = Util::getMyPath() + (*it); + conn_args[0] = arg_one.c_str(); + capabilities["connectors"][(*it).substr(7)] = JSON::fromString(Util::Procs::getOutputOf((char**)conn_args)); + if (capabilities["connectors"][(*it).substr(7)].size() < 1){ + capabilities["connectors"].removeMember((*it).substr(7)); + } + } } - createAccount(conf.getString("account")); + createAccount(Controller::conf.getString("account")); /// User friendliness input added at this line if (isatty(fileno(stdin))){ @@ -340,11 +338,11 @@ int main(int argc, char ** argv){ } //check for streams if ( !Controller::Storage.isMember("streams") || Controller::Storage["streams"].size() < 1){ - std::cerr << "No streams configured, remember to set up streams through local settings page on port " << conf.getInteger("listen_port") << " or using the API." << std::endl; + std::cerr << "No streams configured, remember to set up streams through local settings page on port " << Controller::conf.getInteger("listen_port") << " or using the API." << std::endl; } } - std::string uplink_addr = conf.getString("uplink"); + std::string uplink_addr = Controller::conf.getString("uplink"); std::string uplink_host = ""; int uplink_port = 0; if (uplink_addr.size() > 0){ @@ -359,7 +357,7 @@ int main(int argc, char ** argv){ time_t lastuplink = 0; time_t processchecker = 0; - Socket::Server API_Socket = Socket::Server(conf.getInteger("listen_port"), conf.getString("listen_interface"), true); + Socket::Server API_Socket = Socket::Server(Controller::conf.getInteger("listen_port"), Controller::conf.getString("listen_interface"), true); Socket::Server Stats_Socket = Socket::Server(Util::getTmpFolder() + "statistics", true); Socket::Connection Incoming; std::vector<Controller::ConnectedUser> users; @@ -369,19 +367,21 @@ int main(int argc, char ** argv){ std::string jsonp; Controller::ConnectedUser * uplink = 0; Controller::Log("CONF", "Controller started"); - conf.activate(); - + Controller::conf.activate(); + //Create a converter class and automatically load in all encoders. Converter::Converter myConverter; - while (API_Socket.connected() && conf.is_active){ + tthread::thread statsThread(Controller::SharedMemStats, &Controller::conf); + + while (API_Socket.connected() && Controller::conf.is_active){ Util::sleep(10);//sleep for 10 ms - prevents 100% CPU time + - if (Util::epoch() - processchecker > 10){ + if (Util::epoch() - processchecker > 5){ processchecker = Util::epoch(); Controller::CheckProtocols(Controller::Storage["config"]["protocols"], capabilities); Controller::CheckAllStreams(Controller::Storage["streams"]); - Controller::CheckStats(Controller::Storage["statistics"]); myConverter.updateStatus(); } if (uplink_port && Util::epoch() - lastuplink > UPLINK_INTERVAL){ @@ -414,7 +414,8 @@ int main(int argc, char ** argv){ Response["config"] = Controller::Storage["config"]; Response["streams"] = Controller::Storage["streams"]; Response["log"] = Controller::Storage["log"]; - Response["statistics"] = Controller::Storage["statistics"]; + /// \todo Put this back in, someway, somehow... + //Response["statistics"] = Controller::Storage["statistics"]; Response["now"] = (unsigned int)lastuplink; uplink->H.Clean(); uplink->H.SetBody("command=" + HTTP::Parser::urlencode(Response.toString())); @@ -431,93 +432,6 @@ int main(int argc, char ** argv){ if (Incoming.connected()){ users.push_back((Controller::ConnectedUser)Incoming); } - Incoming = Stats_Socket.accept(true); - if (Incoming.connected()){ - buffers.push_back(Incoming); - } - if (buffers.size() > 0){ - for (std::vector<Socket::Connection>::iterator it = buffers.begin(); it != buffers.end(); it++){ - if ( !it->connected()){ - it->close(); - buffers.erase(it); - break; - } - if (it->spool()){ - while (it->Received().size()){ - it->Received().get().resize(it->Received().get().size() - 1); - Request = JSON::fromString(it->Received().get()); - it->Received().get().clear(); - if (Request.isMember("buffer")){ - std::string thisbuffer = Request["buffer"]; - Controller::lastBuffer[thisbuffer] = Util::epoch(); - //if metadata is available, store it - if (Request.isMember("meta")){ - Controller::Storage["streams"][thisbuffer]["meta"] = Request["meta"]; - } - if (Controller::Storage["streams"][thisbuffer].isMember("updated")){ - Controller::Storage["streams"][thisbuffer].removeMember("updated"); - if (Controller::Storage["streams"][thisbuffer].isMember("cut")){ - it->SendNow("c"+Controller::Storage["streams"][thisbuffer]["cut"].asString()+"\n"); - }else{ - it->SendNow("c0\n"); - } - if (Controller::Storage["streams"][thisbuffer].isMember("DVR")){ - it->SendNow("d"+Controller::Storage["streams"][thisbuffer]["DVR"].asString()+"\n"); - }else{ - it->SendNow("d20000\n"); - } - if (Controller::Storage["streams"][thisbuffer].isMember("source") && Controller::Storage["streams"][thisbuffer]["source"].asStringRef().substr(0, 7) == "push://"){ - it->SendNow("s"+Controller::Storage["streams"][thisbuffer]["source"].asStringRef().substr(7)+"\n"); - }else{ - it->SendNow("s127.0.01\n"); - } - } - if (Request.isMember("totals")){ - Controller::Storage["statistics"][thisbuffer]["curr"] = Request["curr"]; - std::string nowstr = Request["totals"]["now"].asString(); - Controller::Storage["statistics"][thisbuffer]["totals"][nowstr] = Request["totals"]; - Controller::Storage["statistics"][thisbuffer]["totals"][nowstr].removeMember("now"); - Controller::Storage["statistics"][thisbuffer]["totals"].shrink(600); //limit to 10 minutes of data - for (JSON::ObjIter jit = Request["log"].ObjBegin(); jit != Request["log"].ObjEnd(); jit++){ - Controller::Storage["statistics"][thisbuffer]["log"].append(jit->second); - Controller::Storage["statistics"][thisbuffer]["log"].shrink(1000); //limit to 1000 users per buffer - } - } - } - if (Request.isMember("vod")){ - std::string thisfile = Request["vod"]["filename"]; - for (JSON::ObjIter oit = Controller::Storage["streams"].ObjBegin(); oit != Controller::Storage["streams"].ObjEnd(); ++oit){ - if ((oit->second.isMember("source") && oit->second["source"].asString() == thisfile) - || (oit->second.isMember("channel") && oit->second["channel"]["URL"].asString() == thisfile)){ - Controller::lastBuffer[oit->first] = Util::epoch(); - if (Request["vod"].isMember("meta")){ - Controller::Storage["streams"][oit->first]["meta"] = Request["vod"]["meta"]; - } - JSON::Value sockit = (long long int)it->getSocket(); - std::string nowstr = Request["vod"]["now"].asString(); - Controller::Storage["statistics"][oit->first]["curr"][sockit.asString()] = Request["vod"]; - Controller::Storage["statistics"][oit->first]["curr"][sockit.asString()].removeMember("meta"); - JSON::Value nowtotal; - for (JSON::ObjIter u_it = Controller::Storage["statistics"][oit->first]["curr"].ObjBegin(); - u_it != Controller::Storage["statistics"][oit->first]["curr"].ObjEnd(); ++u_it){ - nowtotal["up"] = nowtotal["up"].asInt() + u_it->second["up"].asInt(); - nowtotal["down"] = nowtotal["down"].asInt() + u_it->second["down"].asInt(); - nowtotal["count"] = nowtotal["count"].asInt() + 1; - } - Controller::Storage["statistics"][oit->first]["totals"][nowstr] = nowtotal; - Controller::Storage["statistics"][oit->first]["totals"].shrink(600); - } - } - } - if (Request.isMember("ctrl_log") && Request["ctrl_log"].size() > 0){ - for (JSON::ArrIter it = Request["ctrl_log"].ArrBegin(); it != Request["ctrl_log"].ArrEnd(); it++){ - Controller::Log((*it)[0u], (*it)[1u]); - } - } - } - } - } - } if (users.size() > 0){ for (std::vector<Controller::ConnectedUser>::iterator it = users.begin(); it != users.end(); it++){ if ( !it->C.connected() || it->logins > 3){ @@ -543,12 +457,13 @@ int main(int argc, char ** argv){ Response["config"] = Controller::Storage["config"]; Response["streams"] = Controller::Storage["streams"]; Response["log"] = Controller::Storage["log"]; - Response["statistics"] = Controller::Storage["statistics"]; - Response["authorize"]["username"] = conf.getString("uplink-name"); + /// \todo Put this back in, someway, somehow... + //Response["statistics"] = Controller::Storage["statistics"]; + Response["authorize"]["username"] = Controller::conf.getString("uplink-name"); Controller::checkCapable(capabilities); Response["capabilities"] = capabilities; Controller::Log("UPLK", "Responding to login challenge: " + Request["authorize"]["challenge"].asString()); - Response["authorize"]["password"] = Secure::md5(conf.getString("uplink-pass") + Request["authorize"]["challenge"].asString()); + Response["authorize"]["password"] = Secure::md5(Controller::conf.getString("uplink-pass") + Request["authorize"]["challenge"].asString()); it->H.Clean(); it->H.SetBody("command=" + HTTP::Parser::urlencode(Response.toString())); it->H.BuildRequest(); @@ -568,7 +483,6 @@ int main(int argc, char ** argv){ } if (Request.isMember("clearstatlogs")){ Controller::Storage["log"].null(); - Controller::Storage["statistics"].null(); } } }else{ @@ -578,8 +492,9 @@ int main(int argc, char ** argv){ it->H.SetHeader("Content-Type", "text/html"); it->H.SetHeader("X-Info", "To force an API response, request the file /api"); it->H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver + "/" RELEASE); - it->H.SetBody(std::string((char*)server_html, (size_t)server_html_len)); - it->C.Send(it->H.BuildResponse("200", "OK")); + it->H.SetHeader("Content-Length", server_html_len); + it->H.SendResponse("200", "OK", it->C); + it->C.SendNow(server_html, server_html_len); it->H.Clean(); }else{ Authorize(Request, Response, ( *it)); @@ -622,10 +537,10 @@ int main(int argc, char ** argv){ } } if (Request.isMember("save")){ - if( Controller::WriteFile(conf.getString("configFile"), Controller::Storage.toString())){ + if( Controller::WriteFile(Controller::conf.getString("configFile"), Controller::Storage.toString())){ Controller::Log("CONF", "Config written to file on request through API"); }else{ - Controller::Log("ERROR", "Config " + conf.getString("configFile") + " could not be written"); + Controller::Log("ERROR", "Config " + Controller::conf.getString("configFile") + " could not be written"); } } //sent current configuration, no matter if it was changed or not @@ -640,11 +555,15 @@ int main(int argc, char ** argv){ } //sent any available logs and statistics Response["log"] = Controller::Storage["log"]; - Response["statistics"] = Controller::Storage["statistics"]; //clear log and statistics if requested if (Request.isMember("clearstatlogs")){ Controller::Storage["log"].null(); - Controller::Storage["statistics"].null(); + } + if (Request.isMember("clients")){ + Controller::fillClients(Request["clients"], Response["clients"]); + } + if (Request.isMember("totals")){ + Controller::fillTotals(Request["totals"], Response["totals"]); } } @@ -657,6 +576,11 @@ int main(int argc, char ** argv){ } it->H.Clean(); it->H.SetHeader("Content-Type", "text/javascript"); + it->H.SetHeader("Access-Control-Allow-Origin", "*"); + it->H.SetHeader("Access-Control-Allow-Methods", "GET, POST"); + it->H.SetHeader("Access-Control-Allow-Headers", "Content-Type, X-Requested-With"); + it->H.SetHeader("Access-Control-Allow-Credentials", "true"); + if (jsonp == ""){ it->H.SetBody(Response.toString() + "\n\n"); }else{ @@ -671,15 +595,17 @@ int main(int argc, char ** argv){ } } } - if (!conf.is_active){ + if (!Controller::conf.is_active){ Controller::Log("CONF", "Controller shutting down because of user request (received shutdown signal)"); } if (!API_Socket.connected()){ Controller::Log("CONF", "Controller shutting down because of socket problem (API port closed)"); } + Controller::conf.is_active = false; API_Socket.close(); - if ( !Controller::WriteFile(conf.getString("configFile"), Controller::Storage.toString())){ - std::cerr << "Error writing config " << conf.getString("configFile") << std::endl; + statsThread.join(); + if ( !Controller::WriteFile(Controller::conf.getString("configFile"), Controller::Storage.toString())){ + std::cerr << "Error writing config " << Controller::conf.getString("configFile") << std::endl; Controller::Storage.removeMember("log"); for (JSON::ObjIter it = Controller::Storage["streams"].ObjBegin(); it != Controller::Storage["streams"].ObjEnd(); it++){ it->second.removeMember("meta"); diff --git a/src/controller/controller_connectors.cpp b/src/controller/controller_connectors.cpp index e6ce4bff..f600f653 100644 --- a/src/controller/controller_connectors.cpp +++ b/src/controller/controller_connectors.cpp @@ -1,6 +1,7 @@ #include <stdio.h> // cout, cerr #include <string> #include <cstring> // strcpy +#include <sys/stat.h> //stat #include <mist/json.h> #include <mist/config.h> #include <mist/procs.h> @@ -55,7 +56,14 @@ namespace Controller { static inline void buildPipedArguments(JSON::Value & p, char * argarr[], JSON::Value & capabilities){ int argnum = 0; static std::string tmparg; - tmparg = Util::getMyPath() + std::string("MistConn") + p["connector"].asStringRef(); + tmparg = Util::getMyPath() + std::string("MistOut") + p["connector"].asStringRef(); + struct stat buf; + if (::stat(tmparg.c_str(), &buf) != 0){ + tmparg = Util::getMyPath() + std::string("MistConn") + p["connector"].asStringRef(); + } + if (::stat(tmparg.c_str(), &buf) != 0){ + return; + } argarr[argnum++] = (char*)tmparg.c_str(); argarr[argnum++] = (char*)"-n"; JSON::Value & pipedCapa = capabilities["connectors"][p["connector"].asStringRef()]; diff --git a/src/controller/controller_statistics.cpp b/src/controller/controller_statistics.cpp new file mode 100644 index 00000000..23848904 --- /dev/null +++ b/src/controller/controller_statistics.cpp @@ -0,0 +1,451 @@ +#include <cstdio> +#include <mist/config.h> +#include "controller_statistics.h" + +/// The STAT_CUTOFF define sets how many seconds of statistics history is kept. +#define STAT_CUTOFF 600 + +// These are used to store "clients" field requests in a bitfield for speedup. +#define STAT_CLI_HOST 1 +#define STAT_CLI_STREAM 2 +#define STAT_CLI_PROTO 4 +#define STAT_CLI_CONNTIME 8 +#define STAT_CLI_POSITION 16 +#define STAT_CLI_DOWN 32 +#define STAT_CLI_UP 64 +#define STAT_CLI_BPS_DOWN 128 +#define STAT_CLI_BPS_UP 256 +#define STAT_CLI_ALL 0xFFFF +// These are used to store "totals" field requests in a bitfield for speedup. +#define STAT_TOT_CLIENTS 1 +#define STAT_TOT_BPS_DOWN 2 +#define STAT_TOT_BPS_UP 4 +#define STAT_TOT_ALL 0xFF + + +std::multimap<unsigned long long int, Controller::statStorage> Controller::oldConns;///<Old connections, sorted on disconnect timestamp +std::map<unsigned long, Controller::statStorage> Controller::curConns;///<Connection storage, sorted on page location. + +/// This function runs as a thread and roughly once per second retrieves +/// statistics from all connected clients, as well as wipes +/// old statistics that have disconnected over 10 minutes ago. +void Controller::SharedMemStats(void * config){ + DEBUG_MSG(DLVL_HIGH, "Starting stats thread"); + IPC::sharedServer statServer("statistics", 88, true); + while(((Util::Config*)config)->is_active){ + //parse current users + statServer.parseEach(parseStatistics); + //wipe old statistics + while (oldConns.size() && oldConns.begin()->first < (unsigned long long)(Util::epoch() - STAT_CUTOFF)){ + oldConns.erase(oldConns.begin()); + } + Util::sleep(1000); + } + DEBUG_MSG(DLVL_HIGH, "Stopping stats thread"); +} + +/// This function is called by parseStatistics. +/// It updates the internally saved statistics data. +void Controller::statStorage::update(IPC::statExchange & data) { + if (streamName == ""){ + host = data.host(); + streamName = data.streamName(); + connector = data.connector(); + } + statLog tmp; + tmp.time = data.time(); + tmp.lastSecond = data.lastSecond(); + tmp.down = data.down(); + tmp.up = data.up(); + log[data.now()] = tmp; + //wipe data older than approx. STAT_CUTOFF seconds + if (log.size() > STAT_CUTOFF){ + log.erase(log.begin()); + } +} + +/// This function is called by the shared memory page that holds statistics. +/// It updates the internally saved statistics data, archiving if neccessary. +void Controller::parseStatistics(char * data, size_t len, unsigned int id){ + IPC::statExchange tmpEx(data); + curConns[id].update(tmpEx); + char counter = (*(data - 1)); + if (counter == 126 || counter == 127 || counter == 254 || counter == 255){ + oldConns.insert(std::pair<unsigned long long int, statStorage>(Util::epoch(), curConns[id])); + curConns.erase(id); + } +} + +/// Returns true if this stream has at least one connected client. +bool Controller::hasViewers(std::string streamName){ + if (curConns.size()){ + for (std::map<unsigned long, statStorage>::iterator it = curConns.begin(); it != curConns.end(); it++){ + if (it->second.streamName == streamName){ + return true; + } + } + } + return false; +} + +/// This takes a "clients" request, and fills in the response data. +/// +/// \api +/// `"client"` requests take the form of: +/// ~~~~~~~~~~~~~~~{.js} +/// { +/// //array of streamnames to accumulate. Empty means all. +/// "streams": ["streama", "streamb", "streamc"], +/// //array of protocols to accumulate. Empty means all. +/// "protocols": ["HLS", "HSS"], +/// //list of requested data fields. Empty means all. +/// "fields": ["host", "stream", "protocol", "conntime", "position", "down", "up", "downbps", "upbps"], +/// //unix timestamp of measuring moment. Negative means X seconds ago. Empty means now. +/// "time": 1234567 +/// } +/// ~~~~~~~~~~~~~~~ +/// and are responded to as: +/// ~~~~~~~~~~~~~~~{.js} +/// { +/// //unix timestamp of data. Always present, always absolute. +/// "time": 1234567, +/// //array of actually represented data fields. +/// "fields": [...] +/// //for all clients, the data in the order they appear in the "fields" field. +/// "data": [[x, y, z], [x, y, z], [x, y, z]] +/// } +/// ~~~~~~~~~~~~~~~ +void Controller::fillClients(JSON::Value & req, JSON::Value & rep){ + //first, figure out the timestamp wanted + long long int reqTime = 0; + if (req.isMember("time")){ + reqTime = req["time"].asInt(); + } + //to make sure no nasty timing business takes place, we store the case "now" as a bool. + bool now = (reqTime == 0); + //add the current time, if negative or zero. + if (reqTime <= 0){ + reqTime += Util::epoch(); + } + //at this point, reqTime is the absolute timestamp. + rep["time"] = reqTime; //fill the absolute timestamp + + unsigned int fields = 0; + //next, figure out the fields wanted + if (req.isMember("fields") && req["fields"].size()){ + for (JSON::ArrIter it = req["fields"].ArrBegin(); it != req["fields"].ArrEnd(); it++){ + if ((*it).asStringRef() == "host"){fields |= STAT_CLI_HOST;} + if ((*it).asStringRef() == "stream"){fields |= STAT_CLI_STREAM;} + if ((*it).asStringRef() == "protocol"){fields |= STAT_CLI_PROTO;} + if ((*it).asStringRef() == "conntime"){fields |= STAT_CLI_CONNTIME;} + if ((*it).asStringRef() == "position"){fields |= STAT_CLI_POSITION;} + if ((*it).asStringRef() == "down"){fields |= STAT_CLI_DOWN;} + if ((*it).asStringRef() == "up"){fields |= STAT_CLI_UP;} + if ((*it).asStringRef() == "downbps"){fields |= STAT_CLI_BPS_DOWN;} + if ((*it).asStringRef() == "upbps"){fields |= STAT_CLI_BPS_UP;} + } + } + //select all, if none selected + if (!fields){fields = STAT_CLI_ALL;} + //figure out what streams are wanted + std::set<std::string> streams; + if (req.isMember("streams") && req["streams"].size()){ + for (JSON::ArrIter it = req["streams"].ArrBegin(); it != req["streams"].ArrEnd(); it++){ + streams.insert((*it).asStringRef()); + } + } + //figure out what protocols are wanted + std::set<std::string> protos; + if (req.isMember("protocols") && req["protocols"].size()){ + for (JSON::ArrIter it = req["protocols"].ArrBegin(); it != req["protocols"].ArrEnd(); it++){ + protos.insert((*it).asStringRef()); + } + } + //output the selected fields + rep["fields"].null(); + if (fields & STAT_CLI_HOST){rep["fields"].append("host");} + if (fields & STAT_CLI_STREAM){rep["fields"].append("stream");} + if (fields & STAT_CLI_PROTO){rep["fields"].append("protocol");} + if (fields & STAT_CLI_CONNTIME){rep["fields"].append("conntime");} + if (fields & STAT_CLI_POSITION){rep["fields"].append("position");} + if (fields & STAT_CLI_DOWN){rep["fields"].append("down");} + if (fields & STAT_CLI_UP){rep["fields"].append("up");} + if (fields & STAT_CLI_BPS_DOWN){rep["fields"].append("downbps");} + if (fields & STAT_CLI_BPS_UP){rep["fields"].append("upbps");} + //output the data itself + rep["data"].null(); + //start with current connections + if (curConns.size()){ + for (std::map<unsigned long, statStorage>::iterator it = curConns.begin(); it != curConns.end(); it++){ + unsigned long long time = reqTime; + if (now){time = it->second.log.rbegin()->first;} + //data present and wanted? insert it! + if ((it->second.log.rbegin()->first >= time && it->second.log.begin()->first <= time) && (!streams.size() || streams.count(it->second.streamName)) && (!protos.size() || protos.count(it->second.connector))){ + JSON::Value d; + std::map<unsigned long long, statLog>::iterator statRef = it->second.log.lower_bound(time); + std::map<unsigned long long, statLog>::iterator prevRef = --(it->second.log.lower_bound(time)); + if (fields & STAT_CLI_HOST){d.append(it->second.host);} + if (fields & STAT_CLI_STREAM){d.append(it->second.streamName);} + if (fields & STAT_CLI_PROTO){d.append(it->second.connector);} + if (fields & STAT_CLI_CONNTIME){d.append((long long)statRef->second.time);} + if (fields & STAT_CLI_POSITION){d.append((long long)statRef->second.lastSecond);} + if (fields & STAT_CLI_DOWN){d.append(statRef->second.down);} + if (fields & STAT_CLI_UP){d.append(statRef->second.up);} + if (fields & STAT_CLI_BPS_DOWN){ + if (statRef != it->second.log.begin()){ + unsigned int diff = statRef->first - prevRef->first; + d.append((statRef->second.down - prevRef->second.down) / diff); + }else{ + d.append(statRef->second.down); + } + } + if (fields & STAT_CLI_BPS_UP){ + if (statRef != it->second.log.begin()){ + unsigned int diff = statRef->first - prevRef->first; + d.append((statRef->second.up - prevRef->second.up) / diff); + }else{ + d.append(statRef->second.up); + } + } + rep["data"].append(d); + } + } + } + //if we're only interested in current, don't even bother looking at history + if (now){ + return; + } + //look at history + if (oldConns.size()){ + for (std::map<unsigned long long int, statStorage>::iterator it = oldConns.begin(); it != oldConns.end(); it++){ + //data present and wanted? insert it! + if ((it->second.log.rbegin()->first >= (unsigned long long)reqTime && it->second.log.begin()->first <= (unsigned long long)reqTime) && (!streams.size() || streams.count(it->second.streamName)) && (!protos.size() || protos.count(it->second.connector))){ + JSON::Value d; + std::map<unsigned long long, statLog>::iterator statRef = it->second.log.lower_bound(reqTime); + std::map<unsigned long long, statLog>::iterator prevRef = --(it->second.log.lower_bound(reqTime)); + if (fields & STAT_CLI_HOST){d.append(it->second.host);} + if (fields & STAT_CLI_STREAM){d.append(it->second.streamName);} + if (fields & STAT_CLI_PROTO){d.append(it->second.connector);} + if (fields & STAT_CLI_CONNTIME){d.append((long long)statRef->second.time);} + if (fields & STAT_CLI_POSITION){d.append((long long)statRef->second.lastSecond);} + if (fields & STAT_CLI_DOWN){d.append(statRef->second.down);} + if (fields & STAT_CLI_UP){d.append(statRef->second.up);} + if (fields & STAT_CLI_BPS_DOWN){ + if (statRef != it->second.log.begin()){ + unsigned int diff = statRef->first - prevRef->first; + d.append((statRef->second.down - prevRef->second.down) / diff); + }else{ + d.append(statRef->second.down); + } + } + if (fields & STAT_CLI_BPS_UP){ + if (statRef != it->second.log.begin()){ + unsigned int diff = statRef->first - prevRef->first; + d.append((statRef->second.up - prevRef->second.up) / diff); + }else{ + d.append(statRef->second.up); + } + } + rep["data"].append(d); + } + } + } + //all done! return is by reference, so no need to return anything here. +} + +class totalsData { + public: + totalsData(){ + clients = 0; + downbps = 0; + upbps = 0; + } + void add(unsigned int down, unsigned int up){ + clients++; + downbps += down; + upbps += up; + } + long long clients; + long long downbps; + long long upbps; +}; + +/// This takes a "totals" request, and fills in the response data. +/// +/// \api +/// `"totals"` requests take the form of: +/// ~~~~~~~~~~~~~~~{.js} +/// { +/// //array of streamnames to accumulate. Empty means all. +/// "streams": ["streama", "streamb", "streamc"], +/// //array of protocols to accumulate. Empty means all. +/// "protocols": ["HLS", "HSS"], +/// //list of requested data fields. Empty means all. +/// "fields": ["clients", "downbps", "upbps"], +/// //unix timestamp of data start. Negative means X seconds ago. Empty means earliest available. +/// "start": 1234567 +/// //unix timestamp of data end. Negative means X seconds ago. Empty means latest available (usually 'now'). +/// "end": 1234567 +/// } +/// ~~~~~~~~~~~~~~~ +/// and are responded to as: +/// ~~~~~~~~~~~~~~~{.js} +/// { +/// //unix timestamp of start of data. Always present, always absolute. +/// "start": 1234567, +/// //unix timestamp of end of data. Always present, always absolute. +/// "end": 1234567, +/// //array of actually represented data fields. +/// "fields": [...] +/// // Time between datapoints. Here: 10 points with each 5 seconds afterwards, followed by 10 points with each 1 second afterwards. +/// "interval": [[10, 5], [10, 1]], +/// //the data for the times as mentioned in the "interval" field, in the order they appear in the "fields" field. +/// "data": [[x, y, z], [x, y, z], [x, y, z]] +/// } +/// ~~~~~~~~~~~~~~~ +void Controller::fillTotals(JSON::Value & req, JSON::Value & rep){ + //first, figure out the timestamps wanted + long long int reqStart = 0; + long long int reqEnd = 0; + if (req.isMember("start")){ + reqStart = req["start"].asInt(); + } + if (req.isMember("end")){ + reqEnd = req["end"].asInt(); + } + //add the current time, if negative or zero. + if (reqStart < 0){ + reqStart += Util::epoch(); + } + if (reqStart == 0){ + reqStart = Util::epoch() - STAT_CUTOFF; + } + if (reqEnd <= 0){ + reqEnd += Util::epoch(); + } + //at this point, reqStart and reqEnd are the absolute timestamp. + + unsigned int fields = 0; + //next, figure out the fields wanted + if (req.isMember("fields") && req["fields"].size()){ + for (JSON::ArrIter it = req["fields"].ArrBegin(); it != req["fields"].ArrEnd(); it++){ + if ((*it).asStringRef() == "clients"){fields |= STAT_TOT_CLIENTS;} + if ((*it).asStringRef() == "downbps"){fields |= STAT_TOT_BPS_DOWN;} + if ((*it).asStringRef() == "upbps"){fields |= STAT_TOT_BPS_UP;} + } + } + //select all, if none selected + if (!fields){fields = STAT_TOT_ALL;} + //figure out what streams are wanted + std::set<std::string> streams; + if (req.isMember("streams") && req["streams"].size()){ + for (JSON::ArrIter it = req["streams"].ArrBegin(); it != req["streams"].ArrEnd(); it++){ + streams.insert((*it).asStringRef()); + } + } + //figure out what protocols are wanted + std::set<std::string> protos; + if (req.isMember("protocols") && req["protocols"].size()){ + for (JSON::ArrIter it = req["protocols"].ArrBegin(); it != req["protocols"].ArrEnd(); it++){ + protos.insert((*it).asStringRef()); + } + } + //output the selected fields + rep["fields"].null(); + if (fields & STAT_TOT_CLIENTS){rep["fields"].append("clients");} + if (fields & STAT_TOT_BPS_DOWN){rep["fields"].append("downbps");} + if (fields & STAT_TOT_BPS_UP){rep["fields"].append("upbps");} + //start data collection + std::map<long long unsigned int, totalsData> totalsCount; + //start with current connections + if (curConns.size()){ + for (std::map<unsigned long, statStorage>::iterator it = curConns.begin(); it != curConns.end(); it++){ + //data present and wanted? insert it! + if (it->second.log.size() > 1 && (it->second.log.rbegin()->first >= (unsigned long long)reqStart || it->second.log.begin()->first <= (unsigned long long)reqEnd) && (!streams.size() || streams.count(it->second.streamName)) && (!protos.size() || protos.count(it->second.connector))){ + //keep track of the previous and current, starting at position 2 so there's always a delta down/up value. + std::map<unsigned long long, statLog>::iterator pi = it->second.log.begin(); + for (std::map<unsigned long long, statLog>::iterator li = ++(it->second.log.begin()); li != it->second.log.end(); li++){ + if (li->first < (unsigned long long)reqStart || pi->first > (unsigned long long)reqEnd){ + continue; + } + unsigned int diff = li->first - pi->first; + unsigned int ddown = (li->second.down - pi->second.down) / diff; + unsigned int dup = (li->second.up - pi->second.up) / diff; + for (long long unsigned int t = pi->first; t < li->first; t++){ + if (t >= (unsigned long long)reqStart && t <= (unsigned long long)reqEnd){ + totalsCount[t].add(ddown, dup); + } + } + pi = li;//set previous iterator to log iterator + } + } + } + } + //look at history + if (oldConns.size()){ + for (std::map<unsigned long long int, statStorage>::iterator it = oldConns.begin(); it != oldConns.end(); it++){ + //data present and wanted? insert it! + if (it->second.log.size() > 1 && (it->second.log.rbegin()->first >= (unsigned long long)reqStart || it->second.log.begin()->first <= (unsigned long long)reqEnd) && (!streams.size() || streams.count(it->second.streamName)) && (!protos.size() || protos.count(it->second.connector))){ + //keep track of the previous and current, starting at position 2 so there's always a delta down/up value. + std::map<unsigned long long, statLog>::iterator pi = it->second.log.begin(); + for (std::map<unsigned long long, statLog>::iterator li = ++(it->second.log.begin()); li != it->second.log.end(); li++){ + if (li->first < (unsigned long long)reqStart || pi->first > (unsigned long long)reqEnd){ + continue; + } + unsigned int diff = li->first - pi->first; + unsigned int ddown = (li->second.down - pi->second.down) / diff; + unsigned int dup = (li->second.up - pi->second.up) / diff; + for (long long unsigned int t = pi->first; t < li->first; t++){ + if (t >= (unsigned long long)reqStart && t <= (unsigned long long)reqEnd){ + totalsCount[t].add(ddown, dup); + } + } + pi = li;//set previous iterator to log iterator + } + } + } + } + //output the data itself + if (!totalsCount.size()){ + //Oh noes! No data. We'll just reply with a bunch of nulls. + rep["start"].null(); + rep["end"].null(); + rep["data"].null(); + rep["interval"].null(); + return; + } + //yay! We have data! + rep["start"] = (long long)totalsCount.begin()->first; + rep["end"] = (long long)totalsCount.rbegin()->first; + rep["data"].null(); + rep["interval"].null(); + long long prevT = 0; + JSON::Value i; + for (std::map<long long unsigned int, totalsData>::iterator it = totalsCount.begin(); it != totalsCount.end(); it++){ + JSON::Value d; + if (fields & STAT_TOT_CLIENTS){d.append(it->second.clients);} + if (fields & STAT_TOT_BPS_DOWN){d.append(it->second.downbps);} + if (fields & STAT_TOT_BPS_UP){d.append(it->second.upbps);} + rep["data"].append(d); + if (prevT){ + if (i.size() < 2){ + i.append(1ll); + i.append((long long)(it->first - prevT)); + }else{ + if (i[1u].asInt() != (long long)(it->first - prevT)){ + rep["interval"].append(i); + i[0u] = 1ll; + i[1u] = (long long)(it->first - prevT); + }else{ + i[0u] = i[0u].asInt() + 1; + } + } + } + prevT = it->first; + } + if (i.size() > 1){ + rep["interval"].append(i); + i.null(); + } + //all done! return is by reference, so no need to return anything here. +} diff --git a/src/controller/controller_statistics.h b/src/controller/controller_statistics.h new file mode 100644 index 00000000..5671fbb5 --- /dev/null +++ b/src/controller/controller_statistics.h @@ -0,0 +1,35 @@ +#include <mist/shared_memory.h> +#include <mist/timing.h> +#include <mist/defines.h> +#include <mist/json.h> +#include <string> +#include <map> + + +namespace Controller { + struct statLog { + long time; + long lastSecond; + long long down; + long long up; + }; + + class statStorage { + public: + void update(IPC::statExchange & data); + std::string host; + std::string streamName; + std::string connector; + std::map<unsigned long long, statLog> log; + }; + + + extern std::multimap<unsigned long long int, statStorage> oldConns; + extern std::map<unsigned long, statStorage> curConns; + void parseStatistics(char * data, size_t len, unsigned int id); + void fillClients(JSON::Value & req, JSON::Value & rep); + void fillTotals(JSON::Value & req, JSON::Value & rep); + void SharedMemStats(void * config); + bool hasViewers(std::string streamName); +} + diff --git a/src/controller/controller_streams.cpp b/src/controller/controller_streams.cpp index 1dd73f07..ccceb5ea 100644 --- a/src/controller/controller_streams.cpp +++ b/src/controller/controller_streams.cpp @@ -3,16 +3,17 @@ #include <mist/timing.h> #include <mist/stream.h> #include <mist/dtsc.h> +#include <mist/defines.h> +#include <mist/shared_memory.h> #include "controller_streams.h" #include "controller_storage.h" +#include "controller_statistics.h" #include <sys/stat.h> #include <map> ///\brief Holds everything unique to the controller. namespace Controller { - std::map<std::string, int> lastBuffer; ///< Last moment of contact with all buffers. - ///\brief Checks whether two streams are equal. ///\param one The first stream for the comparison. ///\param two The second stream for the comparison. @@ -43,22 +44,28 @@ namespace Controller { if (data.isMember("source")){ URL = data["source"].asString(); } - std::string buffcmd; if (URL == ""){ Log("STRM", "Error for stream " + name + "! Source parameter missing."); data["error"] = "Stream offline: Missing source parameter!"; return; } - buffcmd = "MistBuffer"; - if (data.isMember("DVR") && data["DVR"].asInt() > 0){ - data["DVR"] = data["DVR"].asInt(); - buffcmd += " -t " + data["DVR"].asString(); - } - buffcmd += " -s " + name; if (URL.substr(0, 4) == "push"){ - std::string pusher = URL.substr(7); - Util::Procs::Start(name, Util::getMyPath() + buffcmd + " " + pusher); - Log("BUFF", "(re)starting stream buffer " + name + " for push data from " + pusher); + if (hasViewers(name)){ + data["meta"].null(); + IPC::sharedPage streamIndex(name,0,false,false); + if (!streamIndex.mapped){ + return; + } + unsigned int i = 0; + JSON::fromDTMI((const unsigned char*)streamIndex.mapped + 8, streamIndex.len - 8, i, data["meta"]); + if (data["meta"].isMember("tracks") && data["meta"]["tracks"].size()){ + for(JSON::ObjIter trackIt = data["meta"]["tracks"].ObjBegin(); trackIt != data["meta"]["tracks"].ObjEnd(); trackIt++){ + trackIt->second.removeMember("fragments"); + trackIt->second.removeMember("keys"); + trackIt->second.removeMember("parts"); + } + } + } }else{ if (URL.substr(0, 1) == "/"){ data.removeMember("error"); @@ -74,6 +81,12 @@ namespace Controller { getMeta = true; data["l_meta"] = (long long)fileinfo.st_mtime; } + if (stat((URL+".dtsh").c_str(), &fileinfo) == 0 && !S_ISDIR(fileinfo.st_mode)){ + if ( !data.isMember("h_meta") || fileinfo.st_mtime != data["h_meta"].asInt()){ + getMeta = true; + data["h_meta"] = (long long)fileinfo.st_mtime; + } + } if ( !getMeta && data.isMember("meta") && data["meta"].isMember("tracks")){ for (JSON::ObjIter trIt = data["meta"]["tracks"].ObjBegin(); trIt != data["meta"]["tracks"].ObjEnd(); trIt++){ if (trIt->second["codec"] == "H264"){ @@ -107,6 +120,9 @@ namespace Controller { getMeta = true; } if (getMeta){ + if ((URL.substr(URL.size() - 5) != ".dtsc") && (stat((URL+".dtsh").c_str(), &fileinfo) != 0)){ + Util::Stream::getStream(name); + } char * tmp_cmd[3] = {0, 0, 0}; std::string mistinfo = Util::getMyPath() + "MistInfo"; tmp_cmd[0] = (char*)mistinfo.c_str(); @@ -127,7 +143,7 @@ namespace Controller { Util::Procs::getOutputOf(tmp_cmd); data.removeMember("meta"); } - if (Util::epoch() - lastBuffer[name] > 5){ + if (!hasViewers(name)){ if ( !data.isMember("error")){ data["error"] = "Available"; } @@ -136,9 +152,11 @@ namespace Controller { data["online"] = 1; } return; //MistPlayer handles VoD + }else{ + /// \todo Implement ffmpeg pulling again? + //Util::Procs::Start(name, "ffmpeg -re -async 2 -i " + URL + " -f flv -", Util::getMyPath() + "MistFLV2DTSC", Util::getMyPath() + buffcmd); + //Log("BUFF", "(re)starting stream buffer " + name + " for ffmpeg data: ffmpeg -re -async 2 -i " + URL + " -f flv -"); } - Util::Procs::Start(name, "ffmpeg -re -async 2 -i " + URL + " -f flv -", Util::getMyPath() + "MistFLV2DTSC", Util::getMyPath() + buffcmd); - Log("BUFF", "(re)starting stream buffer " + name + " for ffmpeg data: ffmpeg -re -async 2 -i " + URL + " -f flv -"); } } @@ -153,7 +171,7 @@ namespace Controller { if (!jit->second.isMember("name")){ jit->second["name"] = jit->first; } - if (currTime - lastBuffer[jit->first] > 5){ + if (!hasViewers(jit->first)){ if (jit->second.isMember("source") && jit->second["source"].asString().substr(0, 1) == "/" && jit->second.isMember("error") && jit->second["error"].asString().substr(0,15) != "Stream offline:"){ jit->second["online"] = 2; @@ -229,11 +247,8 @@ namespace Controller { WriteFile(Util::getTmpFolder() + "streamlist", strlist.toString()); } } - - ///\brief Parse a given stream configuration. - ///\param in The requested configuration. - ///\param out The new configuration after parsing. - void CheckStreams(JSON::Value & in, JSON::Value & out){ + + void AddStreams(JSON::Value & in, JSON::Value & out){ //check for new streams and updates for (JSON::ObjIter jit = in.ObjBegin(); jit != in.ObjEnd(); jit++){ if (out.isMember(jit->first)){ @@ -263,6 +278,14 @@ namespace Controller { startStream(jit->first, out[jit->first]); } } + } + + ///\brief Parse a given stream configuration. + ///\param in The requested configuration. + ///\param out The new configuration after parsing. + void CheckStreams(JSON::Value & in, JSON::Value & out){ + //check for new streams and updates + AddStreams(in, out); //check for deleted streams std::set<std::string> toDelete; diff --git a/src/controller/controller_streams.h b/src/controller/controller_streams.h index fe763960..48e90ef6 100644 --- a/src/controller/controller_streams.h +++ b/src/controller/controller_streams.h @@ -1,13 +1,12 @@ #include <mist/json.h> namespace Controller { - extern std::map<std::string, int> lastBuffer; ///< Last moment of contact with all buffers. - bool streamsEqual(JSON::Value & one, JSON::Value & two); void startStream(std::string name, JSON::Value & data); void CheckAllStreams(JSON::Value & data); void CheckStreams(JSON::Value & in, JSON::Value & out); - + void AddStreams(JSON::Value & in, JSON::Value & out); + struct liveCheck { long long int lastms; long long int last_active; diff --git a/src/converters/dtsc2srt.cpp b/src/converters/dtsc2srt.cpp index 82eb8a37..8a92f480 100644 --- a/src/converters/dtsc2srt.cpp +++ b/src/converters/dtsc2srt.cpp @@ -19,19 +19,21 @@ namespace Converters { int curIndex = 1; F.parseNext(); - while ( !F.getJSON().isNull()){ + std::string tmp; + while (F.getPacket()){ std::cout << curIndex++ << std::endl; - long long unsigned int time = F.getJSON()["time"].asInt(); + long long unsigned int time = F.getPacket().getTime(); std::cout << std::setfill('0') << std::setw(2) << (time / 3600000) << ":"; std::cout << std::setfill('0') << std::setw(2) << ((time % 3600000) / 60000) << ":"; std::cout << std::setfill('0') << std::setw(2) << (((time % 3600000) % 60000) / 1000) << ","; std::cout << std::setfill('0') << std::setw(3) << time % 1000 << " --> "; - time += F.getJSON()["duration"].asInt(); + time += F.getPacket().getInt("duration"); std::cout << std::setfill('0') << std::setw(2) << (time / 3600000) << ":"; std::cout << std::setfill('0') << std::setw(2) << ((time % 3600000) / 60000) << ":"; std::cout << std::setfill('0') << std::setw(2) << (((time % 3600000) % 60000) / 1000) << ","; std::cout << std::setfill('0') << std::setw(3) << time % 1000 << std::endl; - std::cout << F.getJSON()["data"].asString() << std::endl; + F.getPacket().getString("data", tmp); + std::cout << tmp << std::endl; F.parseNext(); } return 0; diff --git a/src/converters/dtscfix.cpp b/src/converters/dtscfix.cpp index 598fa5fc..c24cad66 100644 --- a/src/converters/dtscfix.cpp +++ b/src/converters/dtscfix.cpp @@ -15,7 +15,7 @@ namespace Converters { DTSC::File F(conf.getString("filename")); F.seek_bpos(0); F.parseNext(); - JSON::Value oriheader = F.getJSON(); + JSON::Value oriheader = F.getPacket().toJSON(); DTSC::Meta meta(F.getMeta()); if (meta.isFixed() && !conf.getBool("force")){ @@ -26,9 +26,11 @@ namespace Converters { meta.reset(); int bPos = F.getBytePos(); F.parseNext(); - while ( !F.getJSON().isNull()){ - F.getJSON()["bpos"] = bPos; - meta.update(F.getJSON()); + JSON::Value newPack; + while ( F.getPacket()){ + newPack = F.getPacket().toJSON(); + newPack["bpos"] = bPos; + meta.update(newPack); bPos = F.getBytePos(); F.parseNext(); } diff --git a/src/converters/dtscmerge.cpp b/src/converters/dtscmerge.cpp index b17b2338..41a088db 100644 --- a/src/converters/dtscmerge.cpp +++ b/src/converters/dtscmerge.cpp @@ -128,10 +128,11 @@ namespace Converters { inFiles[sortIt->second.fileName].selectTracks(trackSelector); inFiles[sortIt->second.fileName].seek_time(sortIt->second.keyTime); inFiles[sortIt->second.fileName].seekNext(); - while (inFiles[sortIt->second.fileName].getJSON() && inFiles[sortIt->second.fileName].getBytePos() <= sortIt->second.endBPos && !inFiles[sortIt->second.fileName].reachedEOF()){ - if (inFiles[sortIt->second.fileName].getJSON()["trackid"].asInt() == sortIt->second.trackID){ - inFiles[sortIt->second.fileName].getJSON()["trackid"] = trackMapping[sortIt->second.fileName][sortIt->second.trackID]; - outFile.writePacket(inFiles[sortIt->second.fileName].getJSON()); + while (inFiles[sortIt->second.fileName].getPacket() && inFiles[sortIt->second.fileName].getBytePos() <= sortIt->second.endBPos && !inFiles[sortIt->second.fileName].reachedEOF()){ + if (inFiles[sortIt->second.fileName].getPacket().getTrackId() == sortIt->second.trackID){ + JSON::Value tmp = inFiles[sortIt->second.fileName].getPacket().toJSON(); + tmp["trackid"] = trackMapping[sortIt->second.fileName][sortIt->second.trackID]; + outFile.writePacket(tmp); } inFiles[sortIt->second.fileName].seekNext(); } diff --git a/src/converters/oggconv.cpp b/src/converters/oggconv.cpp index 40d8daea..15fe331f 100644 --- a/src/converters/oggconv.cpp +++ b/src/converters/oggconv.cpp @@ -1,4 +1,4 @@ -#include"oggconv.h" +#include "oggconv.h" #include <stdlib.h> #include <mist/bitstream.h> @@ -11,32 +11,61 @@ namespace OGG{ srand (Util::getMS());//randomising with milliseconds from boot std::vector<unsigned int> curSegTable; //trackInf.clear(); - //Creating ID headers for theora and vorbis + /// \todo This is utter rubbish right now. + /// \todo We shouldn't assume all possible tracks are selected. + /// \todo We shouldn't be buffering, but sending. + /// \todo Especially not in a std::string. (Why, god, why?!) + //Creating headers for ( std::map<int,DTSC::Track>::iterator it = meta.tracks.begin(); it != meta.tracks.end(); it ++) { - curOggPage.clear(); - curOggPage.setVersion(); - curOggPage.setHeaderType(2);//headertype 2 = Begin of Stream - curOggPage.setGranulePosition(0); - trackInf[it->second.trackID].OGGSerial = rand() % 0xFFFFFFFE +1; //initialising on a random not 0 number - curOggPage.setBitstreamSerialNumber(trackInf[it->second.trackID].OGGSerial); - trackInf[it->second.trackID].seqNum = 0; - curOggPage.setPageSequenceNumber(trackInf[it->second.trackID].seqNum++); - curSegTable.clear(); - curSegTable.push_back(it->second.idHeader.size()); - curOggPage.setSegmentTable(curSegTable); - curOggPage.setPayload((char*)it->second.idHeader.c_str(), it->second.idHeader.size()); - curOggPage.setCRCChecksum(curOggPage.calcChecksum()); - //pages.push_back(curOggPage); - parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize()); trackInf[it->second.trackID].codec = it->second.codec; + trackInf[it->second.trackID].OGGSerial = rand() % 0xFFFFFFFE +1; //initialising on a random not 0 number + trackInf[it->second.trackID].seqNum = 0; if (it->second.codec == "theora"){ + curOggPage.clear(); + curOggPage.setVersion(); + curOggPage.setHeaderType(2);//headertype 2 = Begin of Stream + curOggPage.setGranulePosition(0); + curOggPage.setBitstreamSerialNumber(trackInf[it->second.trackID].OGGSerial); + curOggPage.setPageSequenceNumber(trackInf[it->second.trackID].seqNum++); + curSegTable.clear(); + curSegTable.push_back(it->second.idHeader.size()); + curOggPage.setSegmentTable(curSegTable); + curOggPage.setPayload((char*)it->second.idHeader.c_str(), it->second.idHeader.size()); + curOggPage.setCRCChecksum(curOggPage.calcChecksum()); + parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize()); trackInf[it->second.trackID].lastKeyFrame = 1; trackInf[it->second.trackID].sinceKeyFrame = 0; theora::header tempHead; std::string tempString = it->second.idHeader; tempHead.read((char*)tempString.c_str(),42); trackInf[it->second.trackID].significantValue = tempHead.getKFGShift(); + curOggPage.clear(); + curOggPage.setVersion(); + curOggPage.setHeaderType(0);//headertype 0 = normal + curOggPage.setGranulePosition(0); + curOggPage.setBitstreamSerialNumber(trackInf[it->second.trackID].OGGSerial); + curOggPage.setPageSequenceNumber(trackInf[it->second.trackID].seqNum++); + curSegTable.clear(); + curSegTable.push_back(it->second.commentHeader.size()); + curSegTable.push_back(it->second.init.size()); + curOggPage.setSegmentTable(curSegTable); + std::string fullHeader = it->second.commentHeader + it->second.init; + curOggPage.setPayload((char*)fullHeader.c_str(),fullHeader.size()); + curOggPage.setCRCChecksum(curOggPage.calcChecksum()); + parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize()); }else if (it->second.codec == "vorbis"){ + curOggPage.clear(); + curOggPage.setVersion(); + curOggPage.setHeaderType(2);//headertype 2 = Begin of Stream + curOggPage.setGranulePosition(0); + curOggPage.setBitstreamSerialNumber(trackInf[it->second.trackID].OGGSerial); + curOggPage.setPageSequenceNumber(trackInf[it->second.trackID].seqNum++); + curSegTable.clear(); + curSegTable.push_back(it->second.idHeader.size()); + curOggPage.setSegmentTable(curSegTable); + curOggPage.setPayload((char*)it->second.idHeader.c_str(), it->second.idHeader.size()); + curOggPage.setCRCChecksum(curOggPage.calcChecksum()); + parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize()); trackInf[it->second.trackID].lastKeyFrame = 0; trackInf[it->second.trackID].sinceKeyFrame = 0; trackInf[it->second.trackID].prevBlockFlag = -1; @@ -57,27 +86,53 @@ namespace OGG{ tempHead.read((char*)tempString.c_str(),tempString.size()); trackInf[it->second.trackID].vorbisModes = tempHead.readModeDeque(audioChannels); trackInf[it->second.trackID].hadFirst = false; + curOggPage.clear(); + curOggPage.setVersion(); + curOggPage.setHeaderType(0);//headertype 0 = normal + curOggPage.setGranulePosition(0); + curOggPage.setBitstreamSerialNumber(trackInf[it->second.trackID].OGGSerial); + curOggPage.setPageSequenceNumber(trackInf[it->second.trackID].seqNum++); + curSegTable.clear(); + curSegTable.push_back(it->second.commentHeader.size()); + curSegTable.push_back(it->second.init.size()); + curOggPage.setSegmentTable(curSegTable); + std::string fullHeader = it->second.commentHeader + it->second.init; + curOggPage.setPayload((char*)fullHeader.c_str(),fullHeader.size()); + curOggPage.setCRCChecksum(curOggPage.calcChecksum()); + parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize()); + }else if (it->second.codec == "opus"){ + //OpusHead page + curOggPage.clear(); + curOggPage.setVersion(); + curOggPage.setHeaderType(2);//headertype 2 = Begin of Stream + curOggPage.setGranulePosition(0); + curOggPage.setBitstreamSerialNumber(trackInf[it->second.trackID].OGGSerial); + curOggPage.setPageSequenceNumber(trackInf[it->second.trackID].seqNum++); + curSegTable.clear(); + curSegTable.push_back(19); + curOggPage.setSegmentTable(curSegTable); + //version = 1, channels = 2, preskip=0x138, origRate=48k, gain=0, channelmap=0 + //we can safely hard-code these as everything is already overridden elsewhere anyway + // (except preskip - but this seems to be 0x138 for all files, and doesn't hurt much if it's wrong anyway) + curOggPage.setPayload((char*)"OpusHead\001\002\070\001\200\273\000\000\000\000\000", 19); + curOggPage.setCRCChecksum(curOggPage.calcChecksum()); + parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize()); + //end of OpusHead, now moving on to OpusTags + curOggPage.clear(); + curOggPage.setVersion(); + curOggPage.setHeaderType(2);//headertype 2 = Begin of Stream + curOggPage.setGranulePosition(0); + curOggPage.setBitstreamSerialNumber(trackInf[it->second.trackID].OGGSerial); + curOggPage.setPageSequenceNumber(trackInf[it->second.trackID].seqNum++); + curSegTable.clear(); + curSegTable.push_back(26); + curOggPage.setSegmentTable(curSegTable); + //we send an encoder value of "MistServer" and no further tags + curOggPage.setPayload((char*)"OpusTags\012\000\000\000MistServer\000\000\000\000", 26); + curOggPage.setCRCChecksum(curOggPage.calcChecksum()); + parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize()); } } - //Creating remaining headers for theora and vorbis - //for tracks in header - //create standard page with comment (empty) en setup header(init) - for ( std::map<int,DTSC::Track>::iterator it = meta.tracks.begin(); it != meta.tracks.end(); it ++) { - curOggPage.clear(); - curOggPage.setVersion(); - curOggPage.setHeaderType(0);//headertype 0 = normal - curOggPage.setGranulePosition(0); - curOggPage.setBitstreamSerialNumber(trackInf[it->second.trackID].OGGSerial); - curOggPage.setPageSequenceNumber(trackInf[it->second.trackID].seqNum++); - curSegTable.clear(); - curSegTable.push_back(it->second.commentHeader.size()); - curSegTable.push_back(it->second.init.size()); - curOggPage.setSegmentTable(curSegTable); - std::string fullHeader = it->second.commentHeader + it->second.init; - curOggPage.setPayload((char*)fullHeader.c_str(),fullHeader.size()); - curOggPage.setCRCChecksum(curOggPage.calcChecksum()); - parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize()); - } } void converter::readDTSCVector(JSON::Value & DTSCPart, std::string & pageBuffer){ @@ -174,6 +229,8 @@ namespace OGG{ //add to granule position trackInf[DTSCID].lastKeyFrame += curPCMSamples; lastGran = trackInf[DTSCID].lastKeyFrame; + } else if (trackInf[DTSCID].codec == "opus"){ + lastGran = (int)((DTSCPart["time"].asInt() * 48.0) / 120.0 + 0.5) * 120; } //} //last parts of page put out diff --git a/src/input/input.cpp b/src/input/input.cpp new file mode 100644 index 00000000..240790ba --- /dev/null +++ b/src/input/input.cpp @@ -0,0 +1,338 @@ +#include <semaphore.h> +#include <fcntl.h> +#include <sys/stat.h> + +#include <mist/defines.h> +#include "input.h" +#include <sstream> +#include <fstream> +#include <iterator> + +namespace Mist { + Input * Input::singleton = NULL; + + void Input::userCallback(char * data, size_t len, unsigned int id){ + long tid = ((long)(data[0]) << 24) | ((long)(data[1]) << 16) | ((long)(data[2]) << 8) | ((long)(data[3])); + long keyNum = ((long)(data[4]) << 8) | ((long)(data[5])); + bufferFrame(tid, keyNum + 1);//Try buffer next frame + } + + void Input::doNothing(char * data, size_t len, unsigned int id){ + DEBUG_MSG(DLVL_DONTEVEN, "Doing 'nothing'"); + for (int i = 0; i < 5; i++){ + int tmp = ((long)(data[i*6]) << 24) | ((long)(data[i*6 + 1]) << 16) | ((long)(data[i*6 + 2]) << 8) | data[i*6 + 3]; + if (tmp){ + singleton->userCallback(data + (i*6), 6, id);//call the userCallback for this input + } + } + } + + Input::Input(Util::Config * cfg) { + config = cfg; + JSON::Value option; + option["long"] = "json"; + option["short"] = "j"; + option["help"] = "Output MistIn info in JSON format, then exit."; + option["value"].append(0ll); + config->addOption("json", option); + option.null(); + option["arg_num"] = 1ll; + option["arg"] = "string"; + option["help"] = "Name of the input file or - for stdin"; + option["value"].append("-"); + config->addOption("input", option); + option.null(); + option["arg_num"] = 2ll; + option["arg"] = "string"; + option["help"] = "Name of the output file or - for stdout"; + option["value"].append("-"); + config->addOption("output", option); + option.null(); + option["arg"] = "string"; + option["short"] = "s"; + option["long"] = "stream"; + option["help"] = "The name of the stream that this connector will transmit."; + config->addOption("streamname", option); + option.null(); + option["short"] = "p"; + option["long"] = "player"; + option["help"] = "Makes this connector into a player"; + config->addOption("player", option); + + packTime = 0; + lastActive = Util::epoch(); + playing = 0; + playUntil = 0; + + singleton = this; + isBuffer = false; + } + + int Input::run() { + if (config->getBool("json")) { + std::cerr << capa.toString() << std::endl; + return 0; + } + if (!setup()) { + std::cerr << config->getString("cmd") << " setup failed." << std::endl; + return 0; + } + if (!readHeader()) { + std::cerr << "Reading header for " << config->getString("input") << " failed." << std::endl; + return 0; + } + parseHeader(); + + if (!config->getBool("player")){ + //check filename for no - + if (config->getString("output") != "-"){ + //output to dtsc + DTSC::Meta newMeta = myMeta; + newMeta.reset(); + JSON::Value tempVal; + std::ofstream file(config->getString("output").c_str()); + long long int bpos = 0; + seek(0); + getNext(); + while (lastPack){ + tempVal = lastPack.toJSON(); + tempVal["bpos"] = bpos; + newMeta.update(tempVal); + file << std::string(lastPack.getData(), lastPack.getDataLen()); + bpos += lastPack.getDataLen(); + getNext(); + } + //close file + file.close(); + //create header + file.open((config->getString("output")+".dtsh").c_str()); + file << newMeta.toJSON().toNetPacked(); + file.close(); + }else{ + DEBUG_MSG(DLVL_FAIL,"No filename specified, exiting"); + } + }else{ + //after this player functionality + + metaPage.init(config->getString("streamname"), (isBuffer ? 8388608 : myMeta.getSendLen()), true); + myMeta.writeTo(metaPage.mapped); + userPage.init(config->getString("streamname") + "_users", 30, true); + + + if (!isBuffer){ + for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + bufferFrame(it->first, 0); + } + } + + sem_t * waiting = sem_open(std::string("/wait_" + config->getString("streamname")).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 0); + if (waiting == SEM_FAILED){ + DEBUG_MSG(DLVL_FAIL, "Failed to open semaphore - cancelling"); + return -1; + } + sem_post(waiting); + sem_close(waiting); + + DEBUG_MSG(DLVL_HIGH,"Pre-While"); + + long long int activityCounter = Util::getMS(); + while ((Util::getMS() - activityCounter) < 10000){//1minute timeout + DEBUG_MSG(DLVL_HIGH, "Timer running"); + Util::sleep(1000); + removeUnused(); + userPage.parseEach(doNothing); + if (userPage.amount){ + activityCounter = Util::getMS(); + DEBUG_MSG(DLVL_HIGH, "Connected users: %d", userPage.amount); + } + } + DEBUG_MSG(DLVL_DEVEL,"Closing clean"); + //end player functionality + } + return 0; + } + + void Input::removeUnused(){ + for (std::map<unsigned int, std::map<unsigned int, unsigned int> >::iterator it = pageCounter.begin(); it != pageCounter.end(); it++){ + for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){ + it2->second--; + } + bool change = true; + while (change){ + change = false; + for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){ + if (!it2->second){ + DEBUG_MSG(DLVL_DEVEL, "Erasing page %u:%u", it->first, it2->first); + pagesByTrack[it->first].erase(it2->first); + pageCounter[it->first].erase(it2->first); + change = true; + break; + } + } + } + } + } + + void Input::parseHeader(){ + DEBUG_MSG(DLVL_DEVEL,"Parsing the header"); + //Select all tracks for parsing header + selectedTracks.clear(); + std::stringstream trackSpec; + for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { + DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first); + //selectedTracks.insert(it->first); + if (trackSpec.str() != ""){ + trackSpec << " "; + } + trackSpec << it->first; + DEBUG_MSG(DLVL_VERYHIGH, "Trackspec now %s", trackSpec.str().c_str()); + for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++){ + keyTimes[it->first].insert(it2->getTime()); + } + } + trackSelect(trackSpec.str()); + + std::map<int, DTSCPageData> curData; + std::map<int, booking> bookKeeping; + + seek(0); + getNext(); + + while(lastPack){//loop through all + int tid = lastPack.getTrackId(); + if (!bookKeeping.count(tid)){ + bookKeeping[tid].first = 0; + bookKeeping[tid].curPart = 0; + bookKeeping[tid].curKey = 0; + + curData[tid].lastKeyTime = 0xFFFFFFFF; + curData[tid].keyNum = 1; + curData[tid].partNum = 0; + curData[tid].dataSize = 0; + curData[tid].curOffset = 0; + curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime(); + + char tmpId[20]; + sprintf(tmpId, "%d", tid); + indexPages[tid].init(config->getString("streamname") + tmpId, 8192, true);//Pages of 8kb in size, room for 512 parts. + } + if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() == curData[tid].partNum){ + if (curData[tid].dataSize > 8388608) { + pagesByTrack[tid][bookKeeping[tid].first] = curData[tid]; + bookKeeping[tid].first += curData[tid].keyNum; + curData[tid].keyNum = 0; + curData[tid].dataSize = 0; + curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime(); + } + bookKeeping[tid].curKey++; + curData[tid].keyNum++; + curData[tid].partNum = 0; + } + curData[tid].dataSize += lastPack.getDataLen(); + curData[tid].partNum ++; + bookKeeping[tid].curPart ++; + getNext(false); + } + for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { + if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){ + pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first]; + } + if (!pagesByTrack.count(it->first)){ + DEBUG_MSG(DLVL_WARN, "No pages for track %d found", it->first); + }else{ + DEBUG_MSG(DLVL_HIGH, "Track %d (%s) split into %lu pages", it->first, myMeta.tracks[it->first].codec.c_str(), pagesByTrack[it->first].size()); + for (std::map<int, DTSCPageData>::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){ + } + } + } + } + + + bool Input::bufferFrame(int track, int keyNum){ + DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer %d:%d", track, keyNum); + if (!pagesByTrack.count(track)){ + return false; + } + std::map<int, DTSCPageData> ::iterator it = pagesByTrack[track].upper_bound(keyNum); + if (it == pagesByTrack[track].begin()){ + return false; + } + it --; + int pageNum = it->first; + pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use + + if (!dataPages[track].count(pageNum)){ + char pageId[100]; + int pageIdLen = sprintf(pageId, "%s%d_%d", config->getString("streamname").c_str(), track, pageNum); + std::string tmpString(pageId, pageIdLen); + dataPages[track][pageNum].init(tmpString, it->second.dataSize, true); + DEBUG_MSG(DLVL_HIGH, "Buffering page %d through %d / %lu", pageNum, pageNum + it->second.keyNum, myMeta.tracks[track].keys.size()); + + std::stringstream trackSpec; + trackSpec << track; + trackSelect(trackSpec.str()); + }else{ + return true; + } + seek(myMeta.tracks[track].keys[pageNum].getTime()); + long long unsigned int stopTime = myMeta.tracks[track].lastms + 1; + if ((int)myMeta.tracks[track].keys.size() > pageNum + it->second.keyNum){ + stopTime = myMeta.tracks[track].keys[pageNum + it->second.keyNum].getTime(); + } + DEBUG_MSG(DLVL_HIGH, "Playing from %ld to %llu", myMeta.tracks[track].keys[pageNum].getTime(), stopTime); + getNext(); + while (lastPack && lastPack.getTime() < stopTime){ + if (it->second.curOffset + lastPack.getDataLen() > pagesByTrack[track][pageNum].dataSize){ + DEBUG_MSG(DLVL_WARN, "Trying to write %u bytes past the end of page %u/%u", lastPack.getDataLen(), track, pageNum); + return true; + }else{ + memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen()); + it->second.curOffset += lastPack.getDataLen(); + } + getNext(); + } + for (int i = 0; i < indexPages[track].len / 8; i++){ + if (((long long int*)indexPages[track].mapped)[i] == 0){ + ((long long int*)indexPages[track].mapped)[i] = (((long long int)htonl(pageNum)) << 32) | htonl(it->second.keyNum); + break; + } + } + return true; + } + + bool Input::atKeyFrame(){ + static std::map<int, int> lastSeen; + //not in keyTimes? We're not at a keyframe. + unsigned int c = keyTimes[lastPack.getTrackId()].count(lastPack.getTime()); + if (!c){ + return false; + } + //skip double times + if (lastSeen.count(lastPack.getTrackId()) && lastSeen[lastPack.getTrackId()] == lastPack.getTime()){ + return false; + } + //set last seen, and return true + lastSeen[lastPack.getTrackId()] = lastPack.getTime(); + return true; + } + + void Input::play(int until) { + playing = -1; + playUntil = until; + initialTime = 0; + benchMark = Util::getMS(); + } + + void Input::playOnce() { + if (playing <= 0) { + playing = 1; + } + ++playing; + benchMark = Util::getMS(); + } + + void Input::quitPlay() { + playing = 0; + } +} + diff --git a/src/input/input.h b/src/input/input.h new file mode 100644 index 00000000..98425e4a --- /dev/null +++ b/src/input/input.h @@ -0,0 +1,84 @@ +#include <set> +#include <map> +#include <cstdlib> +#include <mist/config.h> +#include <mist/json.h> +#include <mist/timing.h> +#include <mist/dtsc.h> +#include <mist/shared_memory.h> + +namespace Mist { + struct DTSCPageData { + DTSCPageData() : keyNum(0), partNum(0), dataSize(0), curOffset(0), firstTime(0){} + int keyNum;///<The number of keyframes in this page. + int partNum;///<The number of parts in this page. + unsigned long long int dataSize;///<The full size this page should be. + unsigned long long int curOffset;///<The current write offset in the page. + unsigned long long int firstTime;///<The first timestamp of the page. + unsigned long lastKeyTime;///<The last key time encountered on this track. + }; + + struct booking { + int first; + int curKey; + int curPart; + }; + + class Input { + public: + Input(Util::Config * cfg); + int run(); + virtual ~Input() {}; + protected: + static void doNothing(char * data, size_t len, unsigned int id); + virtual bool setup() = 0; + virtual bool readHeader() = 0; + virtual bool atKeyFrame(); + virtual void getNext(bool smart = true) {}; + virtual void seek(int seekTime){}; + void play(int until = 0); + void playOnce(); + void quitPlay(); + virtual void removeUnused(); + virtual void trackSelect(std::string trackSpec){}; + virtual void userCallback(char * data, size_t len, unsigned int id); + + void parseHeader(); + bool bufferFrame(int track, int keyNum); + + unsigned int packTime;///Media-timestamp of the last packet. + int lastActive;///Timestamp of the last time we received or sent something. + int initialTime; + int playing; + unsigned int playUntil; + unsigned int benchMark; + std::set<int> selectedTracks; + + bool isBuffer; + + Util::Config * config; + JSON::Value capa; + Socket::Connection StatsSocket; + DTSC::Meta myMeta; + DTSC::Packet lastPack; + + std::map<int,std::set<int> > keyTimes; + IPC::sharedPage metaPage; + //Create server for user pages + IPC::sharedServer userPage; + + + //TrackIndex pages + std::map<int, IPC::sharedPage> indexPages; + std::map<int, std::map<int, IPC::sharedPage> > dataPages; + + //Page Overview + std::map<int, std::map<int, DTSCPageData> > pagesByTrack; + + std::map<unsigned int, std::map<unsigned int, unsigned int> > pageCounter; + + static Input * singleton; + }; + +} + diff --git a/src/input/input_buffer.cpp b/src/input/input_buffer.cpp new file mode 100644 index 00000000..a0f33115 --- /dev/null +++ b/src/input/input_buffer.cpp @@ -0,0 +1,274 @@ +#include <iostream> +#include <cstring> +#include <cerrno> +#include <cstdlib> +#include <cstdio> +#include <string> +#include <mist/stream.h> +#include <mist/defines.h> + +#include "input_buffer.h" + +namespace Mist { + inputBuffer::inputBuffer(Util::Config * cfg) : Input(cfg) { + JSON::Value option; + option["arg"] = "integer"; + option["long"] = "buffer"; + option["short"] = "b"; + option["help"] = "Buffertime for this stream."; + option["value"].append(30000LL); + config->addOption("bufferTime", option); + + capa["desc"] = "Enables buffered live input"; + capa["codecs"][0u][0u].append("*"); + capa["codecs"][0u][1u].append("*"); + capa["codecs"][0u][2u].append("*"); + capa["codecs"][0u][3u].append("*"); + capa["codecs"][0u][4u].append("*"); + capa["codecs"][0u][5u].append("*"); + capa["codecs"][0u][6u].append("*"); + capa["codecs"][0u][7u].append("*"); + capa["codecs"][0u][8u].append("*"); + capa["codecs"][0u][9u].append("*"); + DEBUG_MSG(DLVL_DEVEL, "Started MistInBuffer"); + isBuffer = true; + singleton = this; + bufferTime = 0; + cutTime = 0; + + } + + void inputBuffer::updateMeta(){ + long long unsigned int firstms = 0xFFFFFFFFFFFFFFFF; + long long unsigned int lastms = 0; + for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + if (it->second.firstms < firstms){ + firstms = it->second.firstms; + } + if (it->second.firstms > lastms){ + lastms = it->second.lastms; + } + } + myMeta.bufferWindow = lastms - firstms; + myMeta.writeTo(metaPage.mapped); + } + + bool inputBuffer::removeKey(unsigned int tid){ + if (myMeta.tracks[tid].keys.size() < 2 || myMeta.tracks[tid].fragments.size() < 2){ + return false; + } + DEBUG_MSG(DLVL_HIGH, "Erasing key %d:%d", tid, myMeta.tracks[tid].keys[0].getNumber()); + //remove all parts of this key + for (int i = 0; i < myMeta.tracks[tid].keys[0].getParts(); i++){ + myMeta.tracks[tid].parts.pop_front(); + } + //remove the key itself + myMeta.tracks[tid].keys.pop_front(); + //re-calculate firstms + myMeta.tracks[tid].firstms = myMeta.tracks[tid].keys[0].getTime(); + //delete the fragment if it's no longer fully buffered + if (myMeta.tracks[tid].fragments[0].getNumber() < myMeta.tracks[tid].keys[0].getNumber()){ + myMeta.tracks[tid].fragments.pop_front(); + myMeta.tracks[tid].missedFrags ++; + } + //if there is more than one page buffered for this track... + if (inputLoc[tid].size() > 1){ + //Check if the first key starts on the second page or higher + if (myMeta.tracks[tid].keys[0].getNumber() >= (++(inputLoc[tid].begin()))->first){ + //Find page in indexpage and null it + for (int i = 0; i < 8192; i += 8){ + int thisKeyNum = ((((long long int *)(indexPages[tid].mapped + i))[0]) >> 32) & 0xFFFFFFFF; + if (thisKeyNum == htonl(pagesByTrack[tid].begin()->first) && ((((long long int *)(indexPages[tid].mapped + i))[0]) != 0)){ + (((long long int *)(indexPages[tid].mapped + i))[0]) = 0; + } + } + DEBUG_MSG(DLVL_DEVEL, "Erasing track %d, keys %lu-%lu from buffer", tid, inputLoc[tid].begin()->first, inputLoc[tid].begin()->first + inputLoc[tid].begin()->second.keyNum - 1); + inputLoc[tid].erase(inputLoc[tid].begin()); + dataPages[tid].erase(dataPages[tid].begin()); + }else{ + DEBUG_MSG(DLVL_HIGH, "%d still on first page (%lu - %lu)", myMeta.tracks[tid].keys[0].getNumber(), inputLoc[tid].begin()->first, inputLoc[tid].begin()->first + inputLoc[tid].begin()->second.keyNum - 1); + } + } + return true; + } + + void inputBuffer::removeUnused(){ + //find the earliest video keyframe stored + unsigned int firstVideo = 1; + for(std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + if (it->second.type == "video"){ + if (it->second.firstms < firstVideo || firstVideo == 1){ + firstVideo = it->second.firstms; + } + } + } + for(std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + //non-video tracks need to have a second keyframe that is <= firstVideo + if (it->second.type != "video"){ + if (it->second.keys.size() < 2 || it->second.keys[1].getTime() > firstVideo){ + continue; + } + } + //Buffer cutting + while(it->second.keys.size() > 1 && it->second.keys[0].getTime() < cutTime){ + if (!removeKey(it->first)){break;} + } + //Buffer size management + while(it->second.keys.size() > 1 && (it->second.lastms - it->second.keys[1].getTime()) > bufferTime){ + if (!removeKey(it->first)){break;} + } + } + updateMeta(); + } + + void inputBuffer::userCallback(char * data, size_t len, unsigned int id) { + unsigned long tmp = ((long)(data[0]) << 24) | ((long)(data[1]) << 16) | ((long)(data[2]) << 8) | ((long)(data[3])); + if (tmp & 0x80000000) { + //Track is set to "New track request", assign new track id and create shared memory page + unsigned long tNum = (givenTracks.size() ? (*givenTracks.rbegin()) : 0) + 1; + ///\todo Neatify this + data[0] = (tNum >> 24) & 0xFF; + data[1] = (tNum >> 16) & 0xFF; + data[2] = (tNum >> 8) & 0xFF; + data[3] = (tNum) & 0xFF; + givenTracks.insert(tNum); + char tmpChr[100]; + long tmpLen = sprintf(tmpChr, "liveStream_%s%lu", config->getString("streamname").c_str(), tNum); + metaPages[tNum].init(std::string(tmpChr, tmpLen), 8388608, true); + } else { + unsigned long tNum = ((long)(data[0]) << 24) | ((long)(data[1]) << 16) | ((long)(data[2]) << 8) | ((long)(data[3])); + if (!myMeta.tracks.count(tNum)) { + DEBUG_MSG(DLVL_DEVEL, "Tracknum not in meta: %lu, from user %u", tNum, id); + if (metaPages[tNum].mapped) { + if (metaPages[tNum].mapped[0] == 'D' && metaPages[tNum].mapped[1] == 'T') { + unsigned int len = ntohl(((int *)metaPages[tNum].mapped)[1]); + unsigned int i = 0; + JSON::Value tmpMeta; + JSON::fromDTMI((const unsigned char *)metaPages[tNum].mapped + 8, len, i, tmpMeta); + DTSC::Meta tmpTrack(tmpMeta); + int oldTNum = tmpTrack.tracks.begin()->first; + bool collision = false; + for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { + if (it->first == tNum) { + continue; + } + if (it->second.getIdentifier() == tmpTrack.tracks[oldTNum].getIdentifier()) { + collision = true; + break; + } + } + if (collision) { + /// \todo Erasing page for now, should do more here + DEBUG_MSG(DLVL_DEVEL, "Collision detected! Erasing page for now, should do more here"); + metaPages.erase(tNum); + data[0] = 0xFF; + data[1] = 0xFF; + data[2] = 0xFF; + data[3] = 0xFF; + } else { + if (!myMeta.tracks.count(tNum)) { + myMeta.tracks[tNum] = tmpTrack.tracks[oldTNum]; + data[4] = 0x00; + data[5] = 0x00; + updateMeta(); + char firstPage[100]; + sprintf(firstPage, "%s%lu", config->getString("streamname").c_str(), tNum); + indexPages[tNum].init(firstPage, 8192, true); + ((long long int *)indexPages[tNum].mapped)[0] = htonl(1000); + ///\todo Fix for non-first-key-pushing + sprintf(firstPage, "%s%lu_0", config->getString("streamname").c_str(), tNum); + ///\todo Make size dynamic / other solution. 25mb is too much. + dataPages[tNum][0].init(firstPage, 26214400, true); + } + } + } + } + } else { + //First check if the previous page has been finished: + if (!inputLoc[tNum].count(dataPages[tNum].rbegin()->first) || !inputLoc[tNum][dataPages[tNum].rbegin()->first].curOffset){ + if (dataPages[tNum].size() > 1){ + int prevPage = (++dataPages[tNum].rbegin())->first; + //update previous page. + updateMetaFromPage(tNum, prevPage); + } + } + //update current page + int curPage = dataPages[tNum].rbegin()->first; + updateMetaFromPage(tNum, curPage); + if (inputLoc[tNum][curPage].curOffset > 8388608) { + //create new page is > 8MB + int nxtPage = curPage + inputLoc[tNum][curPage].keyNum; + char nextPageName[100]; + sprintf(nextPageName, "%s%lu_%d", config->getString("streamname").c_str(), tNum, nxtPage); + dataPages[tNum][nxtPage].init(nextPageName, 20971520, true); + bool createdNew = false; + for (int i = 0; i < 8192; i += 8){ + int thisKeyNum = ((((long long int *)(indexPages[tNum].mapped + i))[0]) >> 32) & 0xFFFFFFFF; + if (thisKeyNum == htonl(curPage)){ + if((ntohl((((long long int*)(indexPages[tNum].mapped + i))[0]) & 0xFFFFFFFF) == 1000)){ + ((long long int *)(indexPages[tNum].mapped + i))[0] &= 0xFFFFFFFF00000000; + ((long long int *)(indexPages[tNum].mapped + i))[0] |= htonl(inputLoc[tNum][curPage].keyNum); + } + } + if (!createdNew && (((long long int*)(indexPages[tNum].mapped + i))[0]) == 0){ + createdNew = true; + ((long long int *)(indexPages[tNum].mapped + i))[0] = (((long long int)htonl(nxtPage)) << 32) | htonl(1000); + } + } + } + } + } + } + + void inputBuffer::updateMetaFromPage(int tNum, int pageNum){ + DTSC::Packet tmpPack; + tmpPack.reInit(dataPages[tNum][pageNum].mapped + inputLoc[tNum][pageNum].curOffset, 0); + while (tmpPack) { + myMeta.update(tmpPack); + if (inputLoc[tNum][pageNum].firstTime == 0){ + inputLoc[tNum][pageNum].firstTime = tmpPack.getTime(); + } + //Overloaded use of .firstTime to indicate last Keytime on non-video streams; + if (myMeta.tracks[tNum].type == "video"){ + inputLoc[tNum][pageNum].keyNum += tmpPack.getFlag("keyframe"); + }else{ + if ((tmpPack.getTime() > 5000) && ((tmpPack.getTime() - 5000) > inputLoc[tNum][pageNum].firstTime)){ + inputLoc[tNum][pageNum].keyNum ++; + } + } + inputLoc[tNum][pageNum].curOffset += tmpPack.getDataLen(); + tmpPack.reInit(dataPages[tNum][pageNum].mapped + inputLoc[tNum][pageNum].curOffset, 0); + } + updateMeta(); + } + + bool inputBuffer::setup() { + if (!bufferTime){ + bufferTime = config->getInteger("bufferTime"); + } + JSON::Value servConf = JSON::fromFile(Util::getTmpFolder() + "streamlist"); + if (servConf.isMember("streams") && servConf["streams"].isMember(config->getString("streamname"))){ + JSON::Value & streamConfig = servConf["streams"][config->getString("streamname")]; + if (streamConfig.isMember("DVR") && streamConfig["DVR"].asInt()){ + if (bufferTime != streamConfig["DVR"].asInt()){ + DEBUG_MSG(DLVL_DEVEL, "Setting bufferTime from %u to new value of %lli", bufferTime, streamConfig["DVR"].asInt()); + bufferTime = streamConfig["DVR"].asInt(); + } + } + } + return true; + } + + bool inputBuffer::readHeader() { + return true; + } + + void inputBuffer::getNext(bool smart) {} + + void inputBuffer::seek(int seekTime) {} + + void inputBuffer::trackSelect(std::string trackSpec) {} +} + + + diff --git a/src/input/input_buffer.h b/src/input/input_buffer.h new file mode 100644 index 00000000..20764f4d --- /dev/null +++ b/src/input/input_buffer.h @@ -0,0 +1,33 @@ +#include "input.h" +#include <mist/dtsc.h> +#include <mist/shared_memory.h> + +namespace Mist { + class inputBuffer : public Input { + public: + inputBuffer(Util::Config * cfg); + private: + unsigned int bufferTime; + unsigned int cutTime; + protected: + //Private Functions + bool setup(); + void updateMeta(); + bool readHeader(); + void getNext(bool smart = true); + void updateMetaFromPage(int tNum, int pageNum); + void seek(int seekTime); + void trackSelect(std::string trackSpec); + bool removeKey(unsigned int tid); + void removeUnused(); + void userCallback(char * data, size_t len, unsigned int id); + std::set<unsigned long> givenTracks; + std::map<unsigned long, IPC::sharedPage> metaPages; + std::map<unsigned long, std::map<unsigned long, DTSCPageData> > inputLoc; + inputBuffer * singleton; + }; +} + +typedef Mist::inputBuffer mistIn; + + diff --git a/src/input/input_dtsc.cpp b/src/input/input_dtsc.cpp new file mode 100644 index 00000000..0f6d9e47 --- /dev/null +++ b/src/input/input_dtsc.cpp @@ -0,0 +1,90 @@ +#include <iostream> +#include <cstring> +#include <cerrno> +#include <cstdlib> +#include <cstdio> +#include <string> +#include <mist/stream.h> +#include <mist/defines.h> + +#include "input_dtsc.h" + +namespace Mist { + inputDTSC::inputDTSC(Util::Config * cfg) : Input(cfg) { + capa["decs"] = "Enables DTSC Input"; + capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][0u].append("H263"); + capa["codecs"][0u][0u].append("VP6"); + capa["codecs"][0u][0u].append("theora"); + capa["codecs"][0u][1u].append("AAC"); + capa["codecs"][0u][1u].append("MP3"); + capa["codecs"][0u][1u].append("vorbis"); + } + + bool inputDTSC::setup() { + if (config->getString("input") == "-") { + std::cerr << "Input from stream not yet supported" << std::endl; + return false; + } + if (config->getString("output") != "-") { + std::cerr << "Output to non-stdout not yet supported" << std::endl; + } + + //open File + inFile = DTSC::File(config->getString("input")); + if (!inFile) { + return false; + } + return true; + } + + bool inputDTSC::readHeader() { + if (!inFile) { + return false; + } + DTSC::File tmp(config->getString("input") + ".dtsh"); + if (tmp) { + myMeta = tmp.getMeta(); + DEBUG_MSG(DLVL_DEVEL,"Meta read in with %lu tracks", myMeta.tracks.size()); + return true; + } + if (inFile.getMeta().moreheader < 0 || inFile.getMeta().tracks.size() == 0) { + DEBUG_MSG(DLVL_FAIL,"Missing external header file"); + return false; + } + myMeta = DTSC::Meta(inFile.getMeta()); + DEBUG_MSG(DLVL_DEVEL,"Meta read in with %lu tracks", myMeta.tracks.size()); + return true; + } + + void inputDTSC::getNext(bool smart) { + if (smart){ + inFile.seekNext(); + }else{ + inFile.parseNext(); + } + lastPack = inFile.getPacket(); + } + + void inputDTSC::seek(int seekTime) { + inFile.seek_time(seekTime); + initialTime = 0; + playUntil = 0; + } + + void inputDTSC::trackSelect(std::string trackSpec) { + selectedTracks.clear(); + long long unsigned int index; + while (trackSpec != "") { + index = trackSpec.find(' '); + selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str())); + if (index != std::string::npos) { + trackSpec.erase(0, index + 1); + } else { + trackSpec = ""; + } + } + inFile.selectTracks(selectedTracks); + } +} + diff --git a/src/input/input_dtsc.h b/src/input/input_dtsc.h new file mode 100644 index 00000000..9a9f12db --- /dev/null +++ b/src/input/input_dtsc.h @@ -0,0 +1,22 @@ +#include "input.h" +#include <mist/dtsc.h> + +namespace Mist { + class inputDTSC : public Input { + public: + inputDTSC(Util::Config * cfg); + protected: + //Private Functions + bool setup(); + bool readHeader(); + void getNext(bool smart = true); + void seek(int seekTime); + void trackSelect(std::string trackSpec); + + DTSC::File inFile; + }; +} + +typedef Mist::inputDTSC mistIn; + + diff --git a/src/input/input_flv.cpp b/src/input/input_flv.cpp new file mode 100644 index 00000000..5e5ef403 --- /dev/null +++ b/src/input/input_flv.cpp @@ -0,0 +1,130 @@ +#include <iostream> +#include <fstream> +#include <cstring> +#include <cerrno> +#include <cstdlib> +#include <cstdio> +#include <string> +#include <mist/stream.h> +#include <mist/flv_tag.h> +#include <mist/defines.h> + +#include "input_flv.h" + +namespace Mist { + inputFLV::inputFLV(Util::Config * cfg) : Input(cfg) { + capa["decs"] = "Enables FLV Input"; + capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][0u].append("H263"); + capa["codecs"][0u][0u].append("VP6"); + capa["codecs"][0u][1u].append("AAC"); + capa["codecs"][0u][1u].append("MP3"); + } + + bool inputFLV::setup() { + if (config->getString("input") == "-") { + std::cerr << "Input from stream not yet supported" << std::endl; + return false; + } + if (config->getString("output") != "-") { + std::cerr << "Output to non-stdout not yet supported" << std::endl; + } + + //open File + inFile = fopen(config->getString("input").c_str(), "r"); + if (!inFile) { + return false; + } + return true; + } + + bool inputFLV::readHeader() { + JSON::Value lastPack; + if (!inFile) { + return false; + } + //See whether a separate header file exists. + DTSC::File tmp(config->getString("input") + ".dtsh"); + if (tmp){ + myMeta = tmp.getMeta(); + return true; + } + //Create header file from FLV data + fseek(inFile, 13, SEEK_SET); + FLV::Tag tmpTag; + long long int lastBytePos = 13; + while (!feof(inFile) && !FLV::Parse_Error){ + if (tmpTag.FileLoader(inFile)){ + lastPack.null(); + lastPack = tmpTag.toJSON(myMeta); + lastPack["bpos"] = lastBytePos; + myMeta.update(lastPack); + lastBytePos = ftell(inFile); + } + } + if (FLV::Parse_Error){ + std::cerr << FLV::Error_Str << std::endl; + return false; + } + std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str()); + oFile << myMeta.toJSON().toNetPacked(); + oFile.close(); + return true; + } + + void inputFLV::getNext(bool smart) { + static JSON::Value thisPack; + thisPack.null(); + long long int lastBytePos = ftell(inFile); + FLV::Tag tmpTag; + while (!feof(inFile) && !FLV::Parse_Error){ + if (tmpTag.FileLoader(inFile)){ + thisPack = tmpTag.toJSON(myMeta); + thisPack["bpos"] = lastBytePos; + if ( !selectedTracks.count(thisPack["trackid"].asInt())){ + getNext(); + } + break; + } + } + if (FLV::Parse_Error){ + std::cerr << FLV::Error_Str << std::endl; + thisPack.null(); + lastPack.null(); + return; + } + std::string tmpStr = thisPack.toNetPacked(); + lastPack.reInit(tmpStr.data(), tmpStr.size()); + } + + void inputFLV::seek(int seekTime) { + //We will seek to the corresponding keyframe of the video track if selected, otherwise audio keyframe. + //Flv files are never multi-track, so track 1 is video, track 2 is audio. + int trackSeek = (selectedTracks.count(1) ? 1 : 2); + size_t seekPos = myMeta.tracks[trackSeek].keys[0].getBpos(); + for (int i = 0; i < myMeta.tracks[trackSeek].keys.size(); i++){ + if (myMeta.tracks[trackSeek].keys[i].getTime() > seekTime){ + DEBUG_MSG(DLVL_WARN, "Seeking to keyframe %d on track %d, timestamp %ld, bytepos %lu", i, trackSeek, myMeta.tracks[trackSeek].keys[i].getTime(), seekPos); + break; + } + seekPos = myMeta.tracks[trackSeek].keys[i].getBpos(); + } + fseek(inFile, seekPos, SEEK_SET); + } + + void inputFLV::trackSelect(std::string trackSpec) { + selectedTracks.clear(); + long long int index; + while (trackSpec != "") { + index = trackSpec.find(' '); + selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str())); + DEBUG_MSG(DLVL_WARN, "Added track %d, index = %lld, (index == npos) = %d", atoi(trackSpec.substr(0, index).c_str()), index, index == std::string::npos); + if (index != std::string::npos) { + trackSpec.erase(0, index + 1); + } else { + trackSpec = ""; + } + } + } +} + diff --git a/src/input/input_flv.h b/src/input/input_flv.h new file mode 100644 index 00000000..6a8d5af9 --- /dev/null +++ b/src/input/input_flv.h @@ -0,0 +1,21 @@ +#include "input.h" +#include <mist/dtsc.h> + +namespace Mist { + class inputFLV : public Input { + public: + inputFLV(Util::Config * cfg); + protected: + //Private Functions + bool setup(); + bool readHeader(); + void getNext(bool smart = true); + void seek(int seekTime); + void trackSelect(std::string trackSpec); + + FILE * inFile; + }; +} + +typedef Mist::inputFLV mistIn; + diff --git a/src/input/input_ogg.cpp b/src/input/input_ogg.cpp new file mode 100644 index 00000000..df17b033 --- /dev/null +++ b/src/input/input_ogg.cpp @@ -0,0 +1,274 @@ +#include <iostream> +#include <fstream> +#include <cstring> +#include <cerrno> +#include <cstdlib> +#include <cstdio> +#include <string> +#include <mist/stream.h> +#include <mist/ogg.h> +#include <mist/defines.h> +#include <mist/bitstream.h> + +#include "input_ogg.h" + +namespace Mist { + inputOGG::inputOGG(Util::Config * cfg) : Input(cfg) { + capa["decs"] = "Enables OGG Input"; + capa["codecs"][0u][0u].append("theora"); + capa["codecs"][0u][1u].append("vorbis"); + } + + bool inputOGG::setup() { + if (config->getString("input") == "-") { + std::cerr << "Input from stream not yet supported" << std::endl; + return false; + } + if (config->getString("output") != "-") { + std::cerr << "Output to non-stdout not yet supported" << std::endl; + } + + //open File + inFile = fopen(config->getString("input").c_str(), "r"); + if (!inFile) { + return false; + } + return true; + } + + void inputOGG::parseBeginOfStream(OGG::Page & bosPage) { + long long int tid = snum2tid.size() + 1; + snum2tid[bosPage.getBitstreamSerialNumber()] = tid; + if (!memcmp(bosPage.getFullPayload() + 1, "theora", 6)) { + oggTracks[tid].codec = THEORA; + theora::header tmpHead(bosPage.getFullPayload(), bosPage.getPayloadSize()); + oggTracks[tid].msPerFrame = (double)(tmpHead.getFRD() * 1000) / tmpHead.getFRN(); + } + if (!memcmp(bosPage.getFullPayload() + 1, "vorbis", 6)) { + oggTracks[tid].codec = VORBIS; + vorbis::header tmpHead(bosPage.getFullPayload(), bosPage.getPayloadSize()); + oggTracks[tid].msPerFrame = (double)1000 / ntohl(tmpHead.getAudioSampleRate()); + } + } + + bool inputOGG::readHeader() { + JSON::Value lastPack; + if (!inFile) { + return false; + } + //See whether a separate header file exists. + DTSC::File tmp(config->getString("input") + ".dtsh"); + if (tmp) { + myMeta = tmp.getMeta(); + return true; + } + //Create header file from OGG data + fseek(inFile, 0, SEEK_SET); + OGG::Page tmpPage; + long long int lastBytePos = 0; + while (tmpPage.read(inFile)) { + DEBUG_MSG(DLVL_WARN,"Read a page"); + if (tmpPage.getHeaderType() & OGG::BeginOfStream){ + parseBeginOfStream(tmpPage); + DEBUG_MSG(DLVL_WARN,"Read BOS page for stream %lu, now track %lld", tmpPage.getBitstreamSerialNumber(), snum2tid[tmpPage.getBitstreamSerialNumber()]); + } + int offset = 0; + long long int tid = snum2tid[tmpPage.getBitstreamSerialNumber()]; + for (std::deque<unsigned int>::iterator it = tmpPage.getSegmentTableDeque().begin(); it != tmpPage.getSegmentTableDeque().end(); it++) { + if (oggTracks[tid].parsedHeaders) { + DEBUG_MSG(DLVL_WARN,"Parsing a page segment on track %lld", tid); + if ((it == (tmpPage.getSegmentTableDeque().end() - 1)) && (int)(tmpPage.getPageSegments()) == 255 && (int)(tmpPage.getSegmentTable()[254]) == 255) { + oggTracks[tid].contBuffer.append(tmpPage.getFullPayload() + offset, (*it)); + } else { + lastPack["trackid"] = tid; + lastPack["time"] = (long long)oggTracks[tid].lastTime; + if (oggTracks[tid].contBuffer.size()) { + lastPack["data"] = oggTracks[tid].contBuffer + std::string(tmpPage.getFullPayload() + offset, (*it)); + oggTracks[tid].contBuffer.clear(); + } else { + lastPack["data"] = std::string(tmpPage.getFullPayload() + offset, (*it)); + } + if (oggTracks[tid].codec == VORBIS) { + unsigned int blockSize = 0; + Utils::bitstreamLSBF packet; + packet.append(lastPack["data"].asString()); + if (!packet.get(1)) { + blockSize = oggTracks[tid].blockSize[oggTracks[tid].vModes[packet.get(vorbis::ilog(oggTracks[tid].vModes.size() - 1))].blockFlag]; + } else { + DEBUG_MSG(DLVL_WARN, "Packet type != 0"); + } + oggTracks[tid].lastTime += oggTracks[tid].msPerFrame * (blockSize / oggTracks[tid].channels); + } + if (oggTracks[tid].codec == THEORA) { + oggTracks[tid].lastTime += oggTracks[tid].msPerFrame; + if (it == (tmpPage.getSegmentTableDeque().end() - 1)) { + if (oggTracks[tid].idHeader.parseGranuleUpper(oggTracks[tid].lastGran) != oggTracks[tid].idHeader.parseGranuleUpper(tmpPage.getGranulePosition())) { + lastPack["keyframe"] = 1ll; + oggTracks[tid].lastGran = tmpPage.getGranulePosition(); + } else { + lastPack["interframe"] = 1ll; + } + } + } + lastPack["bpos"] = 0ll; + DEBUG_MSG(DLVL_WARN,"Parsed a packet of track %lld, new timestamp %f", tid, oggTracks[tid].lastTime); + myMeta.update(lastPack); + } + } else { + //Parsing headers + switch (oggTracks[tid].codec) { + case THEORA: { + theora::header tmpHead(tmpPage.getFullPayload() + offset, (*it)); + DEBUG_MSG(DLVL_WARN,"Theora header, type %d", tmpHead.getHeaderType()); + switch (tmpHead.getHeaderType()) { + case 0: { + oggTracks[tid].idHeader = tmpHead; + myMeta.tracks[tid].height = tmpHead.getPICH(); + myMeta.tracks[tid].width = tmpHead.getPICW(); + myMeta.tracks[tid].idHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); + break; + } + case 1: { + myMeta.tracks[tid].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); + break; + } + case 2: { + myMeta.tracks[tid].codec = "theora"; + myMeta.tracks[tid].trackID = tid; + myMeta.tracks[tid].type = "video"; + myMeta.tracks[tid].init = std::string(tmpPage.getFullPayload() + offset, (*it)); + oggTracks[tid].parsedHeaders = true; + oggTracks[tid].lastGran = 0; + break; + } + } + break; + } + case VORBIS: { + vorbis::header tmpHead(tmpPage.getFullPayload() + offset, (*it)); + DEBUG_MSG(DLVL_WARN,"Vorbis header, type %d", tmpHead.getHeaderType()); + switch (tmpHead.getHeaderType()) { + case 1: { + myMeta.tracks[tid].channels = tmpHead.getAudioChannels(); + myMeta.tracks[tid].idHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); + oggTracks[tid].channels = tmpHead.getAudioChannels(); + oggTracks[tid].blockSize[0] = 1 << tmpHead.getBlockSize0(); + oggTracks[tid].blockSize[1] = 1 << tmpHead.getBlockSize1(); + break; + } + case 3: { + myMeta.tracks[tid].commentHeader = std::string(tmpPage.getFullPayload() + offset, (*it)); + break; + } + case 5: { + myMeta.tracks[tid].codec = "vorbis"; + myMeta.tracks[tid].trackID = tid; + myMeta.tracks[tid].type = "audio"; + DEBUG_MSG(DLVL_WARN,"Set default values"); + myMeta.tracks[tid].init = std::string(tmpPage.getFullPayload() + offset, (*it)); + DEBUG_MSG(DLVL_WARN,"Set init values"); + oggTracks[tid].vModes = tmpHead.readModeDeque(oggTracks[tid].channels); + DEBUG_MSG(DLVL_WARN,"Set vmodevalues"); + oggTracks[tid].parsedHeaders = true; + break; + } + } + break; + } + } + offset += (*it); + } + } + lastBytePos = ftell(inFile); + DEBUG_MSG(DLVL_WARN,"End of Loop, @ filepos %lld", lastBytePos); + } + DEBUG_MSG(DLVL_WARN,"Exited while loop"); + std::ofstream oFile(std::string(config->getString("input") + ".dtsh").c_str()); + oFile << myMeta.toJSON().toNetPacked(); + oFile.close(); + return true; + } + + bool inputOGG::seekNextPage(int tid){ + fseek(inFile, oggTracks[tid].lastPageOffset, SEEK_SET); + bool res = true; + do { + res = oggTracks[tid].myPage.read(inFile); + } while(res && snum2tid[oggTracks[tid].myPage.getBitstreamSerialNumber()] != tid); + oggTracks[tid].lastPageOffset = ftell(inFile); + oggTracks[tid].nxtSegment = 0; + return res; + } + + void inputOGG::getNext(bool smart) { + if (!sortedSegments.size()){ + for (std::set<int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + seekNextPage((*it)); + } + } + if (sortedSegments.size()){ + int tid = (*(sortedSegments.begin())).tid; + bool addedPacket = false; + while (!addedPacket){ + segPart tmpPart; + if (oggTracks[tid].myPage.getSegment(oggTracks[tid].nxtSegment, tmpPart.segData, tmpPart.len)){ + if (oggTracks[tid].nxtSegment == 0 && oggTracks[tid].myPage.getHeaderType() && OGG::Continued){ + segment tmpSeg = *(sortedSegments.begin()); + tmpSeg.parts.push_back(tmpPart); + sortedSegments.erase(sortedSegments.begin()); + sortedSegments.insert(tmpSeg); + }else{ + segment tmpSeg; + tmpSeg.parts.push_back(tmpPart); + tmpSeg.tid = tid; + tmpSeg.time = oggTracks[tid].lastTime; + if (oggTracks[tid].codec == VORBIS) { + std::string data; + data.append(tmpPart.segData, tmpPart.len); + unsigned int blockSize = 0; + Utils::bitstreamLSBF packet; + packet.append(data); + if (!packet.get(1)) { + blockSize = oggTracks[tid].blockSize[oggTracks[tid].vModes[packet.get(vorbis::ilog(oggTracks[tid].vModes.size() - 1))].blockFlag]; + } + oggTracks[tid].lastTime += oggTracks[tid].msPerFrame * (blockSize / oggTracks[tid].channels); + } + if (oggTracks[tid].codec == THEORA) { + oggTracks[tid].lastTime += oggTracks[tid].msPerFrame; + } + sortedSegments.insert(tmpSeg); + addedPacket = true; + } + oggTracks[tid].nxtSegment ++; + }else{ + if (!seekNextPage(tid)){ + break; + } + } + } + std::string data; + } + } + + void inputOGG::seek(int seekTime) { + DEBUG_MSG(DLVL_WARN,"Seeking is not yet supported for ogg files"); + //Do nothing, seeking is not yet implemented for ogg + } + + void inputOGG::trackSelect(std::string trackSpec) { + selectedTracks.clear(); + long long int index; + while (trackSpec != "") { + index = trackSpec.find(' '); + selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str())); + DEBUG_MSG(DLVL_WARN, "Added track %d, index = %lld, (index == npos) = %d", atoi(trackSpec.substr(0, index).c_str()), index, index == std::string::npos); + if (index != std::string::npos) { + trackSpec.erase(0, index + 1); + } else { + trackSpec = ""; + } + } + } +} + + diff --git a/src/input/input_ogg.h b/src/input/input_ogg.h new file mode 100644 index 00000000..fbc33e1f --- /dev/null +++ b/src/input/input_ogg.h @@ -0,0 +1,65 @@ +#include "input.h" +#include <mist/dtsc.h> +#include <mist/ogg.h> + +namespace Mist { + enum codecType {THEORA, VORBIS}; + + struct segPart{ + char * segData; + unsigned int len; + }; + + struct segment{ + bool operator < (const segment & rhs) const { + return time < rhs.time || (time == rhs.time && tid < rhs.tid); + } + std::vector<segPart> parts; + unsigned int time; + unsigned int tid; + }; + + class oggTrack{ + public: + oggTrack() : lastTime(0), parsedHeaders(false), lastPageOffset(0), nxtSegment(0) { } + codecType codec; + std::string contBuffer;//buffer for continuing pages + double lastTime; + long long unsigned int lastGran; + bool parsedHeaders; + double msPerFrame; + long long unsigned int lastPageOffset; + OGG::Page myPage; + unsigned int nxtSegment; + //Codec specific elements + //theora + theora::header idHeader; + //vorbis + std::deque<vorbis::mode> vModes; + char channels; + long long unsigned int blockSize[2]; + }; + + class inputOGG : public Input { + public: + inputOGG(Util::Config * cfg); + protected: + //Private Functions + bool setup(); + bool readHeader(); + bool seekNextPage(int tid); + void getNext(bool smart = true); + void seek(int seekTime); + void trackSelect(std::string trackSpec); + + void parseBeginOfStream(OGG::Page & bosPage); + + FILE * inFile; + std::map<long long int, long long int> snum2tid; + std::map<long long int, oggTrack> oggTracks; + std::set<segment> sortedSegments; + }; +} + +typedef Mist::inputOGG mistIn; + diff --git a/src/input/mist_in.cpp b/src/input/mist_in.cpp new file mode 100644 index 00000000..6f087f36 --- /dev/null +++ b/src/input/mist_in.cpp @@ -0,0 +1,62 @@ +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <semaphore.h> + +#include INPUTTYPE +#include <mist/config.h> +#include <mist/defines.h> + +int main(int argc, char * argv[]) { + Util::Config conf(argv[0], PACKAGE_VERSION); + mistIn conv(&conf); + if (conf.parseArgs(argc, argv)) { + sem_t * playerLock = sem_open(std::string("/lock_" + conf.getString("streamname")).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1); + if (sem_trywait(playerLock) == -1){ + DEBUG_MSG(DLVL_DEVEL, "A player for stream %s is already running", conf.getString("streamname").c_str()); + return 1; + } + conf.activate(); + while (conf.is_active){ + int pid = fork(); + if (pid == 0){ + sem_close(playerLock); + return conv.run(); + } + if (pid == -1){ + DEBUG_MSG(DLVL_FAIL, "Unable to spawn player process"); + sem_post(playerLock); + return 2; + } + //wait for the process to exit + int status; + while (waitpid(pid, &status, 0) != pid && errno == EINTR) continue; + //clean up the semaphore by waiting for it, if it's non-zero + sem_t * waiting = sem_open(std::string("/wait_" + conf.getString("streamname")).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 0); + if (waiting == SEM_FAILED){ + DEBUG_MSG(DLVL_FAIL, "Failed to open semaphore - cancelling"); + return -1; + } + int sem_val = 0; + sem_getvalue(waiting, &sem_val); + while (sem_val){ + while (sem_wait(waiting) == -1 && errno == EINTR) continue; + sem_getvalue(waiting, &sem_val); + } + sem_close(waiting); + //if the exit was clean, don't restart it + if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)){ + DEBUG_MSG(DLVL_DEVEL, "Finished player succesfully"); + break; + } + } + sem_post(playerLock); + sem_close(playerLock); + } + return 0; +} + + diff --git a/src/output/mist_out.cpp b/src/output/mist_out.cpp new file mode 100644 index 00000000..b23ff21a --- /dev/null +++ b/src/output/mist_out.cpp @@ -0,0 +1,21 @@ +#include OUTPUTTYPE +#include <mist/config.h> +#include <mist/socket.h> + +int spawnForked(Socket::Connection & S){ + mistOut tmp(S); + return tmp.run(); +} + +int main(int argc, char * argv[]) { + Util::Config conf(argv[0], PACKAGE_VERSION); + mistOut::init(&conf); + if (conf.parseArgs(argc, argv)) { + if (conf.getBool("json")) { + std::cout << mistOut::capa.toString() << std::endl; + return -1; + } + conf.serveForkedSocket(spawnForked); + } + return 0; +} diff --git a/src/output/output.cpp b/src/output/output.cpp new file mode 100644 index 00000000..047133d5 --- /dev/null +++ b/src/output/output.cpp @@ -0,0 +1,493 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <semaphore.h> +#include <iterator> //std::distance + +#include <mist/stream.h> +#include <mist/defines.h> +#include <mist/http_parser.h> +#include <mist/timing.h> +#include "output.h" + +namespace Mist { + Util::Config * Output::config = NULL; + JSON::Value Output::capa = JSON::Value(); + + int getDTSCLen(char * mapped, long long int offset){ + return ntohl(((int*)(mapped + offset))[1]); + } + + long long int getDTSCTime(char * mapped, long long int offset){ + char * timePoint = mapped + offset + 12; + return ((long long int)timePoint[0] << 56) | ((long long int)timePoint[1] << 48) | ((long long int)timePoint[2] << 40) | ((long long int)timePoint[3] << 32) | ((long long int)timePoint[4] << 24) | ((long long int)timePoint[5] << 16) | ((long long int)timePoint[6] << 8) | timePoint[7]; + } + + Output::Output(Socket::Connection & conn) : myConn(conn) { + firstTime = 0; + parseData = false; + wantRequest = true; + isInitialized = false; + isBlocking = false; + lastStats = 0; + maxSkipAhead = 7500; + minSkipAhead = 5000; + realTime = 1000; + if (myConn){ + setBlocking(true); + }else{ + DEBUG_MSG(DLVL_WARN, "Warning: MistOut created with closed socket!"); + } + sentHeader = false; + } + + void Output::setBlocking(bool blocking){ + isBlocking = blocking; + myConn.setBlocking(isBlocking); + } + + Output::~Output(){ + statsPage.finish(); + playerConn.finish(); + } + + void Output::updateMeta(){ + unsigned int i = 0; + //read metadata from page to myMeta variable + JSON::Value jsonMeta; + JSON::fromDTMI((const unsigned char*)streamIndex.mapped + 8, streamIndex.len - 8, i, jsonMeta); + myMeta = DTSC::Meta(jsonMeta); + } + + /// Called when stream initialization has failed. + /// The standard implementation will set isInitialized to false and close the client connection, + /// thus causing the process to exit cleanly. + void Output::onFail(){ + isInitialized = false; + myConn.close(); + } + + void Output::initialize(){ + if (isInitialized){ + return; + } + if (streamIndex.mapped){ + return; + } + isInitialized = true; + streamIndex.init(streamName,0,false,false); + if (!streamIndex.mapped){ + sem_t * waiting = sem_open(std::string("/wait_" + streamName).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 0); + Util::Stream::getStream(streamName); + if (waiting == SEM_FAILED){ + DEBUG_MSG(DLVL_FAIL, "Failed to open semaphore - cancelling"); + onFail(); + return; + } + #ifdef __APPLE__ + unsigned int timeout = 0; + while (++timeout < 300 && sem_trywait(waiting) == -1 && (errno == EINTR || errno == EAGAIN) ){ + Util::sleep(100); + } + #else + struct timespec ts; + ts.tv_sec = Util::epoch() + 30; + ts.tv_nsec = 0; + while (sem_timedwait(waiting, &ts) == -1 && errno == EINTR) continue; + #endif + sem_post(waiting); + sem_close(waiting); + streamIndex.init(streamName,0); + } + if (!streamIndex.mapped){ + DEBUG_MSG(DLVL_FAIL, "Could not connect to server for %s\n", streamName.c_str()); + onFail(); + return; + } + statsPage = IPC::sharedClient("statistics", 88, true); + playerConn = IPC::sharedClient(streamName + "_users", 30, true); + + updateMeta(); + + //check which tracks don't actually exist + std::set<long unsigned int> toRemove; + for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + if (!myMeta.tracks.count(*it)){ + toRemove.insert(*it); + } + } + //remove those from selectedtracks + for (std::set<long unsigned int>::iterator it = toRemove.begin(); it != toRemove.end(); it++){ + selectedTracks.erase(*it); + } + + //loop through all codec combinations, count max simultaneous active + unsigned int bestSoFar = 0; + unsigned int bestSoFarCount = 0; + unsigned int index = 0; + for (JSON::ArrIter it = capa["codecs"].ArrBegin(); it != capa["codecs"].ArrEnd(); it++){ + unsigned int genCounter = 0; + unsigned int selCounter = 0; + if ((*it).size() > 0){ + for (JSON::ArrIter itb = (*it).ArrBegin(); itb != (*it).ArrEnd(); itb++){ + if ((*itb).size() > 0){ + bool found = false; + for (JSON::ArrIter itc = (*itb).ArrBegin(); itc != (*itb).ArrEnd() && !found; itc++){ + for (std::set<long unsigned int>::iterator itd = selectedTracks.begin(); itd != selectedTracks.end(); itd++){ + if (myMeta.tracks[*itd].codec == (*itc).asStringRef()){ + selCounter++; + found = true; + break; + } + } + if (!found){ + for (std::map<int,DTSC::Track>::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){ + if (trit->second.codec == (*itc).asStringRef()){ + genCounter++; + found = true; + break; + } + } + } + } + } + } + if (selCounter == selectedTracks.size()){ + if (selCounter + genCounter > bestSoFarCount){ + bestSoFarCount = selCounter + genCounter; + bestSoFar = index; + DEBUG_MSG(DLVL_HIGH, "Match (%u/%u): %s", selCounter, selCounter+genCounter, (*it).toString().c_str()); + } + }else{ + DEBUG_MSG(DLVL_VERYHIGH, "Not a match for currently selected tracks: %s", (*it).toString().c_str()); + } + } + index++; + } + + DEBUG_MSG(DLVL_MEDIUM, "Trying to fill: %s", capa["codecs"][bestSoFar].toString().c_str()); + //try to fill as many codecs simultaneously as possible + if (capa["codecs"][bestSoFar].size() > 0){ + for (JSON::ArrIter itb = capa["codecs"][bestSoFar].ArrBegin(); itb != capa["codecs"][bestSoFar].ArrEnd(); itb++){ + if ((*itb).size() > 0){ + bool found = false; + for (JSON::ArrIter itc = (*itb).ArrBegin(); itc != (*itb).ArrEnd() && !found; itc++){ + for (std::set<long unsigned int>::iterator itd = selectedTracks.begin(); itd != selectedTracks.end(); itd++){ + if (myMeta.tracks[*itd].codec == (*itc).asStringRef()){ + found = true; + break; + } + } + if (!found){ + for (std::map<int,DTSC::Track>::iterator trit = myMeta.tracks.begin(); trit != myMeta.tracks.end(); trit++){ + if (trit->second.codec == (*itc).asStringRef()){ + selectedTracks.insert(trit->first); + found = true; + break; + } + } + } + } + } + } + } + + #if DEBUG >= DLVL_MEDIUM + //print the selected tracks + std::stringstream selected; + for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + if (it != selectedTracks.begin()){ + selected << ", "; + } + selected << (*it); + } + DEBUG_MSG(DLVL_MEDIUM, "Selected tracks: %s", selected.str().c_str()); + #endif + + unsigned int firstms = 0x0; + for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + lastKeyTime[*it] = 0xFFFFFFFF; + if (myMeta.tracks[*it].firstms > firstms){ + firstms = myMeta.tracks[*it].firstms; + } + } + if (myMeta.live){ + if (firstms < 5000){ + firstms = 0; + } + seek(firstms); + }else{ + seek(0); + } + } + + /// Clears the buffer, sets parseData to false, and generally makes not very much happen at all. + void Output::stop(){ + buffer.clear(); + parseData = false; + } + + unsigned int Output::getKeyForTime(long unsigned int trackId, long long timeStamp){ + unsigned int keyNo = 0; + for (std::deque<DTSC::Key>::iterator it = myMeta.tracks[trackId].keys.begin(); it != myMeta.tracks[trackId].keys.end(); it++){ + if (it->getTime() <= timeStamp){ + keyNo = it->getNumber(); + }else{ + break; + } + } + return keyNo; + } + + void Output::loadPageForKey(long unsigned int trackId, long long int keyNum){ + if (keyNum >= myMeta.tracks[trackId].keys.rbegin()->getNumber()){ + //curPages.erase(trackId); + return; + } + DEBUG_MSG(DLVL_MEDIUM, "Loading track %lu, containing key %lld", trackId, keyNum); + int pageNum = -1; + int keyAmount = -1; + unsigned int timeout = 0; + if (!indexPages.count(trackId)){ + char id[100]; + sprintf(id, "%s%lu", streamName.c_str(), trackId); + indexPages[trackId].init(id, 8192); + } + while (pageNum == -1 || keyAmount == -1){ + for (int i = 0; i < indexPages[trackId].len / 8; i++){ + long tmpKey = ntohl(((((long long int*)indexPages[trackId].mapped)[i]) >> 32) & 0xFFFFFFFF); + long amountKey = ntohl((((long long int*)indexPages[trackId].mapped)[i]) & 0xFFFFFFFF); + if (tmpKey <= keyNum && (tmpKey + amountKey) > keyNum){ + pageNum = tmpKey; + keyAmount = amountKey; + break; + } + } + if (pageNum == -1 || keyAmount == -1){ + if (!timeout){ + DEBUG_MSG(DLVL_DEVEL, "Requesting/waiting for page that has key %lu:%lld...", trackId, keyNum); + } + if (timeout++ > 100){ + DEBUG_MSG(DLVL_FAIL, "Timeout while waiting for requested page. Aborting."); + curPages.erase(trackId); + return; + } + nxtKeyNum[trackId] = keyNum-1; + stats(); + Util::sleep(100); + } + } + + nxtKeyNum[trackId] = pageNum; + + if (currKeyOpen.count(trackId) && currKeyOpen[trackId] == pageNum){ + return; + } + char id[100]; + sprintf(id, "%s%lu_%d", streamName.c_str(), trackId, pageNum); + curPages[trackId].init(std::string(id),0); + if (!(curPages[trackId].mapped)){ + DEBUG_MSG(DLVL_FAIL, "(%d) Initializing page %s failed", getpid(), curPages[trackId].name.c_str()); + return; + } + currKeyOpen[trackId] = pageNum; + } + + /// Prepares all tracks from selectedTracks for seeking to the specified ms position. + /// \todo Make this actually seek, instead of always loading position zero. + void Output::seek(long long pos){ + firstTime = Util::getMS() - pos; + if (!isInitialized){ + initialize(); + } + buffer.clear(); + currentPacket.null(); + updateMeta(); + for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + seek(*it, pos); + } + } + + bool Output::seek(int tid, long long pos, bool getNextKey){ + loadPageForKey(tid, getKeyForTime(tid, pos) + (getNextKey?1:0)); + if (!curPages.count(tid) || !curPages[tid].mapped){ + DEBUG_MSG(DLVL_DEVEL, "Aborting seek to %llims in track %d, not available.", pos, tid); + return false; + } + sortedPageInfo tmp; + tmp.tid = tid; + tmp.offset = 0; + DTSC::Packet tmpPack; + tmpPack.reInit(curPages[tid].mapped + tmp.offset, 0, true); + tmp.time = tmpPack.getTime(); + while ((long long)tmp.time < pos && tmpPack){ + tmp.offset += tmpPack.getDataLen(); + tmpPack.reInit(curPages[tid].mapped + tmp.offset, 0, true); + tmp.time = tmpPack.getTime(); + } + if (tmpPack){ + buffer.insert(tmp); + return true; + }else{ + //don't print anything for empty packets - not sign of corruption, just unfinished stream. + if (curPages[tid].mapped[tmp.offset] != 0){ + DEBUG_MSG(DLVL_FAIL, "Noes! Couldn't find packet on track %d because of some kind of corruption error or somesuch.", tid); + }else{ + DEBUG_MSG(DLVL_FAIL, "Track %d no data (key %u) - waiting...", tid, getKeyForTime(tid, pos) + (getNextKey?1:0)); + } + return false; + } + } + + int Output::run() { + bool firstData = true;//only the first time, we call OnRequest if there's data buffered already. + DEBUG_MSG(DLVL_MEDIUM, "MistOut client handler started"); + while (myConn.connected() && (wantRequest || parseData)){ + stats(); + if (wantRequest){ + if ((firstData && myConn.Received().size()) || myConn.spool()){ + firstData = false; + DEBUG_MSG(DLVL_VERYHIGH, "(%d) OnRequest", getpid()); + onRequest(); + }else{ + if (!isBlocking && !parseData){ + Util::sleep(500); + } + } + } + if (parseData){ + if (!isInitialized){ + initialize(); + } + if ( !sentHeader){ + DEBUG_MSG(DLVL_VERYHIGH, "(%d) SendHeader", getpid()); + sendHeader(); + } + prepareNext(); + if (currentPacket){ + sendNext(); + }else{ + if (!onFinish()){ + break; + } + } + } + } + DEBUG_MSG(DLVL_MEDIUM, "MistOut client handler shutting down: %s, %s, %s", myConn.connected() ? "conn_active" : "conn_closed", wantRequest ? "want_request" : "no_want_request", parseData ? "parsing_data" : "not_parsing_data"); + myConn.close(); + return 0; + } + + void Output::prepareNext(){ + static unsigned int emptyCount = 0; + if (!buffer.size()){ + currentPacket.null(); + DEBUG_MSG(DLVL_DEVEL, "Buffer completely played out"); + return; + } + sortedPageInfo nxt = *(buffer.begin()); + buffer.erase(buffer.begin()); + + DEBUG_MSG(DLVL_VERYHIGH, "Loading track %u (next=%lu), part @ %u/%lld", nxt.tid, nxtKeyNum[nxt.tid], nxt.offset, curPages[nxt.tid].len); + + if (nxt.offset >= curPages[nxt.tid].len){ + loadPageForKey(nxt.tid, ++nxtKeyNum[nxt.tid]); + nxt.offset = 0; + } + + if (!curPages.count(nxt.tid) || !curPages[nxt.tid].mapped){ + //mapping failure? Drop this track and go to next. + //not an error - usually means end of stream. + DEBUG_MSG(DLVL_DEVEL, "Track %u no page - dropping track.", nxt.tid); + prepareNext(); + return; + } + + if (!memcmp(curPages[nxt.tid].mapped + nxt.offset, "\000\000\000\000", 4)){ + if (!currentPacket.getTime()){ + DEBUG_MSG(DLVL_DEVEL, "Timeless empty packet on track %u - dropping track.", nxt.tid); + prepareNext(); + return; + } + Util::sleep(500); + updateMeta(); + if (myMeta && ++emptyCount < 20){ + if (!seek(nxt.tid, currentPacket.getTime(), true)){ + buffer.insert(nxt); + } + }else{ + DEBUG_MSG(DLVL_DEVEL, "Empty packet on track %u - could not reload, dropping track.", nxt.tid); + } + prepareNext(); + return; + } + currentPacket.reInit(curPages[nxt.tid].mapped + nxt.offset, 0, true); + if (currentPacket){ + nxtKeyNum[nxt.tid] = getKeyForTime(nxt.tid, currentPacket.getTime()); + emptyCount = 0; + } + nxt.offset += currentPacket.getDataLen(); + if (realTime && !myMeta.live){ + while (nxt.time > (Util::getMS() - firstTime + maxSkipAhead)*1000/realTime) { + Util::sleep(nxt.time - (Util::getMS() - firstTime + minSkipAhead)*1000/realTime); + } + } + if (curPages[nxt.tid]){ + if (nxt.offset < curPages[nxt.tid].len){ + nxt.time = getDTSCTime(curPages[nxt.tid].mapped, nxt.offset); + } + buffer.insert(nxt); + } + playerConn.keepAlive(); + } + + void Output::stats(){ + if (!statsPage.getData()){ + return; + } + unsigned long long int now = Util::epoch(); + if (now != lastStats){ + lastStats = now; + IPC::statExchange tmpEx(statsPage.getData()); + tmpEx.now(now); + tmpEx.host(myConn.getHost()); + tmpEx.streamName(streamName); + tmpEx.connector(capa["name"].asString()); + tmpEx.up(myConn.dataUp()); + tmpEx.down(myConn.dataDown()); + tmpEx.time(now - myConn.connTime()); + statsPage.keepAlive(); + } + int tNum = 0; + for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end() && tNum < 5; it++){ + char thisData[6]; + thisData[0] = ((*it >> 24) & 0xFF); + thisData[1] = ((*it >> 16) & 0xFF); + thisData[2] = ((*it >> 8) & 0xFF); + thisData[3] = ((*it) & 0xFF); + thisData[4] = ((nxtKeyNum[*it] >> 8) & 0xFF); + thisData[5] = ((nxtKeyNum[*it]) & 0xFF); + memcpy(playerConn.getData() + (6 * tNum), thisData, 6); + tNum ++; + playerConn.keepAlive(); + } + if (tNum >= 5){ + DEBUG_MSG(DLVL_WARN, "Too many tracks selected, using only first 5"); + } + } + + void Output::onRequest(){ + //simply clear the buffer, we don't support any kind of input by default + myConn.Received().clear(); + wantRequest = false; + } + + void Output::sendHeader(){ + //just set the sentHeader bool to true, by default + sentHeader = true; + } + +} + diff --git a/src/output/output.h b/src/output/output.h new file mode 100644 index 00000000..f8b5c02a --- /dev/null +++ b/src/output/output.h @@ -0,0 +1,98 @@ +#include <set> +#include <cstdlib> +#include <map> +#include <mist/config.h> +#include <mist/json.h> +#include <mist/flv_tag.h> +#include <mist/timing.h> +#include <mist/dtsc.h> +#include <mist/socket.h> +#include <mist/shared_memory.h> + +namespace Mist { + + /// This struct keeps packet information sorted in playback order, so the + /// Mist::Output class knows when to buffer which packet. + struct sortedPageInfo{ + bool operator < (const sortedPageInfo & rhs) const { + if (time < rhs.time){ + return true; + } + return (time == rhs.time && tid < rhs.tid); + } + int tid; + long long unsigned int time; + unsigned int offset; + }; + + /// The output class is intended to be inherited by MistOut process classes. + /// It contains all generic code and logic, while the child classes implement + /// anything specific to particular protocols or containers. + /// It contains several virtual functions, that may be overridden to "hook" into + /// the streaming process at those particular points, simplifying child class + /// logic and implementation details. + class Output { + public: + //constructor and destructor + Output(Socket::Connection & conn); + virtual ~Output(); + //static members for initialization and capabilities + static void init(Util::Config * cfg) {} + static JSON::Value capa; + //non-virtual generic functions + int run(); + void stats(); + void seek(long long pos); + bool seek(int tid, long long pos, bool getNextKey = false); + void stop(); + void setBlocking(bool blocking); + void updateMeta(); + //virtuals. The optional virtuals have default implementations that do as little as possible. + virtual void sendNext() {}//REQUIRED! Others are optional. + virtual void prepareNext(); + virtual void onRequest(); + virtual bool onFinish(){return false;} + virtual void initialize(); + virtual void sendHeader(); + virtual void onFail(); + private://these *should* not be messed with in child classes. + std::map<unsigned long, unsigned int> currKeyOpen; + void loadPageForKey(long unsigned int trackId, long long int keyNum); + bool isBlocking;///< If true, indicates that myConn is blocking. + unsigned int lastStats;///<Time of last sending of stats. + IPC::sharedClient statsPage;///< Shared memory used for statistics reporting. + long long unsigned int firstTime;///< Time of first packet after last seek. Used for real-time sending. + std::map<unsigned long, unsigned long> nxtKeyNum;///< Contains the number of the next key, for page seeking purposes. + std::set<sortedPageInfo> buffer;///< A sorted list of next-to-be-loaded packets. + std::map<unsigned long, unsigned long> lastKeyTime;///< Stores the time of the last keyframe, for preventing duplicates + protected://these are to be messed with by child classes + unsigned int getKeyForTime(long unsigned int trackId, long long timeStamp); + IPC::sharedPage streamIndex;///< Shared memory used for metadata + std::map<int,IPC::sharedPage> indexPages;///< Maintains index pages of each track, holding information about available pages with DTSC packets. + std::map<int,IPC::sharedPage> curPages;///< Holds the currently used pages with DTSC packets for each track. + /// \todo Privitize keyTimes + IPC::sharedClient playerConn;///< Shared memory used for connection to MistIn process. + std::map<int,std::set<int> > keyTimes;///< Per-track list of keyframe times, for keyframe detection. + //static member for initialization + static Util::Config * config;///< Static, global configuration for the MistOut process + + //stream delaying variables + unsigned int maxSkipAhead;///< Maximum ms that we will go ahead of the intended timestamps. + unsigned int minSkipAhead;///< Minimum ms that we will go ahead of the intended timestamps. + unsigned int realTime;///< Playback speed times 1000 (1000 == 1.0X). Zero is infinite. + + //Read/write status variables + Socket::Connection & myConn;///< Connection to the client. + std::string streamName;///< Name of the stream that will be opened by initialize() + std::set<unsigned long> selectedTracks; ///< Tracks that are selected for playback + bool wantRequest;///< If true, waits for a request. + bool parseData;///< If true, triggers initalization if not already done, sending of header, sending of packets. + bool isInitialized;///< If false, triggers initialization if parseData is true. + bool sentHeader;///< If false, triggers sendHeader if parseData is true. + + //Read-only stream data variables + DTSC::Packet currentPacket;///< The packet that is ready for sending now. + DTSC::Meta myMeta;///< Up to date stream metadata + }; + +} diff --git a/src/output/output_hds.cpp b/src/output/output_hds.cpp new file mode 100644 index 00000000..43d129d0 --- /dev/null +++ b/src/output/output_hds.cpp @@ -0,0 +1,266 @@ +#include "output_hds.h" +#include <mist/defines.h> +#include <mist/http_parser.h> +#include <mist/stream.h> +#include <unistd.h> + +#include <mist/amf.h> +#include <mist/mp4_adobe.h> + +namespace Mist { + + void OutHDS::getTracks(){ + /// \todo Why do we have only one audio track option? + videoTracks.clear(); + audioTrack = 0; + for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + if (it->second.codec == "H264" || it->second.codec == "H263" || it->second.codec == "VP6"){ + videoTracks.insert(it->first); + } + if (it->second.codec == "AAC" || it->second.codec == "MP3"){ + audioTrack = it->first; + } + } + } + + ///\brief Builds a bootstrap for use in HTTP Dynamic streaming. + ///\param tid The track this bootstrap is generated for. + ///\return The generated bootstrap. + std::string OutHDS::dynamicBootstrap(int tid){ + updateMeta(); + std::string empty; + + MP4::ASRT asrt; + asrt.setUpdate(false); + asrt.setVersion(1); + //asrt.setQualityEntry(empty, 0); + if (myMeta.live){ + asrt.setSegmentRun(1, 4294967295ul, 0); + }else{ + asrt.setSegmentRun(1, myMeta.tracks[tid].keys.size(), 0); + } + + MP4::AFRT afrt; + afrt.setUpdate(false); + afrt.setVersion(1); + afrt.setTimeScale(1000); + //afrt.setQualityEntry(empty, 0); + MP4::afrt_runtable afrtrun; + int i = 0; + for (std::deque<DTSC::Key>::iterator it = myMeta.tracks[tid].keys.begin(); it != myMeta.tracks[tid].keys.end(); it++){ + if (it->getLength()){ + afrtrun.firstFragment = it->getNumber(); + afrtrun.firstTimestamp = it->getTime(); + afrtrun.duration = it->getLength(); + afrt.setFragmentRun(afrtrun, i); + i++; + } + } + + MP4::ABST abst; + abst.setVersion(1); + abst.setBootstrapinfoVersion(1); + abst.setProfile(0); + abst.setUpdate(false); + abst.setTimeScale(1000); + abst.setLive(myMeta.live); + abst.setCurrentMediaTime(myMeta.tracks[tid].lastms); + abst.setSmpteTimeCodeOffset(0); + abst.setMovieIdentifier(streamName); + abst.setSegmentRunTable(asrt, 0); + abst.setFragmentRunTable(afrt, 0); + + DEBUG_MSG(DLVL_VERYHIGH, "Sending bootstrap: %s", abst.toPrettyString(0).c_str()); + return std::string((char*)abst.asBox(), (int)abst.boxedSize()); + } + + ///\brief Builds an index file for HTTP Dynamic streaming. + ///\return The index file for HTTP Dynamic Streaming. + std::string OutHDS::dynamicIndex(){ + getTracks(); + std::stringstream Result; + Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl; + Result << " <manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">" << std::endl; + Result << " <id>" << streamName << "</id>" << std::endl; + Result << " <mimeType>video/mp4</mimeType>" << std::endl; + Result << " <deliveryType>streaming</deliveryType>" << std::endl; + if (myMeta.vod){ + Result << " <duration>" << myMeta.tracks[*videoTracks.begin()].lastms / 1000 << ".000</duration>" << std::endl; + Result << " <streamType>recorded</streamType>" << std::endl; + }else{ + Result << " <duration>0.00</duration>" << std::endl; + Result << " <streamType>live</streamType>" << std::endl; + } + for (std::set<int>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){ + Result << " <bootstrapInfo " + "profile=\"named\" " + "id=\"boot" << (*it) << "\" " + "url=\"" << (*it) << ".abst\">" + "</bootstrapInfo>" << std::endl; + Result << " <media " + "url=\"" << (*it) << "-\" " + "bitrate=\"" << myMeta.tracks[(*it)].bps * 8 << "\" " + "bootstrapInfoId=\"boot" << (*it) << "\" " + "width=\"" << myMeta.tracks[(*it)].width << "\" " + "height=\"" << myMeta.tracks[(*it)].height << "\">" << std::endl; + Result << " <metadata>AgAKb25NZXRhRGF0YQMAAAk=</metadata>" << std::endl; + Result << " </media>" << std::endl; + } + Result << "</manifest>" << std::endl; + DEBUG_MSG(DLVL_HIGH, "Sending manifest: %s", Result.str().c_str()); + return Result.str(); + } //BuildManifest + + OutHDS::OutHDS(Socket::Connection & conn) : Output(conn) { + audioTrack = 0; + playUntil = 0; + } + + void OutHDS::onFail(){ + HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers + HTTP_S.SetBody("Stream not found. Sorry, we tried."); + HTTP_S.SendResponse("404", "Stream not found", myConn); + Output::onFail(); + } + + OutHDS::~OutHDS() {} + + void OutHDS::init(Util::Config * cfg){ + capa["desc"] = "Enables HTTP protocol Adobe-specific dynamic streaming (also known as HDS)."; + capa["deps"] = "HTTP"; + capa["url_rel"] = "/dynamic/$/manifest.f4m"; + capa["url_prefix"] = "/dynamic/$/"; + capa["socket"] = "http_hds"; + capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][0u].append("H263"); + capa["codecs"][0u][0u].append("VP6"); + capa["codecs"][0u][1u].append("AAC"); + capa["codecs"][0u][1u].append("MP3"); + capa["methods"][0u]["handler"] = "http"; + capa["methods"][0u]["type"] = "flash/11"; + capa["methods"][0u]["priority"] = 7ll; + cfg->addBasicConnectorOptions(capa); + config = cfg; + } + + void OutHDS::sendNext(){ + if (currentPacket.getTime() >= playUntil){ + DEBUG_MSG(DLVL_DEVEL, "(%d) Done sending fragment", getpid() ); + stop(); + wantRequest = true; + HTTP_S.Chunkify("", 0, myConn); + return; + } + tag.DTSCLoader(currentPacket, myMeta.tracks[currentPacket.getTrackId()]); + HTTP_S.Chunkify(tag.data, tag.len, myConn); + } + + void OutHDS::onRequest(){ + HTTP_R.Clean(); + while (HTTP_R.Read(myConn)){ + DEBUG_MSG(DLVL_DEVEL, "Received request: %s", HTTP_R.getUrl().c_str()); + if (HTTP_R.url.find(".abst") != std::string::npos){ + myConn.setHost(HTTP_R.GetHeader("X-Origin")); + streamName = HTTP_R.GetHeader("X-Stream"); + std::string streamID = HTTP_R.url.substr(streamName.size() + 10); + streamID = streamID.substr(0, streamID.find(".abst")); + HTTP_S.Clean(); + HTTP_S.SetBody(dynamicBootstrap(atoll(streamID.c_str()))); + HTTP_S.SetHeader("Content-Type", "binary/octet"); + HTTP_S.SetHeader("Cache-Control", "no-cache"); + HTTP_S.SendResponse("200", "OK", myConn); + HTTP_R.Clean(); //clean for any possible next requests + continue; + } + if (HTTP_R.url.find("f4m") == std::string::npos){ + myConn.setHost(HTTP_R.GetHeader("X-Origin")); + streamName = HTTP_R.GetHeader("X-Stream"); + initialize(); + std::string tmp_qual = HTTP_R.url.substr(HTTP_R.url.find("/", 10) + 1); + unsigned int tid; + unsigned int fragNum; + tid = atoi(tmp_qual.substr(0, tmp_qual.find("Seg") - 1).c_str()); + int temp; + temp = HTTP_R.url.find("Seg") + 3; + temp = HTTP_R.url.find("Frag") + 4; + fragNum = atoi(HTTP_R.url.substr(temp).c_str()); + DEBUG_MSG(DLVL_MEDIUM, "Video track %d, fragment %d\n", tid, fragNum); + if (!audioTrack){getTracks();} + unsigned int mstime = 0; + unsigned int mslen = 0; + for (std::deque<DTSC::Key>::iterator it = myMeta.tracks[tid].keys.begin(); it != myMeta.tracks[tid].keys.end(); it++){ + if (it->getNumber() >= fragNum){ + mstime = it->getTime(); + mslen = it->getLength(); + if (myMeta.live){ + if (it == myMeta.tracks[tid].keys.end() - 2){ + HTTP_S.Clean(); + HTTP_S.SetBody("Proxy, re-request this in a second or two.\n"); + HTTP_S.SendResponse("208", "Ask again later", myConn); + HTTP_R.Clean(); //clean for any possible next requests + std::cout << "Fragment after fragment " << fragNum << " not available yet" << std::endl; + /* + ///\todo patch this back in? + if (ss.spool()){ + while (Strm.parsePacket(ss.Received())){} + } + */ + } + } + break; + } + } + if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead. + if (myMeta.live){ + if (mstime == 0 && fragNum > 1){ + HTTP_S.Clean(); + HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); + HTTP_S.SendResponse("412", "Fragment out of range", myConn); + HTTP_R.Clean(); //clean for any possible next requests + std::cout << "Fragment " << fragNum << " too old" << std::endl; + continue; + } + } + selectedTracks.clear(); + selectedTracks.insert(tid); + selectedTracks.insert(audioTrack); + seek(mstime); + playUntil = mstime + mslen; + + HTTP_S.Clean(); + HTTP_S.SetHeader("Content-Type", "video/mp4"); + HTTP_S.StartResponse(HTTP_R, myConn); + //send the bootstrap + std::string bootstrap = dynamicBootstrap(tid); + HTTP_S.Chunkify(bootstrap, myConn); + //send a zero-size mdat, meaning it stretches until end of file. + HTTP_S.Chunkify("\000\000\000\000mdat", 8, myConn); + //send init data, if needed. + if (audioTrack > 0){ + tag.DTSCAudioInit(myMeta.tracks[audioTrack]); + tag.tagTime(mstime); + HTTP_S.Chunkify(tag.data, tag.len, myConn); + } + if (tid > 0){ + tag.DTSCVideoInit(myMeta.tracks[tid]); + tag.tagTime(mstime); + HTTP_S.Chunkify(tag.data, tag.len, myConn); + } + parseData = true; + wantRequest = false; + }else{ + myConn.setHost(HTTP_R.GetHeader("X-Origin")); + streamName = HTTP_R.GetHeader("X-Stream"); + initialize(); + std::stringstream tmpstr; + myMeta.toPrettyString(tmpstr); + HTTP_S.Clean(); + HTTP_S.SetHeader("Content-Type", "text/xml"); + HTTP_S.SetHeader("Cache-Control", "no-cache"); + HTTP_S.SetBody(dynamicIndex()); + HTTP_S.SendResponse("200", "OK", myConn); + } + HTTP_R.Clean(); //clean for any possible next requests + } + } +} diff --git a/src/output/output_hds.h b/src/output/output_hds.h new file mode 100644 index 00000000..89732e3b --- /dev/null +++ b/src/output/output_hds.h @@ -0,0 +1,30 @@ +#include "output.h" +#include <mist/http_parser.h> +#include <mist/ts_packet.h> +#include <mist/mp4.h> +#include <mist/mp4_generic.h> + +namespace Mist { + class OutHDS : public Output { + public: + OutHDS(Socket::Connection & conn); + ~OutHDS(); + static void init(Util::Config * cfg); + + void onRequest(); + void onFail(); + void sendNext(); + protected: + void getTracks(); + std::string dynamicBootstrap(int tid); + std::string dynamicIndex(); + HTTP::Parser HTTP_S; + HTTP::Parser HTTP_R; + std::set<int> videoTracks;///<< Holds valid video tracks for playback + long long int audioTrack;///<< Holds audio track ID for playback + long long unsigned int playUntil; + FLV::Tag tag; + }; +} + +typedef Mist::OutHDS mistOut; diff --git a/src/output/output_hls.cpp b/src/output/output_hls.cpp new file mode 100644 index 00000000..b1f3dd22 --- /dev/null +++ b/src/output/output_hls.cpp @@ -0,0 +1,282 @@ +#include "output_hls.h" +#include <mist/defines.h> +#include <mist/http_parser.h> +#include <mist/stream.h> +#include <unistd.h> + +namespace Mist { + ///\brief Builds an index file for HTTP Live streaming. + ///\return The index file for HTTP Live Streaming. + std::string OutHLS::liveIndex(){ + std::stringstream result; + result << "#EXTM3U\r\n"; + int audioId = -1; + std::string audioName; + for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + if (it->second.codec == "AAC"){ + audioId = it->first; + audioName = it->second.getIdentifier(); + break; + } + } + for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + if (it->second.codec == "H264"){ + int bWidth = it->second.bps * 2; + if (audioId != -1){ + bWidth += myMeta.tracks[audioId].bps * 2; + } + result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << bWidth * 10 << "\r\n"; + result << it->first; + if (audioId != -1){ + result << "_" << audioId; + } + result << "/index.m3u8\r\n"; + } + } +#if DEBUG >= 8 + std::cerr << "Sending this index:" << std::endl << result.str() << std::endl; +#endif + return result.str(); + } + + std::string OutHLS::liveIndex(int tid){ + updateMeta(); + std::stringstream result; + //parse single track + int longestFragment = 0; + if (!myMeta.tracks[tid].fragments.size()){ + DEBUG_MSG(DLVL_FAIL, "liveIndex called with track %d, which has no fragments!", tid); + return ""; + } + for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[tid].fragments.begin(); (it + 1) != myMeta.tracks[tid].fragments.end(); it++){ + if (it->getDuration() > longestFragment){ + longestFragment = it->getDuration(); + } + } + result << "#EXTM3U\r\n" + "#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n" + "#EXT-X-MEDIA-SEQUENCE:" << myMeta.tracks[tid].missedFrags << "\r\n"; + for (std::deque<DTSC::Fragment>::iterator it = myMeta.tracks[tid].fragments.begin(); it != myMeta.tracks[tid].fragments.end(); it++){ + long long int starttime = myMeta.tracks[tid].getKey(it->getNumber()).getTime(); + + if (it != (myMeta.tracks[tid].fragments.end() - 1)){ + result << "#EXTINF:" << ((it->getDuration() + 500) / 1000) << ", no desc\r\n" << starttime << "_" << it->getDuration() + starttime << ".ts\r\n"; + } + } + if ( !myMeta.live){ + result << "#EXT-X-ENDLIST\r\n"; + } +#if DEBUG >= 8 + std::cerr << "Sending this index:" << std::endl << result.str() << std::endl; +#endif + return result.str(); + } //liveIndex + + + OutHLS::OutHLS(Socket::Connection & conn) : Output(conn) { + haveAvcc = false; + } + + OutHLS::~OutHLS() {} + + void OutHLS::onFail(){ + HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers + HTTP_S.SetBody("Stream not found. Sorry, we tried."); + HTTP_S.SendResponse("404", "Stream not found", myConn); + Output::onFail(); + } + + void OutHLS::init(Util::Config * cfg){ + capa["name"] = "HTTP_Live"; + capa["desc"] = "Enables HTTP protocol Apple-specific streaming (also known as HLS)."; + capa["deps"] = "HTTP"; + capa["url_rel"] = "/hls/$/index.m3u8"; + capa["url_prefix"] = "/hls/$/"; + capa["socket"] = "http_hls"; + capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][1u].append("AAC"); + capa["methods"][0u]["handler"] = "http"; + capa["methods"][0u]["type"] = "html5/application/vnd.apple.mpegurl"; + capa["methods"][0u]["priority"] = 9ll; + cfg->addBasicConnectorOptions(capa); + config = cfg; + } + + void OutHLS::sendNext(){ + Socket::Buffer ToPack; + char * ContCounter = 0; + bool IsKeyFrame = false; + + char * dataPointer = 0; + int dataLen = 0; + currentPacket.getString("data", dataPointer, dataLen); + + if (currentPacket.getTime() >= until){ + DEBUG_MSG(DLVL_DEVEL, "(%d) Done sending fragment", getpid() ); + stop(); + wantRequest = true; + HTTP_S.Chunkify("", 0, myConn); + HTTP_S.Clean(); + return; + } + + //detect packet type, and put converted data into ToPack. + if (myMeta.tracks[currentPacket.getTrackId()].type == "video"){ + ToPack.append(TS::Packet::getPESVideoLeadIn(0ul, currentPacket.getTime() * 90)); + + IsKeyFrame = currentPacket.getInt("keyframe"); + if (IsKeyFrame){ + if (!haveAvcc){ + avccbox.setPayload(myMeta.tracks[currentPacket.getTrackId()].init); + haveAvcc = true; + } + ToPack.append(avccbox.asAnnexB()); + } + unsigned int i = 0; + while (i + 4 < (unsigned int)dataLen){ + unsigned int ThisNaluSize = (dataPointer[i] << 24) + (dataPointer[i+1] << 16) + (dataPointer[i+2] << 8) + dataPointer[i+3]; + if (ThisNaluSize + i + 4 > (unsigned int)dataLen){ + DEBUG_MSG(DLVL_WARN, "Too big NALU detected (%u > %d) - skipping!", ThisNaluSize + i + 4, dataLen); + break; + } + ToPack.append("\000\000\000\001", 4); + i += 4; + ToPack.append(dataPointer + i, ThisNaluSize); + i += ThisNaluSize; + } + ContCounter = &VideoCounter; + }else if (myMeta.tracks[currentPacket.getTrackId()].type == "audio"){ + if (AppleCompat){ + ToPack.append(TS::Packet::getPESAudioLeadIn(7+dataLen, lastVid)); + }else{ + ToPack.append(TS::Packet::getPESAudioLeadIn(7+dataLen, currentPacket.getTime() * 90)); + } + ToPack.append(TS::GetAudioHeader(dataLen, myMeta.tracks[currentPacket.getTrackId()].init)); + ToPack.append(dataPointer, dataLen); + ContCounter = &AudioCounter; + } + + bool first = true; + //send TS packets + while (ToPack.size()){ + if (PacketNumber % 42 == 0){ + HTTP_S.Chunkify(TS::PAT, 188, myConn); + HTTP_S.Chunkify(TS::PMT, 188, myConn); + PacketNumber += 2; + } + PackData.Clear(); + /// \todo Update according to sendHeader()'s generated data. + //0x100 - 1 + currentPacket.getTrackId() + if (myMeta.tracks[currentPacket.getTrackId()].type == "video"){ + PackData.PID(0x100); + }else{ + PackData.PID(0x101); + } + PackData.ContinuityCounter((*ContCounter)++); + if (first){ + PackData.UnitStart(1); + if (IsKeyFrame){ + PackData.RandomAccess(1); + PackData.PCR(currentPacket.getTime() * 27000); + } + first = false; + } + unsigned int toSend = PackData.AddStuffing(ToPack.bytes(184)); + std::string gonnaSend = ToPack.remove(toSend); + PackData.FillFree(gonnaSend); + HTTP_S.Chunkify(PackData.ToString(), 188, myConn); + PacketNumber ++; + } + } + + int OutHLS::canSeekms(unsigned int ms){ + //no tracks? Frame too new by definition. + if ( !myMeta.tracks.size()){ + return 1; + } + //loop trough all the tracks + for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + //return "too late" if one track is past this point + if (ms < it->second.firstms){ + return -1; + } + //return "too early" if one track is not yet at this point + if (ms > it->second.lastms){ + return 1; + } + } + return 0; + } + + void OutHLS::onRequest(){ + while (HTTP_R.Read(myConn)){ + DEBUG_MSG(DLVL_DEVEL, "Received request: %s", HTTP_R.getUrl().c_str()); + myConn.setHost(HTTP_R.GetHeader("X-Origin")); + AppleCompat = (HTTP_R.GetHeader("User-Agent").find("Apple") != std::string::npos); + streamName = HTTP_R.GetHeader("X-Stream"); + initialize(); + if (HTTP_R.url.find(".m3u") == std::string::npos){ + std::string tmpStr = HTTP_R.getUrl(); + std::string fmtStr = "/hls/" + streamName + "/%u_%u/%llu_%llu.ts"; + long long unsigned int from; + sscanf(tmpStr.c_str(), fmtStr.c_str(), &vidTrack, &audTrack, &from, &until); + DEBUG_MSG(DLVL_DEVEL, "Vid %u, Aud %u, From %llu, Until %llu", vidTrack, audTrack, from, until); + selectedTracks.clear(); + selectedTracks.insert(vidTrack); + selectedTracks.insert(audTrack); + + if (myMeta.live){ + /// \todo Detection of out-of-range parts. + int seekable = canSeekms(from); + if (seekable < 0){ + HTTP_S.Clean(); + HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); + myConn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range")); + HTTP_R.Clean(); //clean for any possible next requests + DEBUG_MSG(DLVL_WARN, "Fragment @ %llu too old", from); + continue; + } + if (seekable > 0){ + HTTP_S.Clean(); + HTTP_S.SetBody("Proxy, re-request this in a second or two.\n"); + myConn.SendNow(HTTP_S.BuildResponse("208", "Ask again later")); + HTTP_R.Clean(); //clean for any possible next requests + DEBUG_MSG(DLVL_WARN, "Fragment @ %llu not available yet", from); + continue; + } + } + + seek(from); + lastVid = from * 90; + + HTTP_S.Clean(); + HTTP_S.SetHeader("Content-Type", "video/mp2t"); + HTTP_S.StartResponse(HTTP_R, myConn); + PacketNumber = 0; + parseData = true; + wantRequest = false; + }else{ + streamName = HTTP_R.GetHeader("X-Stream"); + initialize(); + std::string request = HTTP_R.url.substr(HTTP_R.url.find("/", 5) + 1); + HTTP_S.Clean(); + if (HTTP_R.url.find(".m3u8") != std::string::npos){ + HTTP_S.SetHeader("Content-Type", "audio/x-mpegurl"); + }else{ + HTTP_S.SetHeader("Content-Type", "audio/mpegurl"); + } + HTTP_S.SetHeader("Cache-Control", "no-cache"); + std::string manifest; + if (request.find("/") == std::string::npos){ + manifest = liveIndex(); + }else{ + int selectId = atoi(request.substr(0,request.find("/")).c_str()); + manifest = liveIndex(selectId); + } + HTTP_S.SetBody(manifest); + HTTP_S.SendResponse("200", "OK", myConn); + } + HTTP_R.Clean(); //clean for any possible next requests + } + } +} diff --git a/src/output/output_hls.h b/src/output/output_hls.h new file mode 100644 index 00000000..70031026 --- /dev/null +++ b/src/output/output_hls.h @@ -0,0 +1,39 @@ +#include "output.h" +#include <mist/http_parser.h> +#include <mist/ts_packet.h> +#include <mist/mp4.h> +#include <mist/mp4_generic.h> + +namespace Mist { + class OutHLS : public Output { + public: + OutHLS(Socket::Connection & conn); + ~OutHLS(); + static void init(Util::Config * cfg); + + void onRequest(); + void onFail(); + void sendNext(); + protected: + HTTP::Parser HTTP_S; + HTTP::Parser HTTP_R; + std::string liveIndex(); + std::string liveIndex(int tid); + int canSeekms(unsigned int ms); + int keysToSend; + long long int playUntil; + TS::Packet PackData; + unsigned int PacketNumber; + bool haveAvcc; + char VideoCounter; + char AudioCounter; + MP4::AVCC avccbox; + bool AppleCompat; + long long unsigned int lastVid; + long long unsigned int until; + unsigned int vidTrack; + unsigned int audTrack; + }; +} + +typedef Mist::OutHLS mistOut; diff --git a/src/output/output_hss.cpp b/src/output/output_hss.cpp new file mode 100644 index 00000000..b5146ddf --- /dev/null +++ b/src/output/output_hss.cpp @@ -0,0 +1,484 @@ +#include "output_hss.h" +#include <mist/defines.h> +#include <mist/mp4.h> +#include <mist/mp4_ms.h> +#include <mist/mp4_generic.h> +#include <mist/mp4_encryption.h> +#include <mist/base64.h> +#include <mist/http_parser.h> +#include <mist/stream.h> +#include <unistd.h> + + + +///\todo Maybe move to util? +long long unsigned int binToInt(std::string & binary) { + long long int result = 0; + for (int i = 0; i < 8; i++) { + result <<= 8; + result += binary[i]; + } + return result; +} + +std::string intToBin(long long unsigned int number) { + std::string result; + result.resize(8); + for (int i = 7; i >= 0; i--) { + result[i] = number & 0xFF; + number >>= 8; + } + return result; +} + +std::string toUTF16(std::string original) { + std::string result; + result += (char)0xFF; + result += (char)0xFE; + for (std::string::iterator it = original.begin(); it != original.end(); it++) { + result += (*it); + result += (char)0x00; + } + return result; +} + + + +namespace Mist { + OutHSS::OutHSS(Socket::Connection & conn) : Output(conn) { } + + OutHSS::~OutHSS() {} + + void OutHSS::init(Util::Config * cfg) { + capa["name"] = "HTTP_Smooth"; + capa["desc"] = "Enables HTTP protocol Microsoft-specific smooth streaming through silverlight (also known as HSS)."; + capa["deps"] = "HTTP"; + capa["url_rel"] = "/smooth/$.ism/Manifest"; + capa["url_prefix"] = "/smooth/$.ism/"; + capa["socket"] = "http_hss"; + capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][1u].append("AAC"); + capa["methods"][0u]["handler"] = "http"; + capa["methods"][0u]["type"] = "html5/application/vnd.ms-ss"; + capa["methods"][0u]["priority"] = 9ll; + capa["methods"][0u]["nolive"] = 1; + capa["methods"][1u]["handler"] = "http"; + capa["methods"][1u]["type"] = "silverlight"; + capa["methods"][1u]["priority"] = 1ll; + capa["methods"][1u]["nolive"] = 1; + cfg->addBasicConnectorOptions(capa); + config = cfg; + } + + void OutHSS::sendNext() { + if (currentPacket.getTime() >= playUntil) { + DEBUG_MSG(DLVL_DEVEL, "(%d) Done sending fragment %d:%d", getpid(), myTrackStor, myKeyStor); + stop(); + wantRequest = true; + HTTP_S.Chunkify("", 0, myConn); + HTTP_R.Clean(); + return; + } + char * dataPointer = 0; + int len = 0; + currentPacket.getString("data", dataPointer, len); + HTTP_S.Chunkify(dataPointer, len, myConn); + } + + void OutHSS::onFail(){ + HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers + HTTP_S.SetBody("Stream not found. Sorry, we tried."); + HTTP_S.SendResponse("404", "Stream not found", myConn); + Output::onFail(); + } + + int OutHSS::canSeekms(unsigned int ms) { + //no tracks? Frame too new by definition. + if (!myMeta.tracks.size()) { + DEBUG_MSG(DLVL_DEVEL, "HSS Canseek to %d returns 1 because no tracks", ms); + return 1; + } + //loop trough all selected tracks + for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++) { + //return "too late" if one track is past this point + if (ms < myMeta.tracks[*it].firstms) { + DEBUG_MSG(DLVL_DEVEL, "HSS Canseek to %d returns -1 because track %lu firstms == %d", ms, *it, myMeta.tracks[*it].firstms); + return -1; + } + //return "too early" if one track is not yet at this point + if (ms > myMeta.tracks[*it].lastms) { + DEBUG_MSG(DLVL_DEVEL, "HSS Canseek to %d returns 1 because track %lu lastms == %d", ms, *it, myMeta.tracks[*it].lastms); + return 1; + } + } + return 0; + } + + + void OutHSS::sendHeader() { + //We have a non-manifest request, parse it. + std::string Quality = HTTP_R.url.substr(HTTP_R.url.find("TrackID=", 8) + 8); + Quality = Quality.substr(0, Quality.find(")")); + std::string parseString = HTTP_R.url.substr(HTTP_R.url.find(")/") + 2); + parseString = parseString.substr(parseString.find("(") + 1); + long long int seekTime = atoll(parseString.substr(0, parseString.find(")")).c_str()) / 10000; + unsigned int tid = atoll(Quality.c_str()); + selectedTracks.clear(); + selectedTracks.insert(tid); + if (myMeta.live) { + updateMeta(); + int seekable = canSeekms(seekTime / 10000); + if (seekable == 0){ + // iff the fragment in question is available, check if the next is available too + for (std::deque<DTSC::Key>::iterator it = myMeta.tracks[tid].keys.begin(); it != myMeta.tracks[tid].keys.end(); it++){ + if (it->getTime() >= (seekTime / 10000)){ + if ((it + 1) == myMeta.tracks[tid].keys.end()){ + seekable = 1; + } + break; + } + } + } + if (seekable < 0){ + HTTP_S.Clean(); + HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); + myConn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range")); + HTTP_R.Clean(); //clean for any possible next requests + std::cout << "Fragment @ " << seekTime / 10000 << "ms too old (" << myMeta.tracks[tid].firstms << " - " << myMeta.tracks[tid].lastms << " ms)" << std::endl; + stop(); + wantRequest = true; + return; + } + if (seekable > 0){ + HTTP_S.Clean(); + HTTP_S.SetBody("Proxy, re-request this in a second or two.\n"); + myConn.SendNow(HTTP_S.BuildResponse("208", "Ask again later")); + HTTP_R.Clean(); //clean for any possible next requests + std::cout << "Fragment @ " << seekTime / 10000 << "ms not available yet (" << myMeta.tracks[tid].firstms << " - " << myMeta.tracks[tid].lastms << " ms)" << std::endl; + stop(); + wantRequest = true; + return; + } + } + DEBUG_MSG(DLVL_DEVEL, "(%d) Seeking to time %lld on track %d", getpid(), seekTime, tid); + seek(seekTime); + playUntil = (*(keyTimes[tid].upper_bound(seekTime))); + DEBUG_MSG(DLVL_DEVEL, "Set playUntil to %lld", playUntil); + myTrackStor = tid; + myKeyStor = seekTime; + keysToSend = 1; + //Seek to the right place and send a play-once for a single fragment. + std::stringstream sstream; + + int partOffset = 0; + int keyDur = 0; + DTSC::Key keyObj; + for (std::deque<DTSC::Key>::iterator it = myMeta.tracks[tid].keys.begin(); it != myMeta.tracks[tid].keys.end(); it++) { + if (it->getTime() >= seekTime) { + keyObj = (*it); + keyDur = it->getLength(); + std::deque<DTSC::Key>::iterator nextIt = it; + nextIt++; + if (nextIt == myMeta.tracks[tid].keys.end()) { + if (myMeta.live) { + HTTP_S.Clean(); + HTTP_S.SetBody("Proxy, re-request this in a second or two.\n"); + myConn.SendNow(HTTP_S.BuildResponse("208", "Ask again later")); + HTTP_R.Clean(); //clean for any possible next requests + std::cout << "Fragment after fragment @ " << (seekTime / 10000) << " not available yet" << std::endl; + } + } + break; + } + partOffset += it->getParts(); + } + if (HTTP_R.url == "/") { + return; //Don't continue, but continue instead. + } + /* + if (myMeta.live) { + if (mstime == 0 && (seekTime / 10000) > 1){ + HTTP_S.Clean(); + HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n"); + myConn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range")); + HTTP_R.Clean(); //clean for any possible next requests + std::cout << "Fragment @ " << (seekTime / 10000) << " too old" << std::endl; + continue; + } + } + */ + + ///\todo Select correct track (tid); + + //Wrap everything in mp4 boxes + MP4::MFHD mfhd_box; + mfhd_box.setSequenceNumber(((keyObj.getNumber() - 1) * 2) + tid);///\todo Urgent: Check this for multitrack... :P wtf... :P + + MP4::TFHD tfhd_box; + tfhd_box.setFlags(MP4::tfhdSampleFlag); + tfhd_box.setTrackID(tid); + if (myMeta.tracks[tid].type == "video") { + tfhd_box.setDefaultSampleFlags(0x00004001); + } else { + tfhd_box.setDefaultSampleFlags(0x00008002); + } + + MP4::TRUN trun_box; + trun_box.setDataOffset(42);///\todo Check if this is a placeholder, or an actually correct number + unsigned int keySize = 0; + if (myMeta.tracks[tid].type == "video") { + trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize | MP4::trunsampleOffsets); + } else { + trun_box.setFlags(MP4::trundataOffset | MP4::trunsampleDuration | MP4::trunsampleSize); + } + trun_box.setFirstSampleFlags(0x00004002); + for (int i = 0; i < keyObj.getParts(); i++) { + MP4::trunSampleInformation trunSample; + trunSample.sampleSize = myMeta.tracks[tid].parts[i + partOffset].getSize(); + keySize += myMeta.tracks[tid].parts[i + partOffset].getSize(); + trunSample.sampleDuration = myMeta.tracks[tid].parts[i + partOffset].getDuration() * 10000; + if (myMeta.tracks[tid].type == "video") { + trunSample.sampleOffset = myMeta.tracks[tid].parts[i + partOffset].getOffset() * 10000; + } + trun_box.setSampleInformation(trunSample, i); + } + + MP4::SDTP sdtp_box; + sdtp_box.setVersion(0); + if (myMeta.tracks[tid].type == "video") { + sdtp_box.setValue(36, 4); + for (int i = 1; i < keyObj.getParts(); i++) { + sdtp_box.setValue(20, 4 + i); + } + } else { + sdtp_box.setValue(40, 4); + for (int i = 1; i < keyObj.getParts(); i++) { + sdtp_box.setValue(40, 4 + i); + } + } + + MP4::TRAF traf_box; + traf_box.setContent(tfhd_box, 0); + traf_box.setContent(trun_box, 1); + traf_box.setContent(sdtp_box, 2); + + //If the stream is live, we want to have a fragref box if possible + //////HEREHEREHERE + if (myMeta.live) { + MP4::UUID_TrackFragmentReference fragref_box; + fragref_box.setVersion(1); + fragref_box.setFragmentCount(0); + int fragCount = 0; + for (unsigned int i = 0; fragCount < 2 && i < myMeta.tracks[tid].keys.size() - 1; i++) { + if (myMeta.tracks[tid].keys[i].getTime() > seekTime) { + DEBUG_MSG(DLVL_DEVEL, "Key %d added to fragRef box, time %ld > %lld", i, myMeta.tracks[tid].keys[i].getTime(), seekTime); + fragref_box.setTime(fragCount, myMeta.tracks[tid].keys[i].getTime() * 10000); + fragref_box.setDuration(fragCount, myMeta.tracks[tid].keys[i].getLength() * 10000); + fragref_box.setFragmentCount(++fragCount); + } + } + traf_box.setContent(fragref_box, 3); + } + + MP4::MOOF moof_box; + moof_box.setContent(mfhd_box, 0); + moof_box.setContent(traf_box, 1); + //Setting the correct offsets. + moof_box.setContent(traf_box, 1); + trun_box.setDataOffset(moof_box.boxedSize() + 8); + traf_box.setContent(trun_box, 1); + moof_box.setContent(traf_box, 1); + + HTTP_S.Clean(); + HTTP_S.SetHeader("Content-Type", "video/mp4"); + HTTP_S.StartResponse(HTTP_R, myConn); + HTTP_S.Chunkify(moof_box.asBox(), moof_box.boxedSize(), myConn); + int size = htonl(keySize + 8); + HTTP_S.Chunkify((char *)&size, 4, myConn); + HTTP_S.Chunkify("mdat", 4, myConn); + sentHeader = true; + HTTP_R.Clean(); + DEBUG_MSG(DLVL_DEVEL, "(%d) Sent full header", getpid()); + } + + + ///\brief Builds an index file for HTTP Smooth streaming. + ///\return The index file for HTTP Smooth Streaming. + std::string OutHSS::smoothIndex(){ + updateMeta(); + std::stringstream Result; + Result << "<?xml version=\"1.0\" encoding=\"utf-16\"?>\n"; + Result << "<SmoothStreamingMedia " + "MajorVersion=\"2\" " + "MinorVersion=\"0\" " + "TimeScale=\"10000000\" "; + std::deque<std::map<int, DTSC::Track>::iterator> audioIters; + std::deque<std::map<int, DTSC::Track>::iterator> videoIters; + long long int maxWidth = 0; + long long int maxHeight = 0; + long long int minWidth = 99999999; + long long int minHeight = 99999999; + for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { + if (it->second.codec == "AAC") { + audioIters.push_back(it); + } + if (it->second.codec == "H264") { + videoIters.push_back(it); + if (it->second.width > maxWidth) { + maxWidth = it->second.width; + } + if (it->second.width < minWidth) { + minWidth = it->second.width; + } + if (it->second.height > maxHeight) { + maxHeight = it->second.height; + } + if (it->second.height < minHeight) { + minHeight = it->second.height; + } + } + } + DEBUG_MSG(DLVL_DEVEL, "Buffer window here %lld", myMeta.bufferWindow); + if (myMeta.vod) { + Result << "Duration=\"" << (*videoIters.begin())->second.lastms << "0000\""; + } else { + Result << "Duration=\"0\" " + "IsLive=\"TRUE\" " + "LookAheadFragmentCount=\"2\" " + "DVRWindowLength=\"" << myMeta.bufferWindow << "0000\" " + "CanSeek=\"TRUE\" " + "CanPause=\"TRUE\" "; + } + Result << ">\n"; + + //Add audio entries + if (audioIters.size()) { + Result << "<StreamIndex " + "Type=\"audio\" " + "QualityLevels=\"" << audioIters.size() << "\" " + "Name=\"audio\" " + "Chunks=\"" << (*audioIters.begin())->second.keys.size() << "\" " + "Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n"; + int index = 0; + for (std::deque<std::map<int, DTSC::Track>::iterator>::iterator it = audioIters.begin(); it != audioIters.end(); it++) { + Result << "<QualityLevel " + "Index=\"" << index << "\" " + "Bitrate=\"" << (*it)->second.bps * 8 << "\" " + "CodecPrivateData=\"" << std::hex; + for (unsigned int i = 0; i < (*it)->second.init.size(); i++) { + Result << std::setfill('0') << std::setw(2) << std::right << (int)(*it)->second.init[i]; + } + Result << std::dec << "\" " + "SamplingRate=\"" << (*it)->second.rate << "\" " + "Channels=\"2\" " + "BitsPerSample=\"16\" " + "PacketSize=\"4\" " + "AudioTag=\"255\" " + "FourCC=\"AACL\" >\n"; + Result << "<CustomAttributes>\n" + "<Attribute Name = \"TrackID\" Value = \"" << (*it)->first << "\" />" + "</CustomAttributes>"; + Result << "</QualityLevel>\n"; + index++; + } + if ((*audioIters.begin())->second.keys.size()) { + for (std::deque<DTSC::Key>::iterator it = (*audioIters.begin())->second.keys.begin(); it != (((*audioIters.begin())->second.keys.end()) - 1); it++) { + Result << "<c "; + if (it == (*audioIters.begin())->second.keys.begin()) { + Result << "t=\"" << it->getTime() * 10000 << "\" "; + } + Result << "d=\"" << it->getLength() * 10000 << "\" />\n"; + } + } + Result << "</StreamIndex>\n"; + } + //Add video entries + if (videoIters.size()) { + Result << "<StreamIndex " + "Type=\"video\" " + "QualityLevels=\"" << videoIters.size() << "\" " + "Name=\"video\" " + "Chunks=\"" << (*videoIters.begin())->second.keys.size() << "\" " + "Url=\"Q({bitrate},{CustomAttributes})/V({start time})\" " + "MaxWidth=\"" << maxWidth << "\" " + "MaxHeight=\"" << maxHeight << "\" " + "DisplayWidth=\"" << maxWidth << "\" " + "DisplayHeight=\"" << maxHeight << "\">\n"; + int index = 0; + for (std::deque<std::map<int, DTSC::Track>::iterator>::iterator it = videoIters.begin(); it != videoIters.end(); it++) { + //Add video qualities + Result << "<QualityLevel " + "Index=\"" << index << "\" " + "Bitrate=\"" << (*it)->second.bps * 8 << "\" " + "CodecPrivateData=\"" << std::hex; + MP4::AVCC avccbox; + avccbox.setPayload((*it)->second.init); + std::string tmpString = avccbox.asAnnexB(); + for (unsigned int i = 0; i < tmpString.size(); i++) { + Result << std::setfill('0') << std::setw(2) << std::right << (int)tmpString[i]; + } + Result << std::dec << "\" " + "MaxWidth=\"" << (*it)->second.width << "\" " + "MaxHeight=\"" << (*it)->second.height << "\" " + "FourCC=\"AVC1\" >\n"; + Result << "<CustomAttributes>\n" + "<Attribute Name = \"TrackID\" Value = \"" << (*it)->first << "\" />" + "</CustomAttributes>"; + Result << "</QualityLevel>\n"; + index++; + } + if ((*videoIters.begin())->second.keys.size()) { + for (std::deque<DTSC::Key>::iterator it = (*videoIters.begin())->second.keys.begin(); it != (((*videoIters.begin())->second.keys.end()) - 1); it++) { + Result << "<c "; + if (it == (*videoIters.begin())->second.keys.begin()) { + Result << "t=\"" << it->getTime() * 10000 << "\" "; + } + Result << "d=\"" << it->getLength() * 10000 << "\" />\n"; + } + } + Result << "</StreamIndex>\n"; + } + Result << "</SmoothStreamingMedia>\n"; + +#if DEBUG >= 8 + std::cerr << "Sending this manifest:" << std::endl << Result << std::endl; +#endif + return toUTF16(Result.str()); + } //smoothIndex + + + void OutHSS::onRequest() { + sentHeader = false; + while (HTTP_R.Read(myConn)) { + DEBUG_MSG(DLVL_DEVEL, "(%d) Received request %s", getpid(), HTTP_R.getUrl().c_str()); + myConn.setHost(HTTP_R.GetHeader("X-Origin")); + streamName = HTTP_R.GetHeader("X-Stream"); + initialize(); + if (HTTP_R.url.find("Manifest") != std::string::npos) { + //Manifest, direct reply + HTTP_S.Clean(); + HTTP_S.SetHeader("Content-Type", "text/xml"); + HTTP_S.SetHeader("Cache-Control", "no-cache"); + std::string manifest = smoothIndex(); + HTTP_S.SetBody(manifest); + HTTP_S.SendResponse("200", "OK", myConn); + HTTP_R.Clean(); + } else { + parseData = true; + wantRequest = false; + } + } + } + + void OutHSS::initialize() { + Output::initialize(); + for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) { + for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++) { + keyTimes[it->first].insert(it2->getTime()); + } + } + } + + +} + diff --git a/src/output/output_hss.h b/src/output/output_hss.h new file mode 100644 index 00000000..15f2d036 --- /dev/null +++ b/src/output/output_hss.h @@ -0,0 +1,29 @@ +#include "output.h" +#include <mist/http_parser.h> + +namespace Mist { + class OutHSS : public Output { + public: + OutHSS(Socket::Connection & conn); + ~OutHSS(); + static void init(Util::Config * cfg); + + void onRequest(); + void sendNext(); + void initialize(); + void onFail(); + void sendHeader(); + protected: + HTTP::Parser HTTP_S; + HTTP::Parser HTTP_R; + JSON::Value encryption; + std::string smoothIndex(); + int canSeekms(unsigned int ms); + int keysToSend; + int myTrackStor; + int myKeyStor; + long long int playUntil; + }; +} + +typedef Mist::OutHSS mistOut; diff --git a/src/output/output_json.cpp b/src/output/output_json.cpp new file mode 100644 index 00000000..94a83e5f --- /dev/null +++ b/src/output/output_json.cpp @@ -0,0 +1,84 @@ +#include "output_json.h" +#include <mist/http_parser.h> +#include <mist/defines.h> +#include <iomanip> + +namespace Mist { + OutJSON::OutJSON(Socket::Connection & conn) : Output(conn){ + realTime = 0; + } + + OutJSON::~OutJSON() {} + + void OutJSON::init(Util::Config * cfg){ + capa["desc"] = "Enables HTTP protocol JSON streaming."; + capa["deps"] = "HTTP"; + capa["url_rel"] = "/$.json"; + capa["url_match"] = "/$.json"; + capa["url_handler"] = "http"; + capa["url_type"] = "json"; + capa["socket"] = "http_json"; + cfg->addBasicConnectorOptions(capa); + config = cfg; + } + + void OutJSON::sendNext(){ + if(!first) { + myConn.SendNow(", ", 2); + }else{ + if (jsonp == ""){ + myConn.SendNow("[", 1); + }else{ + myConn.SendNow(jsonp + "(["); + } + first = false; + } + myConn.SendNow(currentPacket.toJSON().toString()); + } + + void OutJSON::sendHeader(){ + HTTP::Parser HTTP_S; + FLV::Tag tag; + HTTP_S.SetHeader("Content-Type", "text/javascript"); + HTTP_S.protocol = "HTTP/1.0"; + myConn.SendNow(HTTP_S.BuildResponse("200", "OK")); + sentHeader = true; + } + + bool OutJSON::onFinish(){ + if (jsonp == ""){ + myConn.SendNow("]\n\n", 3); + }else{ + myConn.SendNow("]);\n\n", 5); + } + return false; + } + + void OutJSON::onRequest(){ + HTTP::Parser HTTP_R; + while (HTTP_R.Read(myConn)){ + DEBUG_MSG(DLVL_DEVEL, "Received request %s", HTTP_R.getUrl().c_str()); + first = true; + myConn.setHost(HTTP_R.GetHeader("X-Origin")); + streamName = HTTP_R.GetHeader("X-Stream"); + jsonp = ""; + if (HTTP_R.GetVar("callback") != ""){ + jsonp = HTTP_R.GetVar("callback"); + } + if (HTTP_R.GetVar("jsonp") != ""){ + jsonp = HTTP_R.GetVar("jsonp"); + } + initialize(); + for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){ + if (it->second.type == "meta" ){ + selectedTracks.insert(it->first); + } + } + seek(0); + parseData = true; + wantRequest = false; + HTTP_R.Clean(); + } + } + +} diff --git a/src/output/output_json.h b/src/output/output_json.h new file mode 100644 index 00000000..281a0bc9 --- /dev/null +++ b/src/output/output_json.h @@ -0,0 +1,20 @@ +#include "output.h" + + +namespace Mist { + class OutJSON : public Output { + public: + OutJSON(Socket::Connection & conn); + ~OutJSON(); + static void init(Util::Config * cfg); + void onRequest(); + bool onFinish(); + void sendNext(); + void sendHeader(); + protected: + std::string jsonp; + bool first; + }; +} + +typedef Mist::OutJSON mistOut; diff --git a/src/output/output_progressive_flv.cpp b/src/output/output_progressive_flv.cpp new file mode 100644 index 00000000..36c2ec51 --- /dev/null +++ b/src/output/output_progressive_flv.cpp @@ -0,0 +1,88 @@ +#include "output_progressive_flv.h" +#include <mist/http_parser.h> +#include <mist/defines.h> + +namespace Mist { + OutProgressiveFLV::OutProgressiveFLV(Socket::Connection & conn) : Output(conn) { } + + OutProgressiveFLV::~OutProgressiveFLV() {} + + void OutProgressiveFLV::init(Util::Config * cfg){ + capa["name"] = "HTTP_Progressive_FLV"; + capa["desc"] = "Enables HTTP protocol progressive streaming."; + capa["deps"] = "HTTP"; + capa["url_rel"] = "/$.flv"; + capa["url_match"] = "/$.flv"; + capa["socket"] = "http_progressive_flv"; + capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][0u].append("H263"); + capa["codecs"][0u][0u].append("VP6"); + capa["codecs"][0u][1u].append("AAC"); + capa["codecs"][0u][1u].append("MP3"); + capa["methods"][0u]["handler"] = "http"; + capa["methods"][0u]["type"] = "flash/7"; + capa["methods"][0u]["priority"] = 5ll; + + cfg->addBasicConnectorOptions(capa); + config = cfg; + } + + void OutProgressiveFLV::sendNext(){ + FLV::Tag tag; + bool tmp = tag.DTSCLoader(currentPacket, myMeta.tracks[currentPacket.getTrackId()]); + if (!tmp){ + DEBUG_MSG(DLVL_DEVEL, "Invalid JSON"); + } + myConn.SendNow(tag.data, tag.len); + } + + void OutProgressiveFLV::sendHeader(){ + HTTP::Parser HTTP_S; + FLV::Tag tag; + HTTP_S.SetHeader("Content-Type", "video/x-flv"); + HTTP_S.protocol = "HTTP/1.0"; + myConn.SendNow(HTTP_S.BuildResponse("200", "OK")); + myConn.SendNow(FLV::Header, 13); + tag.DTSCMetaInit(myMeta, selectedTracks); + myConn.SendNow(tag.data, tag.len); + + for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + if (myMeta.tracks[*it].type == "video"){ + tag.DTSCVideoInit(myMeta.tracks[*it]); + myConn.SendNow(tag.data, tag.len); + } + if (myMeta.tracks[*it].type == "audio"){ + tag.DTSCAudioInit(myMeta.tracks[*it]); + myConn.SendNow(tag.data, tag.len); + } + } + sentHeader = true; + } + + void OutProgressiveFLV::onFail(){ + HTTP::Parser HTTP_S; + HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers + HTTP_S.SetBody("Stream not found. Sorry, we tried."); + HTTP_S.SendResponse("404", "Stream not found", myConn); + Output::onFail(); + } + + void OutProgressiveFLV::onRequest(){ + HTTP::Parser HTTP_R; + while (HTTP_R.Read(myConn)){ + DEBUG_MSG(DLVL_DEVEL, "Received request %s", HTTP_R.getUrl().c_str()); + if (HTTP_R.GetVar("audio") != ""){ + selectedTracks.insert(JSON::Value(HTTP_R.GetVar("audio")).asInt()); + } + if (HTTP_R.GetVar("video") != ""){ + selectedTracks.insert(JSON::Value(HTTP_R.GetVar("video")).asInt()); + } + myConn.setHost(HTTP_R.GetHeader("X-Origin")); + streamName = HTTP_R.GetHeader("X-Stream"); + parseData = true; + wantRequest = false; + HTTP_R.Clean(); + } + } + +} diff --git a/src/output/output_progressive_flv.h b/src/output/output_progressive_flv.h new file mode 100644 index 00000000..aee4d066 --- /dev/null +++ b/src/output/output_progressive_flv.h @@ -0,0 +1,18 @@ +#include "output.h" + + +namespace Mist { + class OutProgressiveFLV : public Output { + public: + OutProgressiveFLV(Socket::Connection & conn); + ~OutProgressiveFLV(); + static void init(Util::Config * cfg); + void onRequest(); + void sendNext(); + void onFail(); + void sendHeader(); + protected: + }; +} + +typedef Mist::OutProgressiveFLV mistOut; diff --git a/src/output/output_progressive_mp3.cpp b/src/output/output_progressive_mp3.cpp new file mode 100644 index 00000000..80d37abf --- /dev/null +++ b/src/output/output_progressive_mp3.cpp @@ -0,0 +1,65 @@ +#include "output_progressive_mp3.h" +#include <mist/http_parser.h> +#include <mist/defines.h> + +namespace Mist { + OutProgressiveMP3::OutProgressiveMP3(Socket::Connection & conn) : Output(conn) { } + + OutProgressiveMP3::~OutProgressiveMP3() {} + + void OutProgressiveMP3::init(Util::Config * cfg){ + capa["name"] = "HTTP_Progressive_MP3"; + capa["desc"] = "Enables HTTP protocol progressive streaming."; + capa["deps"] = "HTTP"; + capa["url_rel"] = "/$.mp3"; + capa["url_match"] = "/$.mp3"; + capa["socket"] = "http_progressive_mp3"; + capa["codecs"][0u][0u].append("MP3"); + capa["methods"][0u]["handler"] = "http"; + capa["methods"][0u]["type"] = "mp3"; + capa["methods"][0u]["priority"] = 8ll; + + cfg->addBasicConnectorOptions(capa); + config = cfg; + } + + void OutProgressiveMP3::sendNext(){ + char * dataPointer = 0; + int len = 0; + currentPacket.getString("data", dataPointer, len); + myConn.SendNow(dataPointer, len); + } + + void OutProgressiveMP3::sendHeader(){ + HTTP::Parser HTTP_S; + FLV::Tag tag; + HTTP_S.SetHeader("Content-Type", "audio/mpeg"); + HTTP_S.protocol = "HTTP/1.0"; + myConn.SendNow(HTTP_S.BuildResponse("200", "OK")); + sentHeader = true; + } + + void OutProgressiveMP3::onFail(){ + HTTP::Parser HTTP_S; + HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers + HTTP_S.SetBody("Stream not found. Sorry, we tried."); + HTTP_S.SendResponse("404", "Stream not found", myConn); + Output::onFail(); + } + + void OutProgressiveMP3::onRequest(){ + HTTP::Parser HTTP_R; + while (HTTP_R.Read(myConn)){ + DEBUG_MSG(DLVL_DEVEL, "Received request %s", HTTP_R.getUrl().c_str()); + if (HTTP_R.GetVar("audio") != ""){ + selectedTracks.insert(JSON::Value(HTTP_R.GetVar("audio")).asInt()); + } + myConn.setHost(HTTP_R.GetHeader("X-Origin")); + streamName = HTTP_R.GetHeader("X-Stream"); + parseData = true; + wantRequest = false; + HTTP_R.Clean(); + } + } + +} diff --git a/src/output/output_progressive_mp3.h b/src/output/output_progressive_mp3.h new file mode 100644 index 00000000..b7cb5a19 --- /dev/null +++ b/src/output/output_progressive_mp3.h @@ -0,0 +1,18 @@ +#include "output.h" + + +namespace Mist { + class OutProgressiveMP3 : public Output { + public: + OutProgressiveMP3(Socket::Connection & conn); + ~OutProgressiveMP3(); + static void init(Util::Config * cfg); + void onRequest(); + void sendNext(); + void onFail(); + void sendHeader(); + protected: + }; +} + +typedef Mist::OutProgressiveMP3 mistOut; diff --git a/src/output/output_progressive_mp4.cpp b/src/output/output_progressive_mp4.cpp new file mode 100644 index 00000000..4b49420f --- /dev/null +++ b/src/output/output_progressive_mp4.cpp @@ -0,0 +1,558 @@ +#include "output_progressive_mp4.h" +#include <mist/defines.h> +#include <mist/mp4.h> +#include <mist/mp4_generic.h> + +namespace Mist { + OutProgressiveMP4::OutProgressiveMP4(Socket::Connection & conn) : Output(conn) { } + + OutProgressiveMP4::~OutProgressiveMP4() {} + + void OutProgressiveMP4::init(Util::Config * cfg){ + capa["name"] = "HTTP_Progressive_MP4"; + capa["desc"] = "Enables HTTP protocol progressive streaming."; + capa["deps"] = "HTTP"; + capa["url_rel"] = "/$.mp4"; + capa["url_match"] = "/$.mp4"; + capa["socket"] = "http_progressive_mp4"; + capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][1u].append("AAC"); + capa["methods"][0u]["handler"] = "http"; + capa["methods"][0u]["type"] = "html5/video/mp4"; + capa["methods"][0u]["priority"] = 8ll; + capa["methods"][0u]["nolive"] = 1; + + + cfg->addBasicConnectorOptions(capa); + config = cfg; + } + + std::string OutProgressiveMP4::DTSCMeta2MP4Header(long long & size){ + std::stringstream header; + //ftyp box + MP4::FTYP ftypBox; + header << std::string(ftypBox.asBox(),ftypBox.boxedSize()); + + uint64_t mdatSize = 0; + //moov box + MP4::MOOV moovBox; + unsigned int moovOffset = 0; + { + //calculating longest duration + long long int firstms = -1; + long long int lastms = -1; + for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++) { + if (lastms == -1 || lastms < myMeta.tracks[*it].lastms){ + lastms = myMeta.tracks[*it].lastms; + } + if (firstms == -1 || firstms > myMeta.tracks[*it].firstms){ + firstms = myMeta.tracks[*it].firstms; + } + } + MP4::MVHD mvhdBox(lastms - firstms); + moovBox.setContent(mvhdBox, moovOffset++); + } + for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++) { + MP4::TRAK trakBox; + { + { + MP4::TKHD tkhdBox(*it, myMeta.tracks[*it].lastms - myMeta.tracks[*it].firstms, myMeta.tracks[*it].width, myMeta.tracks[*it].height); + trakBox.setContent(tkhdBox, 0); + }{ + MP4::MDIA mdiaBox; + unsigned int mdiaOffset = 0; + { + MP4::MDHD mdhdBox(myMeta.tracks[*it].lastms - myMeta.tracks[*it].firstms); + mdiaBox.setContent(mdhdBox, mdiaOffset++); + }//MDHD box + { + MP4::HDLR hdlrBox(myMeta.tracks[*it].type, myMeta.tracks[*it].getIdentifier()); + mdiaBox.setContent(hdlrBox, mdiaOffset++); + }//hdlr box + { + MP4::MINF minfBox; + unsigned int minfOffset = 0; + if (myMeta.tracks[*it].type== "video"){ + MP4::VMHD vmhdBox; + vmhdBox.setFlags(1); + minfBox.setContent(vmhdBox,minfOffset++); + }else if (myMeta.tracks[*it].type == "audio"){ + MP4::SMHD smhdBox; + minfBox.setContent(smhdBox,minfOffset++); + }//type box + { + MP4::DINF dinfBox; + MP4::DREF drefBox; + dinfBox.setContent(drefBox,0); + minfBox.setContent(dinfBox,minfOffset++); + }//dinf box + { + MP4::STBL stblBox; + unsigned int offset = 0; + { + MP4::STSD stsdBox; + stsdBox.setVersion(0); + if (myMeta.tracks[*it].type == "video"){//boxname = codec + MP4::VisualSampleEntry vse; + if (myMeta.tracks[*it].codec == "H264"){ + vse.setCodec("avc1"); + } + vse.setDataReferenceIndex(1); + vse.setWidth(myMeta.tracks[*it].width); + vse.setHeight(myMeta.tracks[*it].height); + MP4::AVCC avccBox; + avccBox.setPayload(myMeta.tracks[*it].init); + vse.setCLAP(avccBox); + stsdBox.setEntry(vse,0); + }else if(myMeta.tracks[*it].type == "audio"){//boxname = codec + MP4::AudioSampleEntry ase; + if (myMeta.tracks[*it].codec == "AAC"){ + ase.setCodec("mp4a"); + ase.setDataReferenceIndex(1); + } + ase.setSampleRate(myMeta.tracks[*it].rate); + ase.setChannelCount(myMeta.tracks[*it].channels); + ase.setSampleSize(myMeta.tracks[*it].size); + //MP4::ESDS esdsBox(myMeta.tracks[*it].init, myMeta.tracks[*it].bps); + MP4::ESDS esdsBox; + + //outputting these values first, so malloc isn't called as often. + esdsBox.setESHeaderStartCodes(myMeta.tracks[*it].init); + esdsBox.setSLValue(2); + + esdsBox.setESDescriptorTypeLength(32+myMeta.tracks[*it].init.size()); + esdsBox.setESID(2); + esdsBox.setStreamPriority(0); + esdsBox.setDecoderConfigDescriptorTypeLength(18 + myMeta.tracks[*it].init.size()); + esdsBox.setByteObjectTypeID(0x40); + esdsBox.setStreamType(5); + esdsBox.setReservedFlag(1); + esdsBox.setBufferSize(1250000); + esdsBox.setMaximumBitRate(10000000); + esdsBox.setAverageBitRate(myMeta.tracks[*it].bps * 8); + esdsBox.setConfigDescriptorTypeLength(5); + esdsBox.setSLConfigDescriptorTypeTag(0x6); + esdsBox.setSLConfigExtendedDescriptorTypeTag(0x808080); + esdsBox.setSLDescriptorTypeLength(1); + ase.setCodecBox(esdsBox); + stsdBox.setEntry(ase,0); + } + stblBox.setContent(stsdBox,offset++); + }//stsd box + { + MP4::STTS sttsBox; + sttsBox.setVersion(0); + if (myMeta.tracks[*it].parts.size()){ + for (unsigned int part = 0; part < myMeta.tracks[*it].parts.size(); part++){ + MP4::STTSEntry newEntry; + newEntry.sampleCount = 1; + newEntry.sampleDelta = myMeta.tracks[*it].parts[part].getDuration(); + sttsBox.setSTTSEntry(newEntry, part); + } + } + stblBox.setContent(sttsBox,offset++); + }//stts box + if (myMeta.tracks[*it].type == "video"){ + //STSS Box here + MP4::STSS stssBox; + stssBox.setVersion(0); + int tmpCount = 0; + int tmpItCount = 0; + for ( std::deque< DTSC::Key>::iterator tmpIt = myMeta.tracks[*it].keys.begin(); tmpIt != myMeta.tracks[*it].keys.end(); tmpIt ++) { + stssBox.setSampleNumber(tmpCount,tmpItCount); + tmpCount += tmpIt->getParts(); + tmpItCount ++; + } + stblBox.setContent(stssBox,offset++); + }//stss box + { + MP4::STSC stscBox; + stscBox.setVersion(0); + MP4::STSCEntry stscEntry; + stscEntry.firstChunk = 1; + stscEntry.samplesPerChunk = 1; + stscEntry.sampleDescriptionIndex = 1; + stscBox.setSTSCEntry(stscEntry, 0); + stblBox.setContent(stscBox,offset++); + }//stsc box + { + uint32_t total = 0; + MP4::STSZ stszBox; + stszBox.setVersion(0); + total = 0; + for (std::deque< DTSC::Part>::iterator partIt = myMeta.tracks[*it].parts.begin(); partIt != myMeta.tracks[*it].parts.end(); partIt ++) { + stszBox.setEntrySize(partIt->getSize(), total);//in bytes in file + size += partIt->getSize(); + total++; + } + stblBox.setContent(stszBox,offset++); + }//stsz box + //add STCO boxes here + { + MP4::STCO stcoBox; + stcoBox.setVersion(1); + //Inserting empty values on purpose here, will be fixed later. + if (myMeta.tracks[*it].parts.size() != 0){ + stcoBox.setChunkOffset(0, myMeta.tracks[*it].parts.size() - 1);//this inserts all empty entries at once + } + stblBox.setContent(stcoBox,offset++); + }//stco box + minfBox.setContent(stblBox,minfOffset++); + }//stbl box + mdiaBox.setContent(minfBox, mdiaOffset++); + }//minf box + trakBox.setContent(mdiaBox, 1); + } + }//trak Box + moovBox.setContent(trakBox, moovOffset++); + } + //initial offset length ftyp, length moov + 8 + unsigned long long int byteOffset = ftypBox.boxedSize() + moovBox.boxedSize() + 8; + //update all STCO from the following map; + std::map <int, MP4::STCO> checkStcoBoxes; + //for all tracks + for (unsigned int i = 1; i < moovBox.getContentCount(); i++){ + //10 lines to get the STCO box. + MP4::TRAK checkTrakBox; + MP4::Box checkMdiaBox; + MP4::Box checkTkhdBox; + MP4::MINF checkMinfBox; + MP4::STBL checkStblBox; + //MP4::STCO checkStcoBox; + checkTrakBox = ((MP4::TRAK&)moovBox.getContent(i)); + for (unsigned int j = 0; j < checkTrakBox.getContentCount(); j++){ + if (checkTrakBox.getContent(j).isType("mdia")){ + checkMdiaBox = checkTrakBox.getContent(j); + break; + } + if (checkTrakBox.getContent(j).isType("tkhd")){ + checkTkhdBox = checkTrakBox.getContent(j); + } + } + for (unsigned int j = 0; j < ((MP4::MDIA&)checkMdiaBox).getContentCount(); j++){ + if (((MP4::MDIA&)checkMdiaBox).getContent(j).isType("minf")){ + checkMinfBox = ((MP4::MINF&)((MP4::MDIA&)checkMdiaBox).getContent(j)); + break; + } + } + for (unsigned int j = 0; j < checkMinfBox.getContentCount(); j++){ + if (checkMinfBox.getContent(j).isType("stbl")){ + checkStblBox = ((MP4::STBL&)checkMinfBox.getContent(j)); + break; + } + } + for (unsigned int j = 0; j < checkStblBox.getContentCount(); j++){ + if (checkStblBox.getContent(j).isType("stco")){ + checkStcoBoxes.insert( std::pair<int, MP4::STCO>(((MP4::TKHD&)checkTkhdBox).getTrackID(), ((MP4::STCO&)checkStblBox.getContent(j)) )); + break; + } + } + } + //inserting right values in the STCO box header + //total = 0; + long long unsigned int totalByteOffset = 0; + //Current values are actual byte offset without header-sized offset + std::set <keyPart> sortSet;//filling sortset for interleaving parts + for (std::set<long unsigned int>::iterator subIt = selectedTracks.begin(); subIt != selectedTracks.end(); subIt++) { + keyPart temp; + temp.trackID = *subIt; + temp.time = myMeta.tracks[*subIt].firstms;//timeplace of frame + temp.endTime = myMeta.tracks[*subIt].firstms + myMeta.tracks[*subIt].parts[0].getDuration(); + temp.size = myMeta.tracks[*subIt].parts[0].getSize();//bytesize of frame (alle parts all together) + temp.index = 0; + sortSet.insert(temp); + } + while (!sortSet.empty()){ + //setting the right STCO size in the STCO box + checkStcoBoxes[sortSet.begin()->trackID].setChunkOffset(totalByteOffset + byteOffset, sortSet.begin()->index); + totalByteOffset += sortSet.begin()->size; + //add keyPart to sortSet + keyPart temp; + temp.index = sortSet.begin()->index + 1; + temp.trackID = sortSet.begin()->trackID; + if(temp.index < myMeta.tracks[temp.trackID].parts.size() ){//only insert when there are parts left + temp.time = sortSet.begin()->endTime;//timeplace of frame + temp.endTime = sortSet.begin()->endTime + myMeta.tracks[temp.trackID].parts[temp.index].getDuration(); + temp.size = myMeta.tracks[temp.trackID].parts[temp.index].getSize();//bytesize of frame + sortSet.insert(temp); + } + //remove highest keyPart + sortSet.erase(sortSet.begin()); + } + + mdatSize = totalByteOffset+8; + + header << std::string(moovBox.asBox(),moovBox.boxedSize()); + + header << (char)((mdatSize>>24) & 0xFF) << (char)((mdatSize>>16) & 0xFF) << (char)((mdatSize>>8) & 0xFF) << (char)(mdatSize & 0xFF) << "mdat"; + //end of header + + size += header.str().size(); + return header.str(); + } + + /// Calculate a seekPoint, based on byteStart, metadata, tracks and headerSize. + /// The seekPoint will be set to the timestamp of the first packet to send. + void OutProgressiveMP4::findSeekPoint(long long byteStart, long long & seekPoint, unsigned int headerSize){ + seekPoint = 0; + //if we're starting in the header, seekPoint is always zero. + if (byteStart <= headerSize){return;} + //okay, we're past the header. Substract the headersize from the starting postion. + byteStart -= headerSize; + //initialize a list of sorted parts that this file contains + std::set <keyPart> sortSet; + for (std::set<long unsigned int>::iterator subIt = selectedTracks.begin(); subIt != selectedTracks.end(); subIt++) { + keyPart temp; + temp.trackID = *subIt; + temp.time = myMeta.tracks[*subIt].firstms;//timeplace of frame + temp.endTime = myMeta.tracks[*subIt].firstms + myMeta.tracks[*subIt].parts[0].getDuration(); + temp.size = myMeta.tracks[*subIt].parts[0].getSize();//bytesize of frame (alle parts all together) + temp.index = 0; + sortSet.insert(temp); + } + //forward through the file by headers, until we reach the point where we need to be + while (!sortSet.empty()){ + //substract the size of this fragment from byteStart + byteStart -= sortSet.begin()->size; + //if that put us past the point where we wanted to be, return right now + if (byteStart < 0){return;} + //otherwise, set seekPoint to where we are now + seekPoint = sortSet.begin()->time; + //then find the next part + keyPart temp; + temp.index = sortSet.begin()->index + 1; + temp.trackID = sortSet.begin()->trackID; + if(temp.index < myMeta.tracks[temp.trackID].parts.size() ){//only insert when there are parts left + temp.time = sortSet.begin()->endTime;//timeplace of frame + temp.endTime = sortSet.begin()->endTime + myMeta.tracks[temp.trackID].parts[temp.index].getDuration(); + temp.size = myMeta.tracks[temp.trackID].parts[temp.index].getSize();//bytesize of frame + sortSet.insert(temp); + } + //remove highest keyPart + sortSet.erase(sortSet.begin()); + } + //If we're here, we're in the last fragment. + //That's technically legal, of course. + } + + /// Parses a "Range: " header, setting byteStart, byteEnd and seekPoint using data from metadata and tracks to do + /// the calculations. + /// On error, byteEnd is set to zero. + void OutProgressiveMP4::parseRange(std::string header, long long & byteStart, long long & byteEnd, long long & seekPoint, unsigned int headerSize){ + if (header.size() < 6 || header.substr(0, 6) != "bytes="){ + byteEnd = 0; + DEBUG_MSG(DLVL_WARN, "Invalid range header: %s", header.c_str()); + return; + } + header.erase(0, 6); + if (header.size() && header[0] == '-'){ + //negative range = count from end + byteStart = 0; + for (unsigned int i = 1; i < header.size(); ++i){ + if (header[i] >= '0' && header[i] <= '9'){ + byteStart *= 10; + byteStart += header[i] - '0'; + continue; + } + break; + } + if (byteStart > byteEnd){ + //entire file if starting before byte zero + byteStart = 0; + DEBUG_MSG(DLVL_DEVEL, "Full negative range: %lli-%lli", byteStart, byteEnd); + findSeekPoint(byteStart, seekPoint, headerSize); + return; + }else{ + //start byteStart bytes before byteEnd + byteStart = byteEnd - byteStart; + DEBUG_MSG(DLVL_DEVEL, "Partial negative range: %lli-%lli", byteStart, byteEnd); + findSeekPoint(byteStart, seekPoint, headerSize); + return; + } + }else{ + long long size = byteEnd; + byteEnd = 0; + byteStart = 0; + unsigned int i = 0; + for ( ; i < header.size(); ++i){ + if (header[i] >= '0' && header[i] <= '9'){ + byteStart *= 10; + byteStart += header[i] - '0'; + continue; + } + break; + } + if (header[i] != '-'){ + DEBUG_MSG(DLVL_WARN, "Invalid range header: %s", header.c_str()); + byteEnd = 0; + return; + } + ++i; + if (i < header.size()){ + for ( ; i < header.size(); ++i){ + if (header[i] >= '0' && header[i] <= '9'){ + byteEnd *= 10; + byteEnd += header[i] - '0'; + continue; + } + break; + } + if (byteEnd > size-1){byteEnd = size;} + }else{ + byteEnd = size; + } + DEBUG_MSG(DLVL_DEVEL, "Range request: %lli-%lli (%s)", byteStart, byteEnd, header.c_str()); + findSeekPoint(byteStart, seekPoint, headerSize); + return; + } + } + + void OutProgressiveMP4::onRequest(){ + while (HTTP_R.Read(myConn)){ + DEBUG_MSG(DLVL_DEVEL, "Received request: %s", HTTP_R.getUrl().c_str()); + myConn.setHost(HTTP_R.GetHeader("X-Origin")); + streamName = HTTP_R.GetHeader("X-Stream"); + if (HTTP_R.GetVar("audio") != ""){ + DEBUG_MSG(DLVL_DEVEL, "GetVar Aud = %s", HTTP_R.GetVar("audio").c_str()); + selectedTracks.insert(JSON::Value(HTTP_R.GetVar("audio")).asInt()); + }else{ + DEBUG_MSG(DLVL_DEVEL, "No audio param given"); + } + if (HTTP_R.GetVar("video") != ""){ + DEBUG_MSG(DLVL_DEVEL, "GetVar Vid = %s", HTTP_R.GetVar("video").c_str()); + selectedTracks.insert(JSON::Value(HTTP_R.GetVar("video")).asInt()); + }else{ + DEBUG_MSG(DLVL_DEVEL, "No video param given"); + } + + parseData = true; + wantRequest = false; + } + } + + bool OutProgressiveMP4::onFinish(){ + HTTP_R.Clean(); + parseData = false; + wantRequest = true; + return true; + } + + void OutProgressiveMP4::onFail(){ + HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers + HTTP_S.SetBody("Stream not found. Sorry, we tried."); + HTTP_S.SendResponse("404", "Stream not found", myConn); + Output::onFail(); + } + + void OutProgressiveMP4::sendNext(){ + char * dataPointer = 0; + int len = 0; + currentPacket.getString("data", dataPointer, len); + + //keep track of where we are - fast-forward until where we are now + while (!sortSet.empty() && ((long long)sortSet.begin()->trackID != currentPacket.getTrackId() || (long long)sortSet.begin()->time != currentPacket.getTime())){ + keyPart temp; + temp.index = sortSet.begin()->index + 1; + temp.trackID = sortSet.begin()->trackID; + if(temp.index < myMeta.tracks[temp.trackID].parts.size() ){//only insert when there are parts left + temp.time = sortSet.begin()->endTime;//timeplace of frame + temp.endTime = sortSet.begin()->endTime + myMeta.tracks[temp.trackID].parts[temp.index].getDuration(); + temp.size = myMeta.tracks[temp.trackID].parts[temp.index].getSize();//bytesize of frame + sortSet.insert(temp); + } + currPos += sortSet.begin()->size; + //remove highest keyPart + sortSet.erase(sortSet.begin()); + } + if (currPos >= byteStart){ + sortSet.clear();//we don't need you anymore! + myConn.SendNow(dataPointer, std::min(leftOver, (long long)len)); + //HTTP_S.Chunkify(Strm.lastData().data(), Strm.lastData().size(), conn); + leftOver -= len; + }else{ + if (currPos + (long long)len > byteStart){ + myConn.SendNow(dataPointer+(byteStart-currPos), len-(byteStart-currPos)); + leftOver -= len-(byteStart-currPos); + currPos = byteStart; + sortSet.clear();//we don't need you anymore! + } + } + if (leftOver < 1){ + //stop playback, wait for new request + stop(); + wantRequest = true; + } + } + + void OutProgressiveMP4::sendHeader(){ + fileSize = 0; + std::string headerData = DTSCMeta2MP4Header(fileSize); + byteStart = 0; + byteEnd = fileSize - 1; + long long seekPoint = 0; + char rangeType = ' '; + if (HTTP_R.GetHeader("Range") != ""){ + parseRange(HTTP_R.GetHeader("Range"), byteStart, byteEnd, seekPoint, headerData.size()); + rangeType = HTTP_R.GetHeader("Range")[0]; + } + sortSet.clear(); + for (std::set<long unsigned int>::iterator subIt = selectedTracks.begin(); subIt != selectedTracks.end(); subIt++) { + keyPart temp; + temp.trackID = *subIt; + temp.time = myMeta.tracks[*subIt].firstms;//timeplace of frame + temp.endTime = myMeta.tracks[*subIt].firstms + myMeta.tracks[*subIt].parts[0].getDuration(); + temp.size = myMeta.tracks[*subIt].parts[0].getSize();//bytesize of frame (alle parts all together) + temp.index = 0; + sortSet.insert(temp); + } + HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers + HTTP_S.SetHeader("Content-Type", "video/MP4"); //Send the correct content-type for MP4 files + HTTP_S.SetHeader("Accept-Ranges", "bytes, parsec"); + if (rangeType != ' '){ + DEBUG_MSG(DLVL_DEVEL, "Ranged request"); + if (!byteEnd){ + if (rangeType == 'p'){ + HTTP_S.SetBody("Starsystem not in communications range"); + HTTP_S.SendResponse("416", "Starsystem not in communications range", myConn); + return; + }else{ + HTTP_S.SetBody("Requested Range Not Satisfiable"); + HTTP_S.SendResponse("416", "Requested Range Not Satisfiable", myConn); + return; + } + }else{ + std::stringstream rangeReply; + rangeReply << "bytes " << byteStart << "-" << byteEnd << "/" << fileSize; + HTTP_S.SetHeader("Content-Length", byteEnd - byteStart + 1); + //do not multiplex requests that are > 1MiB + if (byteEnd - byteStart + 1 > 1024*1024){ + HTTP_S.SetHeader("MistMultiplex", "No"); + } + HTTP_S.SetHeader("Content-Range", rangeReply.str()); + /// \todo Switch to chunked? + HTTP_S.SendResponse("206", "Partial content", myConn); + //HTTP_S.StartResponse("206", "Partial content", HTTP_R, conn); + } + }else{ + DEBUG_MSG(DLVL_DEVEL, "Non-Ranged request"); + HTTP_S.SetHeader("Content-Length", byteEnd - byteStart + 1); + //do not multiplex requests that aren't ranged + HTTP_S.SetHeader("MistMultiplex", "No"); + /// \todo Switch to chunked? + HTTP_S.SendResponse("200", "OK", myConn); + //HTTP_S.StartResponse(HTTP_R, conn); + } + leftOver = byteEnd - byteStart + 1;//add one byte, because range "0-0" = 1 byte of data + currPos = 0; + if (byteStart < (long long)headerData.size()){ + /// \todo Switch to chunked? + //HTTP_S.Chunkify(headerData.data()+byteStart, std::min((long long)headerData.size(), byteEnd) - byteStart, conn);//send MP4 header + myConn.SendNow(headerData.data()+byteStart, std::min((long long)headerData.size(), byteEnd) - byteStart);//send MP4 header + leftOver -= std::min((long long)headerData.size(), byteEnd) - byteStart; + } + currPos = headerData.size();//we're now guaranteed to be past the header point, no matter what + seek(seekPoint); + sentHeader = true; + } + +} diff --git a/src/output/output_progressive_mp4.h b/src/output/output_progressive_mp4.h new file mode 100644 index 00000000..f82f24a5 --- /dev/null +++ b/src/output/output_progressive_mp4.h @@ -0,0 +1,50 @@ +#include "output.h" +#include <mist/http_parser.h> + +namespace Mist { + struct keyPart{ + public: + bool operator < (const keyPart& rhs) const { + if (time < rhs.time){ + return true; + } + if (time == rhs.time){ + if (trackID < rhs.trackID){ + return true; + } + } + return false; + } + long unsigned int trackID; + long unsigned int size; + long long unsigned int time; + long long unsigned int endTime; + long unsigned int index; + }; + + class OutProgressiveMP4 : public Output { + public: + OutProgressiveMP4(Socket::Connection & conn); + ~OutProgressiveMP4(); + static void init(Util::Config * cfg); + void parseRange(std::string header, long long & byteStart, long long & byteEnd, long long & seekPoint, unsigned int headerSize); + std::string DTSCMeta2MP4Header(long long & size); + void findSeekPoint(long long byteStart, long long & seekPoint, unsigned int headerSize); + + void onRequest(); + void sendNext(); + bool onFinish(); + void sendHeader(); + void onFail(); + protected: + long long fileSize; + long long byteStart; + long long byteEnd; + long long leftOver; + long long currPos; + std::set <keyPart> sortSet;//filling sortset for interleaving parts + HTTP::Parser HTTP_R, HTTP_S; + }; +} + +typedef Mist::OutProgressiveMP4 mistOut; diff --git a/src/output/output_raw.cpp b/src/output/output_raw.cpp new file mode 100644 index 00000000..2bd71204 --- /dev/null +++ b/src/output/output_raw.cpp @@ -0,0 +1,69 @@ +#include "output_raw.h" + +namespace Mist { + OutRaw::OutRaw(Socket::Connection & conn) : Output(conn) { + streamName = config->getString("streamname"); + initialize(); + selectedTracks.clear(); + std::string tracks = config->getString("tracks"); + unsigned int currTrack = 0; + //loop over tracks, add any found track IDs to selectedTracks + if (tracks != ""){ + for (unsigned int i = 0; i < tracks.size(); ++i){ + if (tracks[i] >= '0' && tracks[i] <= '9'){ + currTrack = currTrack*10 + (tracks[i] - '0'); + }else{ + if (currTrack > 0){ + selectedTracks.insert(currTrack); + } + currTrack = 0; + } + } + if (currTrack > 0){ + selectedTracks.insert(currTrack); + } + } + parseData = true; + seek(config->getInteger("seek")); + } + + OutRaw::~OutRaw() {} + + void OutRaw::init(Util::Config * cfg){ + capa["name"] = "RAW"; + capa["desc"] = "Enables raw DTSC over TCP."; + capa["deps"] = ""; + capa["required"]["streamname"]["name"] = "Stream"; + capa["required"]["streamname"]["help"] = "What streamname to serve. For multiple streams, add this protocol multiple times using different ports."; + capa["required"]["streamname"]["type"] = "str"; + capa["required"]["streamname"]["option"] = "--stream"; + capa["optional"]["tracks"]["name"] = "Tracks"; + capa["optional"]["tracks"]["help"] = "The track IDs of the stream that this connector will transmit separated by spaces"; + capa["optional"]["tracks"]["type"] = "str"; + capa["optional"]["tracks"]["option"] = "--tracks"; + capa["optional"]["seek"]["name"] = "Seek point"; + capa["optional"]["seek"]["help"] = "The time in milliseconds to seek to, 0 by default."; + capa["optional"]["seek"]["type"] = "int"; + capa["optional"]["seek"]["option"] = "--seek"; + capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][1u].append("AAC"); + cfg->addOption("streamname", + JSON::fromString("{\"arg\":\"string\",\"short\":\"s\",\"long\":\"stream\",\"help\":\"The name of the stream that this connector will transmit.\"}")); + cfg->addOption("tracks", + JSON::fromString("{\"arg\":\"string\",\"value\":[\"\"],\"short\": \"t\",\"long\":\"tracks\",\"help\":\"The track IDs of the stream that this connector will transmit separated by spaces.\"}")); + cfg->addOption("seek", + JSON::fromString("{\"arg\":\"integer\",\"value\":[0],\"short\": \"S\",\"long\":\"seek\",\"help\":\"The time in milliseconds to seek to, 0 by default.\"}")); + cfg->addConnectorOptions(666, capa); + config = cfg; + } + + void OutRaw::sendNext(){ + myConn.SendNow(currentPacket.getData(), currentPacket.getDataLen()); + } + + void OutRaw::sendHeader(){ + myMeta.send(myConn); + sentHeader = true; + } + +} diff --git a/src/output/output_raw.h b/src/output/output_raw.h new file mode 100644 index 00000000..82105f0b --- /dev/null +++ b/src/output/output_raw.h @@ -0,0 +1,15 @@ +#include "output.h" + + +namespace Mist { + class OutRaw : public Output { + public: + OutRaw(Socket::Connection & conn); + ~OutRaw(); + static void init(Util::Config * cfg); + void sendNext(); + void sendHeader(); + }; +} + +typedef Mist::OutRaw mistOut; diff --git a/src/output/output_rtmp.cpp b/src/output/output_rtmp.cpp new file mode 100644 index 00000000..e6e3eaff --- /dev/null +++ b/src/output/output_rtmp.cpp @@ -0,0 +1,754 @@ +#include "output_rtmp.h" +#include <mist/http_parser.h> +#include <mist/defines.h> +#include <mist/stream.h> +#include <cstring> +#include <cstdlib> + +namespace Mist { + OutRTMP::OutRTMP(Socket::Connection & conn) : Output(conn) { + playTransaction = -1; + playMessageType = -1; + playStreamId = -1; + setBlocking(false); + while (!conn.Received().available(1537) && conn.connected()) { + conn.spool(); + Util::sleep(5); + } + RTMPStream::handshake_in = conn.Received().remove(1537); + RTMPStream::rec_cnt += 1537; + + if (RTMPStream::doHandshake()) { + conn.SendNow(RTMPStream::handshake_out); + while (!conn.Received().available(1536) && conn.connected()) { + conn.spool(); + Util::sleep(5); + } + conn.Received().remove(1536); + RTMPStream::rec_cnt += 1536; + DEBUG_MSG(DLVL_HIGH, "Handshake success!"); + } else { + DEBUG_MSG(DLVL_DEVEL, "Handshake fail!"); + } + counter = 0; + sending = false; + streamReset = false; + } + + OutRTMP::~OutRTMP() {} + + void OutRTMP::init(Util::Config * cfg) { + capa["name"] = "RTMP"; + capa["desc"] = "Enables the RTMP protocol which is used by Adobe Flash Player."; + capa["deps"] = ""; + capa["url_rel"] = "/play/$"; + capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][0u].append("H263"); + capa["codecs"][0u][0u].append("VP6"); + capa["codecs"][0u][1u].append("AAC"); + capa["codecs"][0u][1u].append("MP3"); + capa["methods"][0u]["handler"] = "rtmp"; + capa["methods"][0u]["type"] = "flash/10"; + capa["methods"][0u]["priority"] = 6ll; + cfg->addConnectorOptions(1935, capa); + config = cfg; + } + + void OutRTMP::sendNext() { + //sent a tag + FLV::Tag tag; + if (tag.DTSCLoader(currentPacket, myMeta.tracks[currentPacket.getTrackId()])) { + if (tag.len) { + myConn.SendNow(RTMPStream::SendMedia(tag)); +#if DEBUG >= 8 + fprintf(stderr, "Sent tag to %i: [%u] %s\n", myConn.getSocket(), tag.tagTime(), tag.tagType().c_str()); +#endif + } + } + } + + void OutRTMP::sendHeader() { + FLV::Tag tag; + tag.DTSCMetaInit(myMeta, selectedTracks); + if (tag.len) { + myConn.SendNow(RTMPStream::SendMedia(tag)); + } + + for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++) { + if (myMeta.tracks[*it].type == "video") { + tag.DTSCVideoInit(myMeta.tracks[*it]); + if (tag.len) { + myConn.SendNow(RTMPStream::SendMedia(tag)); + } + } + if (myMeta.tracks[*it].type == "audio") { + tag.DTSCAudioInit(myMeta.tracks[*it]); + if (tag.len) { + myConn.SendNow(RTMPStream::SendMedia(tag)); + } + } + } + sentHeader = true; + } + + void OutRTMP::onRequest() { + parseChunk(myConn.Received()); + } + + ///\brief Sends a RTMP command either in AMF or AMF3 mode. + ///\param amfReply The data to be sent over RTMP. + ///\param messageType The type of message. + ///\param streamId The ID of the AMF stream. + void OutRTMP::sendCommand(AMF::Object & amfReply, int messageType, int streamId) { +#if DEBUG >= 8 + std::cerr << amfReply.Print() << std::endl; +#endif + if (messageType == 17) { + myConn.SendNow(RTMPStream::SendChunk(3, messageType, streamId, (char)0 + amfReply.Pack())); + } else { + myConn.SendNow(RTMPStream::SendChunk(3, messageType, streamId, amfReply.Pack())); + } + } //sendCommand + + ///\brief Parses a single AMF command message, and sends a direct response through sendCommand(). + ///\param amfData The received request. + ///\param messageType The type of message. + ///\param streamId The ID of the AMF stream. + void OutRTMP::parseAMFCommand(AMF::Object & amfData, int messageType, int streamId) { +#if DEBUG >= 5 + fprintf(stderr, "Received command: %s\n", amfData.Print().c_str()); +#endif +#if DEBUG >= 8 + fprintf(stderr, "AMF0 command: %s\n", amfData.getContentP(0)->StrValue().c_str()); +#endif + if (amfData.getContentP(0)->StrValue() == "connect") { + double objencoding = 0; + if (amfData.getContentP(2)->getContentP("objectEncoding")) { + objencoding = amfData.getContentP(2)->getContentP("objectEncoding")->NumValue(); + } +#if DEBUG >= 6 + int tmpint; + if (amfData.getContentP(2)->getContentP("videoCodecs")) { + tmpint = (int)amfData.getContentP(2)->getContentP("videoCodecs")->NumValue(); + if (tmpint & 0x04) { + fprintf(stderr, "Sorensen video support detected\n"); + } + if (tmpint & 0x80) { + fprintf(stderr, "H264 video support detected\n"); + } + } + if (amfData.getContentP(2)->getContentP("audioCodecs")) { + tmpint = (int)amfData.getContentP(2)->getContentP("audioCodecs")->NumValue(); + if (tmpint & 0x04) { + fprintf(stderr, "MP3 audio support detected\n"); + } + if (tmpint & 0x400) { + fprintf(stderr, "AAC audio support detected\n"); + } + } +#endif + app_name = amfData.getContentP(2)->getContentP("tcUrl")->StrValue(); + app_name = app_name.substr(app_name.find('/', 7) + 1); + RTMPStream::chunk_snd_max = 4096; + myConn.Send(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1) + myConn.Send(RTMPStream::SendCTL(5, RTMPStream::snd_window_size)); //send window acknowledgement size (msg 5) + myConn.Send(RTMPStream::SendCTL(6, RTMPStream::rec_window_size)); //send rec window acknowledgement size (msg 6) + myConn.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 + //send a _result reply + AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "_result")); //result success + amfReply.addContent(amfData.getContent(1)); //same transaction ID + amfReply.addContent(AMF::Object("")); //server properties + amfReply.getContentP(2)->addContent(AMF::Object("fmsVer", "FMS/3,5,5,2004")); + amfReply.getContentP(2)->addContent(AMF::Object("capabilities", (double)31)); + amfReply.getContentP(2)->addContent(AMF::Object("mode", (double)1)); + amfReply.addContent(AMF::Object("")); //info + amfReply.getContentP(3)->addContent(AMF::Object("level", "status")); + amfReply.getContentP(3)->addContent(AMF::Object("code", "NetConnection.Connect.Success")); + amfReply.getContentP(3)->addContent(AMF::Object("description", "Connection succeeded.")); + amfReply.getContentP(3)->addContent(AMF::Object("clientid", 1337)); + amfReply.getContentP(3)->addContent(AMF::Object("objectEncoding", objencoding)); + //amfReply.getContentP(3)->addContent(AMF::Object("data", AMF::AMF0_ECMA_ARRAY)); + //amfReply.getContentP(3)->getContentP(4)->addContent(AMF::Object("version", "3,5,4,1004")); + sendCommand(amfReply, messageType, streamId); + //send onBWDone packet - no clue what it is, but real server sends it... + //amfReply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER); + //amfReply.addContent(AMF::Object("", "onBWDone"));//result + //amfReply.addContent(amfData.getContent(1));//same transaction ID + //amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null + //sendCommand(amfReply, messageType, streamId); + return; + } //connect + if (amfData.getContentP(0)->StrValue() == "createStream") { + //send a _result reply + AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "_result")); //result success + amfReply.addContent(amfData.getContent(1)); //same transaction ID + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfReply.addContent(AMF::Object("", (double)1)); //stream ID - we use 1 + sendCommand(amfReply, messageType, streamId); + myConn.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 + return; + } //createStream + if ((amfData.getContentP(0)->StrValue() == "closeStream") || (amfData.getContentP(0)->StrValue() == "deleteStream")) { + stop(); + return; + } + if ((amfData.getContentP(0)->StrValue() == "FCUnpublish") || (amfData.getContentP(0)->StrValue() == "releaseStream")) { + // ignored + return; + } + if ((amfData.getContentP(0)->StrValue() == "FCPublish")) { + //send a FCPublic reply + AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "onFCPublish")); //status reply + amfReply.addContent(AMF::Object("", 0, AMF::AMF0_NUMBER)); //same transaction ID + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfReply.addContent(AMF::Object("")); //info + amfReply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Publish.Start")); + amfReply.getContentP(3)->addContent(AMF::Object("description", "Please followup with publish command...")); + sendCommand(amfReply, messageType, streamId); + return; + } //FCPublish + if (amfData.getContentP(0)->StrValue() == "releaseStream") { + //send a _result reply + AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "_result")); //result success + amfReply.addContent(amfData.getContent(1)); //same transaction ID + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfReply.addContent(AMF::Object("", AMF::AMF0_UNDEFINED)); //stream ID? + sendCommand(amfReply, messageType, streamId); + return; + }//releaseStream + if ((amfData.getContentP(0)->StrValue() == "getStreamLength") || (amfData.getContentP(0)->StrValue() == "getMovLen")) { + //send a _result reply + AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "_result")); //result success + amfReply.addContent(amfData.getContent(1)); //same transaction ID + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfReply.addContent(AMF::Object("", (double)0)); //zero length + sendCommand(amfReply, messageType, streamId); + return; + } //getStreamLength + if ((amfData.getContentP(0)->StrValue() == "publish")) { + if (amfData.getContentP(3)) { + streamName = amfData.getContentP(3)->StrValue(); + //pull the server configuration + JSON::Value servConf = JSON::fromFile(Util::getTmpFolder() + "streamlist"); + if (servConf.isMember("streams") && servConf["streams"].isMember(streamName)){ + JSON::Value & streamConfig = servConf["streams"][streamName]; + if (!streamConfig.isMember("source") || streamConfig["source"].asStringRef().substr(0, 7) != "push://"){ + DEBUG_MSG(DLVL_FAIL, "Push rejected - stream not a push-able stream. (%s != push://*)", streamConfig["source"].asStringRef().c_str()); + myConn.close(); + return; + } + std::string source = streamConfig["source"].asStringRef().substr(7); + std::string IP = source.substr(0, source.find('@')); + if (IP != ""){ + if (!myConn.isAddress(IP)){ + DEBUG_MSG(DLVL_FAIL, "Push rejected - source host not whitelisted"); + myConn.close(); + return; + } + } + }else{ + DEBUG_MSG(DLVL_FAIL, "Push rejected - stream not configured."); + myConn.close(); + return; + } + initialize(); + } + //send a _result reply + AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "_result")); //result success + amfReply.addContent(amfData.getContent(1)); //same transaction ID + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfReply.addContent(AMF::Object("", 1, AMF::AMF0_BOOL)); //publish success? + sendCommand(amfReply, messageType, streamId); + myConn.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 + //send a status reply + amfReply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "onStatus")); //status reply + amfReply.addContent(AMF::Object("", 0, AMF::AMF0_NUMBER)); //same transaction ID + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfReply.addContent(AMF::Object("")); //info + amfReply.getContentP(3)->addContent(AMF::Object("level", "status")); + amfReply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Publish.Start")); + amfReply.getContentP(3)->addContent(AMF::Object("description", "Stream is now published!")); + amfReply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + sendCommand(amfReply, messageType, streamId); + return; + } //getStreamLength + if (amfData.getContentP(0)->StrValue() == "checkBandwidth") { + //send a _result reply + AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "_result")); //result success + amfReply.addContent(amfData.getContent(1)); //same transaction ID + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + sendCommand(amfReply, messageType, streamId); + return; + } //checkBandwidth + if ((amfData.getContentP(0)->StrValue() == "play") || (amfData.getContentP(0)->StrValue() == "play2")) { + //set reply number and stream name, actual reply is sent up in the ss.spool() handler + int playTransaction = amfData.getContentP(1)->NumValue(); + int playMessageType = messageType; + int playStreamId = streamId; + streamName = amfData.getContentP(3)->StrValue(); + initialize(); + + //send a status reply + AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(AMF::Object("", (double)playTransaction)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info + amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); + amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Reset")); + amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing and resetting...")); + amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV")); + amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + sendCommand(amfreply, playMessageType, playStreamId); + //send streamisrecorded if stream, well, is recorded. + if (myMeta.vod) { //isMember("length") && Strm.metadata["length"].asInt() > 0){ + myConn.Send(RTMPStream::SendUSR(4, 1)); //send UCM StreamIsRecorded (4), stream 1 + } + //send streambegin + myConn.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 + //and more reply + amfreply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER); + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(AMF::Object("", (double)playTransaction)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info + amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); + amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Start")); + amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing!")); + amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV")); + amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + sendCommand(amfreply, playMessageType, playStreamId); + RTMPStream::chunk_snd_max = 102400; //100KiB + myConn.Send(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1) + //send dunno? + myConn.Send(RTMPStream::SendUSR(32, 1)); //send UCM no clue?, stream 1 + + parseData = true; + return; + } //play + if ((amfData.getContentP(0)->StrValue() == "seek")) { + //set reply number and stream name, actual reply is sent up in the ss.spool() handler + int playTransaction = amfData.getContentP(1)->NumValue(); + int playMessageType = messageType; + int playStreamId = streamId; + + AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "onStatus")); //status reply + amfReply.addContent(amfData.getContent(1)); //same transaction ID + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfReply.addContent(AMF::Object("")); //info + amfReply.getContentP(3)->addContent(AMF::Object("level", "status")); + amfReply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Seek.Notify")); + amfReply.getContentP(3)->addContent(AMF::Object("description", "Seeking to the specified time")); + amfReply.getContentP(3)->addContent(AMF::Object("details", "DDV")); + amfReply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + sendCommand(amfReply, playMessageType, playStreamId); + seek((long long int)amfData.getContentP(3)->NumValue()); + + //send a status reply + AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(AMF::Object("", (double)playTransaction)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info + amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); + amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Reset")); + amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing and resetting...")); + amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV")); + amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + sendCommand(amfreply, playMessageType, playStreamId); + //send streamisrecorded if stream, well, is recorded. + if (myMeta.vod) { //isMember("length") && Strm.metadata["length"].asInt() > 0){ + myConn.Send(RTMPStream::SendUSR(4, 1)); //send UCM StreamIsRecorded (4), stream 1 + } + //send streambegin + myConn.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 + //and more reply + amfreply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER); + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(AMF::Object("", (double)playTransaction)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info + amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); + amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Start")); + amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing!")); + amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV")); + amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + sendCommand(amfreply, playMessageType, playStreamId); + RTMPStream::chunk_snd_max = 102400; //100KiB + myConn.Send(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1) + //send dunno? + myConn.Send(RTMPStream::SendUSR(32, 1)); //send UCM no clue?, stream 1 + + return; + } //seek + if ((amfData.getContentP(0)->StrValue() == "pauseRaw") || (amfData.getContentP(0)->StrValue() == "pause")) { + if (amfData.getContentP(3)->NumValue()) { + parseData = false; + //send a status reply + AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "onStatus")); //status reply + amfReply.addContent(amfData.getContent(1)); //same transaction ID + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfReply.addContent(AMF::Object("")); //info + amfReply.getContentP(3)->addContent(AMF::Object("level", "status")); + amfReply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Pause.Notify")); + amfReply.getContentP(3)->addContent(AMF::Object("description", "Pausing playback")); + amfReply.getContentP(3)->addContent(AMF::Object("details", "DDV")); + amfReply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + sendCommand(amfReply, playMessageType, playStreamId); + } else { + parseData = true; + //send a status reply + AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER); + amfReply.addContent(AMF::Object("", "onStatus")); //status reply + amfReply.addContent(amfData.getContent(1)); //same transaction ID + amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfReply.addContent(AMF::Object("")); //info + amfReply.getContentP(3)->addContent(AMF::Object("level", "status")); + amfReply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Unpause.Notify")); + amfReply.getContentP(3)->addContent(AMF::Object("description", "Resuming playback")); + amfReply.getContentP(3)->addContent(AMF::Object("details", "DDV")); + amfReply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); + sendCommand(amfReply, playMessageType, playStreamId); + } + return; + } //seek + +#if DEBUG >= 2 + fprintf(stderr, "AMF0 command not processed!\n%s\n", amfData.Print().c_str()); +#endif + } //parseAMFCommand + + void OutRTMP::bufferPacket(JSON::Value & pack){ + if (!trackMap.count(pack["trackid"].asInt())){ + //declined track; + return; + } + pack["trackid"] = trackMap[pack["trackid"].asInt()]; + long long unsigned int tNum = pack["trackid"].asInt(); + if (!bookKeeping.count(tNum)){ + return; + } + int pageNum = bookKeeping[tNum].pageNum; + std::string tmp = pack.toNetPacked(); + if (bookKeeping[tNum].curOffset > 8388608 && pack.isMember("keyframe") && pack["keyframe"]){ + Util::sleep(500); + //open new page + char nextPage[100]; + sprintf(nextPage, "%s%llu_%d", streamName.c_str(), tNum, bookKeeping[tNum].pageNum + bookKeeping[tNum].keyNum); + curPages[tNum].init(nextPage, 0, false); + bookKeeping[tNum].pageNum += bookKeeping[tNum].keyNum; + bookKeeping[tNum].keyNum = 0; + bookKeeping[tNum].curOffset = 0; + } + if (bookKeeping[tNum].curOffset + tmp.size() < curPages[tNum].len){ + bookKeeping[tNum].keyNum += (pack.isMember("keyframe") && pack["keyframe"]); + memcpy(curPages[tNum].mapped + bookKeeping[tNum].curOffset, tmp.data(), tmp.size()); + bookKeeping[tNum].curOffset += tmp.size(); + }else{ + bookKeeping[tNum].curOffset += tmp.size(); + DEBUG_MSG(DLVL_WARN, "Can't buffer frame on page %d, track %llu, time %lld, keyNum %d, offset %llu", pageNum, tNum, pack["time"].asInt(), bookKeeping[tNum].pageNum + bookKeeping[tNum].keyNum, bookKeeping[tNum].curOffset); + ///\todo Open next page plx + } + playerConn.keepAlive(); + } + + + void OutRTMP::negotiatePushTracks() { + char * tmp = playerConn.getData(); + if (!tmp){ + DEBUG_MSG(DLVL_FAIL, "No userpage allocated"); + return; + } + memset(tmp, 0, 30); + unsigned int i = 0; + for (std::map<int, DTSC::Track>::iterator it = meta_out.tracks.begin(); it != meta_out.tracks.end() && i < 5; it++){ + DEBUG_MSG(DLVL_DEVEL, "Negotiating tracknum for id %d", it->first); + (tmp + 6 * i)[0] = 0x80; + (tmp + 6 * i)[1] = 0x00; + (tmp + 6 * i)[2] = 0x00; + (tmp + 6 * i)[3] = 0x00; + (tmp + 6 * i)[4] = (it->first >> 8) & 0xFF; + (tmp + 6 * i)[5] = (it->first) & 0xFF; + i++; + } + playerConn.keepAlive(); + bool gotAllNumbers = false; + while (!gotAllNumbers){ + Util::sleep(100); + gotAllNumbers = true; + i = 0; + for (std::map<int, DTSC::Track>::iterator it = meta_out.tracks.begin(); it != meta_out.tracks.end() && i < 5; it++){ + unsigned long tNum = (((long)(tmp + (6 * i))[0]) << 24) | (((long)(tmp + (6 * i))[1]) << 16) | (((long)(tmp + (6 * i))[2]) << 8) | (long)(tmp + (6 * i))[3]; + unsigned short oldNum = (((long)(tmp + (6 * i))[4]) << 8) | (long)(tmp + (6 * i))[5]; + if( tNum & 0x80000000){ + gotAllNumbers = false; + break; + }else{ + DEBUG_MSG(DLVL_DEVEL, "Mapped %d -> %lu", oldNum, tNum); + trackMap[oldNum] = tNum; + } + i++; + } + } + for (std::map<int, int>::iterator it = trackMap.begin(); it != trackMap.end(); it++){ + char tmp[100]; + sprintf( tmp, "liveStream_%s%d", streamName.c_str(), it->second); + metaPages[it->second].init(std::string(tmp), 0, false); + DTSC::Meta tmpMeta = meta_out; + tmpMeta.tracks.clear(); + tmpMeta.tracks[it->second] = meta_out.tracks[it->first]; + tmpMeta.tracks[it->second].trackID = it->second; + JSON::Value tmpVal = tmpMeta.toJSON(); + std::string tmpStr = tmpVal.toNetPacked(); + memcpy(metaPages[it->second].mapped, tmpStr.data(), tmpStr.size()); + DEBUG_MSG(DLVL_DEVEL, "Written meta for track %d", it->second); + } + gotAllNumbers = false; + while (!gotAllNumbers){ + Util::sleep(100); + gotAllNumbers = true; + i = 0; + unsigned int j = 0; + //update Metadata; + JSON::Value jsonMeta; + JSON::fromDTMI((const unsigned char*)streamIndex.mapped + 8, streamIndex.len - 8, j, jsonMeta); + myMeta = DTSC::Meta(jsonMeta); + tmp = playerConn.getData(); + for (std::map<int, DTSC::Track>::iterator it = meta_out.tracks.begin(); it != meta_out.tracks.end() && i < 5; it++){ + unsigned long tNum = (((long)(tmp + (6 * i))[0]) << 24) | (((long)(tmp + (6 * i))[1]) << 16) | (((long)(tmp + (6 * i))[2]) << 8) | (long)(tmp + (6 * i))[3]; + if( tNum == 0xFFFFFFFF){ + DEBUG_MSG(DLVL_DEVEL, "Skipping a declined track"); + i++; + continue; + } + if(!myMeta.tracks.count(tNum)){ + gotAllNumbers = false; + break; + } + i++; + } + } + i = 0; + tmp = playerConn.getData(); + for (std::map<int, DTSC::Track>::iterator it = meta_out.tracks.begin(); it != meta_out.tracks.end() && i < 5; it++){ + unsigned long tNum = ((long)(tmp[6*i]) << 24) | ((long)(tmp[6 * i + 1]) << 16) | ((long)(tmp[6 * i + 2]) << 8) | tmp[6 * i + 3]; + if( tNum == 0xFFFFFFFF){ + tNum = ((long)(tmp[6 * i + 4]) << 8) | (long)tmp[6 * i + 5]; + DEBUG_MSG(DLVL_WARN, "Buffer declined track %i", trackMap[tNum]); + trackMap.erase(tNum); + tmp[6*i] = 0; + tmp[6*i+1] = 0; + tmp[6*i+2] = 0; + tmp[6*i+3] = 0; + tmp[6*i+4] = 0; + tmp[6*i+5] = 0; + }else{ + char firstPage[100]; + sprintf(firstPage, "%s%lu_%d", streamName.c_str(), tNum, 0); + curPages[tNum].init(firstPage, 0, false); + bookKeeping[tNum] = DTSCPageData(); + DEBUG_MSG(DLVL_WARN, "Buffer accepted track %lu", tNum); + } + i++; + } + } + + ///\brief Gets and parses one RTMP chunk at a time. + ///\param inputBuffer A buffer filled with chunk data. + void OutRTMP::parseChunk(Socket::Buffer & inputBuffer) { + //for DTSC conversion + static std::stringstream prebuffer; // Temporary buffer before sending real data + //for chunk parsing + static RTMPStream::Chunk next; + static FLV::Tag F; + static AMF::Object amfdata("empty", AMF::AMF0_DDV_CONTAINER); + static AMF::Object amfelem("empty", AMF::AMF0_DDV_CONTAINER); + static AMF::Object3 amf3data("empty", AMF::AMF3_DDV_CONTAINER); + static AMF::Object3 amf3elem("empty", AMF::AMF3_DDV_CONTAINER); + + while (next.Parse(inputBuffer)) { + + //send ACK if we received a whole window + if ((RTMPStream::rec_cnt - RTMPStream::rec_window_at > RTMPStream::rec_window_size)) { + RTMPStream::rec_window_at = RTMPStream::rec_cnt; + myConn.Send(RTMPStream::SendCTL(3, RTMPStream::rec_cnt)); //send ack (msg 3) + } + + switch (next.msg_type_id) { + case 0: //does not exist +#if DEBUG >= 2 + fprintf(stderr, "UNKN: Received a zero-type message. Possible data corruption? Aborting!\n"); +#endif + while (inputBuffer.size()) { + inputBuffer.get().clear(); + } + stop(); + myConn.close(); + break; //happens when connection breaks unexpectedly + case 1: //set chunk size + RTMPStream::chunk_rec_max = ntohl(*(int *)next.data.c_str()); +#if DEBUG >= 5 + fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max); +#endif + break; + case 2: //abort message - we ignore this one +#if DEBUG >= 5 + fprintf(stderr, "CTRL: Abort message\n"); +#endif + //4 bytes of stream id to drop + break; + case 3: //ack +#if DEBUG >= 8 + fprintf(stderr, "CTRL: Acknowledgement\n"); +#endif + RTMPStream::snd_window_at = ntohl(*(int *)next.data.c_str()); + RTMPStream::snd_window_at = RTMPStream::snd_cnt; + break; + case 4: { + //2 bytes event type, rest = event data + //types: + //0 = stream begin, 4 bytes ID + //1 = stream EOF, 4 bytes ID + //2 = stream dry, 4 bytes ID + //3 = setbufferlen, 4 bytes ID, 4 bytes length + //4 = streamisrecorded, 4 bytes ID + //6 = pingrequest, 4 bytes data + //7 = pingresponse, 4 bytes data + //we don't need to process this +#if DEBUG >= 5 + short int ucmtype = ntohs(*(short int *)next.data.c_str()); + switch (ucmtype) { + case 0: + fprintf(stderr, "CTRL: UCM StreamBegin %i\n", ntohl(*((int *)(next.data.c_str() + 2)))); + break; + case 1: + fprintf(stderr, "CTRL: UCM StreamEOF %i\n", ntohl(*((int *)(next.data.c_str() + 2)))); + break; + case 2: + fprintf(stderr, "CTRL: UCM StreamDry %i\n", ntohl(*((int *)(next.data.c_str() + 2)))); + break; + case 3: + fprintf(stderr, "CTRL: UCM SetBufferLength %i %i\n", ntohl(*((int *)(next.data.c_str() + 2))), ntohl(*((int *)(next.data.c_str() + 6)))); + break; + case 4: + fprintf(stderr, "CTRL: UCM StreamIsRecorded %i\n", ntohl(*((int *)(next.data.c_str() + 2)))); + break; + case 6: + fprintf(stderr, "CTRL: UCM PingRequest %i\n", ntohl(*((int *)(next.data.c_str() + 2)))); + break; + case 7: + fprintf(stderr, "CTRL: UCM PingResponse %i\n", ntohl(*((int *)(next.data.c_str() + 2)))); + break; + default: + fprintf(stderr, "CTRL: UCM Unknown (%hi)\n", ucmtype); + break; + } +#endif + } + break; + case 5: //window size of other end +#if DEBUG >= 5 + fprintf(stderr, "CTRL: Window size\n"); +#endif + RTMPStream::rec_window_size = ntohl(*(int *)next.data.c_str()); + RTMPStream::rec_window_at = RTMPStream::rec_cnt; + myConn.Send(RTMPStream::SendCTL(3, RTMPStream::rec_cnt)); //send ack (msg 3) + break; + case 6: +#if DEBUG >= 5 + fprintf(stderr, "CTRL: Set peer bandwidth\n"); +#endif + //4 bytes window size, 1 byte limit type (ignored) + RTMPStream::snd_window_size = ntohl(*(int *)next.data.c_str()); + myConn.Send(RTMPStream::SendCTL(5, RTMPStream::snd_window_size)); //send window acknowledgement size (msg 5) + break; + case 8: //audio data + case 9: //video data + case 18: {//meta data + if (!isInitialized) { + DEBUG_MSG(DLVL_MEDIUM, "Received useless media data\n"); + myConn.close(); + break; + } + if (streamReset) { + //reset push data to empty, in case stream properties change + meta_out.reset(); + preBuf.clear(); + sending = false; + counter = 0; + streamReset = false; + } + F.ChunkLoader(next); + JSON::Value pack_out = F.toJSON(meta_out); + if ( !pack_out.isNull()){ + if ( !sending){ + counter++; + if (counter > 8){ + sending = true; + negotiatePushTracks(); + for (std::deque<JSON::Value>::iterator it = preBuf.begin(); it != preBuf.end(); it++){ + bufferPacket((*it)); + } + preBuf.clear(); //clear buffer + bufferPacket(pack_out); + }else{ + preBuf.push_back(pack_out); + } + }else{ + bufferPacket(pack_out); + } + } + break; + } + case 15: + DEBUG_MSG(DLVL_MEDIUM, "Received AMF3 data message"); + break; + case 16: + DEBUG_MSG(DLVL_MEDIUM, "Received AMF3 shared object"); + break; + case 17: { + DEBUG_MSG(DLVL_MEDIUM, "Received AMF3 command message"); + if (next.data[0] != 0) { + next.data = next.data.substr(1); + amf3data = AMF::parse3(next.data); +#if DEBUG >= 5 + amf3data.Print(); +#endif + } else { + DEBUG_MSG(DLVL_MEDIUM, "Received AMF3-0 command message"); + next.data = next.data.substr(1); + amfdata = AMF::parse(next.data); + parseAMFCommand(amfdata, 17, next.msg_stream_id); + } //parsing AMF0-style + } + break; + case 19: + DEBUG_MSG(DLVL_MEDIUM, "Received AMF0 shared object"); + break; + case 20: { //AMF0 command message + amfdata = AMF::parse(next.data); + parseAMFCommand(amfdata, 20, next.msg_stream_id); + } + break; + case 22: + DEBUG_MSG(DLVL_MEDIUM, "Received aggregate message"); + break; + default: + DEBUG_MSG(DLVL_FAIL, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data."); + break; + } + } + } +} + diff --git a/src/output/output_rtmp.h b/src/output/output_rtmp.h new file mode 100644 index 00000000..1c0493fa --- /dev/null +++ b/src/output/output_rtmp.h @@ -0,0 +1,48 @@ +#include "output.h" +#include <mist/flv_tag.h> +#include <mist/amf.h> +#include <mist/rtmpchunks.h> + + +namespace Mist { + struct DTSCPageData { + DTSCPageData() : pageNum(0), keyNum(0), partNum(0), dataSize(0), curOffset(0), firstTime(0){} + int pageNum;///<The current page number + int keyNum;///<The number of keyframes in this page. + int partNum;///<The number of parts in this page. + unsigned long long int dataSize;///<The full size this page should be. + unsigned long long int curOffset;///<The current write offset in the page. + unsigned long long int firstTime;///<The first timestamp of the page. + }; + + class OutRTMP : public Output { + public: + OutRTMP(Socket::Connection & conn); + ~OutRTMP(); + static void init(Util::Config * cfg); + + void onRequest(); + void sendNext(); + void sendHeader(); + void bufferPacket(JSON::Value & pack); + protected: + DTSC::Meta meta_out; + void negotiatePushTracks(); + std::string app_name; + bool sending; + int counter; + bool streamReset; + int playTransaction;///<The transaction number of the reply. + int playStreamId;///<The stream id of the reply. + int playMessageType;///<The message type of the reply. + void parseChunk(Socket::Buffer & inputBuffer); + void parseAMFCommand(AMF::Object & amfData, int messageType, int streamId); + void sendCommand(AMF::Object & amfReply, int messageType, int streamId); + std::deque<JSON::Value> preBuf; + std::map<int,int> trackMap; + std::map<int,IPC::sharedPage> metaPages; + std::map<int,DTSCPageData> bookKeeping; + }; +} + +typedef Mist::OutRTMP mistOut; diff --git a/src/output/output_srt.cpp b/src/output/output_srt.cpp new file mode 100644 index 00000000..c6dd23f4 --- /dev/null +++ b/src/output/output_srt.cpp @@ -0,0 +1,82 @@ +#include "output_srt.h" +#include <mist/http_parser.h> +#include <mist/defines.h> +#include <iomanip> + +namespace Mist { + OutProgressiveSRT::OutProgressiveSRT(Socket::Connection & conn) : Output(conn) { + realTime = 0; + } + + void OutProgressiveSRT::onFail(){ + HTTP::Parser HTTP_S; + HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers + HTTP_S.SetBody("Stream not found. Sorry, we tried."); + HTTP_S.SendResponse("404", "Stream not found", myConn); + Output::onFail(); + } + + OutProgressiveSRT::~OutProgressiveSRT() {} + + void OutProgressiveSRT::init(Util::Config * cfg){ + capa["desc"] = "Enables HTTP protocol subtitle streaming."; + capa["deps"] = "HTTP"; + capa["url_rel"] = "/$.srt"; + capa["url_match"] = "/$.srt"; + capa["url_handler"] = "http"; + capa["url_type"] = "subtitle"; + capa["socket"] = "http_srt"; + + cfg->addBasicConnectorOptions(capa); + config = cfg; + } + + void OutProgressiveSRT::sendNext(){ + char * dataPointer = 0; + int len = 0; + currentPacket.getString("data", dataPointer, len); + std::stringstream tmp; + if(!webVTT) { + tmp << lastNum++ << std::endl; + } + long long unsigned int time = currentPacket.getTime(); + char tmpBuf[50]; + int tmpLen = sprintf(tmpBuf, "%0.2llu:%0.2llu:%0.2llu,%0.3llu", (time / 3600000), ((time % 3600000) / 60000), (((time % 3600000) % 60000) / 1000), time % 1000); + tmp.write(tmpBuf, tmpLen); + tmp << " --> "; + time += currentPacket.getInt("duration"); + tmpLen = sprintf(tmpBuf, "%0.2llu:%0.2llu:%0.2llu,%0.3llu", (time / 3600000), ((time % 3600000) / 60000), (((time % 3600000) % 60000) / 1000), time % 1000); + tmp.write(tmpBuf, tmpLen); + tmp << std::endl; + myConn.SendNow(tmp.str()); + myConn.SendNow(dataPointer, len); + myConn.SendNow("\n"); + } + + void OutProgressiveSRT::sendHeader(){ + HTTP::Parser HTTP_S; + FLV::Tag tag; + HTTP_S.SetHeader("Content-Type", "text/plain"); + HTTP_S.protocol = "HTTP/1.0"; + myConn.SendNow(HTTP_S.BuildResponse("200", "OK")); + sentHeader = true; + } + + void OutProgressiveSRT::onRequest(){ + HTTP::Parser HTTP_R; + while (HTTP_R.Read(myConn)){ + DEBUG_MSG(DLVL_DEVEL, "Received request %s", HTTP_R.getUrl().c_str()); + lastNum = 0; + webVTT = (HTTP_R.url.find(".webvtt") != std::string::npos); + if (HTTP_R.GetVar("track") != ""){ + selectedTracks.insert(JSON::Value(HTTP_R.GetVar("track")).asInt()); + } + myConn.setHost(HTTP_R.GetHeader("X-Origin")); + streamName = HTTP_R.GetHeader("X-Stream"); + parseData = true; + wantRequest = false; + HTTP_R.Clean(); + } + } + +} diff --git a/src/output/output_srt.h b/src/output/output_srt.h new file mode 100644 index 00000000..06fd8dc9 --- /dev/null +++ b/src/output/output_srt.h @@ -0,0 +1,20 @@ +#include "output.h" + + +namespace Mist { + class OutProgressiveSRT : public Output { + public: + OutProgressiveSRT(Socket::Connection & conn); + ~OutProgressiveSRT(); + static void init(Util::Config * cfg); + void onRequest(); + void sendNext(); + void onFail(); + void sendHeader(); + protected: + bool webVTT; + int lastNum; + }; +} + +typedef Mist::OutProgressiveSRT mistOut; diff --git a/src/output/output_ts.cpp b/src/output/output_ts.cpp new file mode 100644 index 00000000..8b97afd8 --- /dev/null +++ b/src/output/output_ts.cpp @@ -0,0 +1,134 @@ +#include "output_ts.h" +#include <mist/http_parser.h> +#include <mist/defines.h> + +namespace Mist { + OutTS::OutTS(Socket::Connection & conn) : Output(conn){ + haveAvcc = false; + AudioCounter = 0; + VideoCounter = 0; + std::string tracks = config->getString("tracks"); + unsigned int currTrack = 0; + //loop over tracks, add any found track IDs to selectedTracks + if (tracks != ""){ + for (unsigned int i = 0; i < tracks.size(); ++i){ + if (tracks[i] >= '0' && tracks[i] <= '9'){ + currTrack = currTrack*10 + (tracks[i] - '0'); + }else{ + if (currTrack > 0){ + selectedTracks.insert(currTrack); + } + currTrack = 0; + } + } + if (currTrack > 0){ + selectedTracks.insert(currTrack); + } + } + streamName = config->getString("streamname"); + parseData = true; + wantRequest = false; + initialize(); + } + + OutTS::~OutTS() {} + + void OutTS::init(Util::Config * cfg){ + capa["name"] = "TS"; + capa["desc"] = "Enables the raw MPEG Transport Stream protocol over TCP."; + capa["deps"] = ""; + capa["required"]["streamname"]["name"] = "Stream"; + capa["required"]["streamname"]["help"] = "What streamname to serve. For multiple streams, add this protocol multiple times using different ports."; + capa["required"]["streamname"]["type"] = "str"; + capa["required"]["streamname"]["option"] = "--stream"; + capa["optional"]["tracks"]["name"] = "Tracks"; + capa["optional"]["tracks"]["help"] = "The track IDs of the stream that this connector will transmit separated by spaces"; + capa["optional"]["tracks"]["type"] = "str"; + capa["optional"]["tracks"]["option"] = "--tracks"; + capa["codecs"][0u][0u].append("H264"); + capa["codecs"][0u][1u].append("AAC"); + cfg->addOption("streamname", + JSON::fromString("{\"arg\":\"string\",\"short\":\"s\",\"long\":\"stream\",\"help\":\"The name of the stream that this connector will transmit.\"}")); + cfg->addOption("tracks", + JSON::fromString("{\"arg\":\"string\",\"value\":[\"\"],\"short\": \"t\",\"long\":\"tracks\",\"help\":\"The track IDs of the stream that this connector will transmit separated by spaces.\"}")); + cfg->addConnectorOptions(8888, capa); + config = cfg; + } + + void OutTS::sendNext(){ + Socket::Buffer ToPack; + char * ContCounter = 0; + bool IsKeyFrame = false; + + char * dataPointer = 0; + int dataLen = 0; + currentPacket.getString("data", dataPointer, dataLen); + + //detect packet type, and put converted data into ToPack. + if (myMeta.tracks[currentPacket.getTrackId()].type == "video"){ + ToPack.append(TS::Packet::getPESVideoLeadIn(0ul, currentPacket.getTime() * 90)); + + IsKeyFrame = currentPacket.getInt("keyframe"); + if (IsKeyFrame){ + if (!haveAvcc){ + avccbox.setPayload(myMeta.tracks[currentPacket.getTrackId()].init); + haveAvcc = true; + } + ToPack.append(avccbox.asAnnexB()); + } + unsigned int i = 0; + while (i + 4 < (unsigned int)dataLen){ + unsigned int ThisNaluSize = (dataPointer[i] << 24) + (dataPointer[i+1] << 16) + (dataPointer[i+2] << 8) + dataPointer[i+3]; + if (ThisNaluSize + i + 4 > (unsigned int)dataLen){ + DEBUG_MSG(DLVL_WARN, "Too big NALU detected (%u > %d) - skipping!", ThisNaluSize + i + 4, dataLen); + break; + } + ToPack.append("\000\000\000\001", 4); + i += 4; + ToPack.append(dataPointer + i, ThisNaluSize); + i += ThisNaluSize; + } + ContCounter = &VideoCounter; + }else if (myMeta.tracks[currentPacket.getTrackId()].type == "audio"){ + ToPack.append(TS::Packet::getPESAudioLeadIn(7+dataLen, currentPacket.getTime() * 90)); + ToPack.append(TS::GetAudioHeader(dataLen, myMeta.tracks[currentPacket.getTrackId()].init)); + ToPack.append(dataPointer, dataLen); + ContCounter = &AudioCounter; + } + + bool first = true; + //send TS packets + while (ToPack.size()){ + PackData.Clear(); + /// \todo Update according to sendHeader()'s generated data. + //0x100 - 1 + currentPacket.getTrackId() + if (myMeta.tracks[currentPacket.getTrackId()].type == "video"){ + PackData.PID(0x100); + }else{ + PackData.PID(0x101); + } + PackData.ContinuityCounter((*ContCounter)++); + if (first){ + PackData.UnitStart(1); + if (IsKeyFrame){ + PackData.RandomAccess(1); + PackData.PCR(currentPacket.getTime() * 27000); + } + first = false; + } + unsigned int toSend = PackData.AddStuffing(ToPack.bytes(184)); + std::string gonnaSend = ToPack.remove(toSend); + PackData.FillFree(gonnaSend); + myConn.SendNow(PackData.ToString(), 188); + } + } + + void OutTS::sendHeader(){ + /// \todo Update this to actually generate these from the selected tracks. + /// \todo ts_packet.h contains all neccesary info for this + myConn.SendNow(TS::PAT, 188); + myConn.SendNow(TS::PMT, 188); + sentHeader = true; + } + +} diff --git a/src/output/output_ts.h b/src/output/output_ts.h new file mode 100644 index 00000000..78efee19 --- /dev/null +++ b/src/output/output_ts.h @@ -0,0 +1,23 @@ +#include "output.h" +#include <mist/mp4_generic.h> +#include <mist/ts_packet.h> + +namespace Mist { + class OutTS : public Output { + public: + OutTS(Socket::Connection & conn); + ~OutTS(); + static void init(Util::Config * cfg); + void sendNext(); + void sendHeader(); + protected: + TS::Packet PackData; + unsigned int PacketNumber; + bool haveAvcc; + char VideoCounter; + char AudioCounter; + MP4::AVCC avccbox; + }; +} + +typedef Mist::OutTS mistOut;