summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kneschke <jan@kneschke.de>2006-03-07 12:26:10 +0000
committerJan Kneschke <jan@kneschke.de>2006-03-07 12:26:10 +0000
commit7a6d43dea648cc558e04736c9717bcc91db40a95 (patch)
tree15ea34353a451d4b0da10addbb46aee8136caa57
parent52d2264cdae1691b592e07a9d147f17b9e4a7f1e (diff)
downloadlighttpd-git-7a6d43dea648cc558e04736c9717bcc91db40a95.tar.gz
added mod_flv_streaming.c for streaming flash movies efficiently
git-svn-id: svn+ssh://svn.lighttpd.net/lighttpd/branches/lighttpd-merge-1.4.x@1030 152afb58-edef-0310-8abb-c4023f1b3aa9
-rw-r--r--src/Makefile.am6
-rw-r--r--src/SConscript1
-rw-r--r--src/mod_flv_streaming.c278
3 files changed, 284 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 17d14358..ffec47e9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -70,12 +70,16 @@ src += $(common_src)
common_libadd =
endif
+lib_LTLIBRARIES += mod_flv_streaming.la
+mod_flv_streaming_la_SOURCES = mod_flv_streaming.c
+mod_flv_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
+mod_flv_streaming_la_LIBADD = $(common_libadd)
+
lib_LTLIBRARIES += mod_evasive.la
mod_evasive_la_SOURCES = mod_evasive.c
mod_evasive_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
mod_evasive_la_LIBADD = $(common_libadd)
-
lib_LTLIBRARIES += mod_webdav.la
mod_webdav_la_SOURCES = mod_webdav.c
mod_webdav_la_CFLAGS = $(AM_CFLAGS) $(XML_CFLAGS) $(SQLITE_CFLAGS)
diff --git a/src/SConscript b/src/SConscript
index e2bef43b..ef412830 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -71,6 +71,7 @@ modules = {
'mod_uploadprogress' : { 'src' : [ 'mod_uploadprogress.c' ] },
'mod_evasive' : { 'src' : [ 'mod_evasive.c' ] },
'mod_ssi' : { 'src' : [ 'mod_ssi_exprparser.c', 'mod_ssi_expr.c', 'mod_ssi.c' ], 'lib' : [ env['LIBPCRE'] ] },
+ 'mod_flv_streaming' : { 'src' : [ 'mod_flv_streaming.c' ] },
}
staticenv = env.Copy(CPPFLAGS=[ env['CPPFLAGS'], '-DLIGHTTPD_STATIC', '-DOPENSSL_NO_KRB5'])
diff --git a/src/mod_flv_streaming.c b/src/mod_flv_streaming.c
new file mode 100644
index 00000000..c557bd47
--- /dev/null
+++ b/src/mod_flv_streaming.c
@@ -0,0 +1,278 @@
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base.h"
+#include "log.h"
+#include "buffer.h"
+#include "response.h"
+#include "http_chunk.h"
+#include "stat_cache.h"
+
+#include "plugin.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* plugin config for all request/connections */
+
+typedef struct {
+ array *extensions;
+} plugin_config;
+
+typedef struct {
+ PLUGIN_DATA;
+
+ buffer *query_str;
+ array *get_params;
+
+ plugin_config **config_storage;
+
+ plugin_config conf;
+} plugin_data;
+
+/* init the plugin data */
+INIT_FUNC(mod_flv_streaming_init) {
+ plugin_data *p;
+
+ p = calloc(1, sizeof(*p));
+
+ p->query_str = buffer_init();
+ p->get_params = array_init();
+
+ return p;
+}
+
+/* detroy the plugin data */
+FREE_FUNC(mod_flv_streaming_free) {
+ plugin_data *p = p_d;
+
+ UNUSED(srv);
+
+ if (!p) return HANDLER_GO_ON;
+
+ if (p->config_storage) {
+ size_t i;
+
+ for (i = 0; i < srv->config_context->used; i++) {
+ plugin_config *s = p->config_storage[i];
+
+ if (!s) continue;
+
+ array_free(s->extensions);
+
+ free(s);
+ }
+ free(p->config_storage);
+ }
+
+ buffer_free(p->query_str);
+ array_free(p->get_params);
+
+ free(p);
+
+ return HANDLER_GO_ON;
+}
+
+/* handle plugin config and check values */
+
+SETDEFAULTS_FUNC(mod_flv_streaming_set_defaults) {
+ plugin_data *p = p_d;
+ size_t i = 0;
+
+ config_values_t cv[] = {
+ { "flv-streaming.extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
+ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
+ };
+
+ if (!p) return HANDLER_ERROR;
+
+ p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
+
+ for (i = 0; i < srv->config_context->used; i++) {
+ plugin_config *s;
+
+ s = calloc(1, sizeof(plugin_config));
+ s->extensions = array_init();
+
+ cv[0].destination = s->extensions;
+
+ p->config_storage[i] = s;
+
+ if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+ return HANDLER_ERROR;
+ }
+ }
+
+ return HANDLER_GO_ON;
+}
+
+#define PATCH(x) \
+ p->conf.x = s->x;
+static int mod_flv_streaming_patch_connection(server *srv, connection *con, plugin_data *p) {
+ size_t i, j;
+ plugin_config *s = p->config_storage[0];
+
+ PATCH(extensions);
+
+ /* skip the first, the global context */
+ for (i = 1; i < srv->config_context->used; i++) {
+ data_config *dc = (data_config *)srv->config_context->data[i];
+ s = p->config_storage[i];
+
+ /* condition didn't match */
+ if (!config_check_cond(srv, con, dc)) continue;
+
+ /* merge config */
+ for (j = 0; j < dc->value->used; j++) {
+ data_unset *du = dc->value->data[j];
+
+ if (buffer_is_equal_string(du->key, CONST_STR_LEN("flv-streaming.extensions"))) {
+ PATCH(extensions);
+ }
+ }
+ }
+
+ return 0;
+}
+#undef PATCH
+
+static int split_get_params(server *srv, connection *con, array *get_params, buffer *qrystr) {
+ size_t is_key = 1;
+ size_t i;
+ char *key = NULL, *val = NULL;
+
+ key = qrystr->ptr;
+
+ /* we need the \0 */
+ for (i = 0; i < qrystr->used; i++) {
+ switch(qrystr->ptr[i]) {
+ case '=':
+ if (is_key) {
+ val = qrystr->ptr + i + 1;
+
+ qrystr->ptr[i] = '\0';
+
+ is_key = 0;
+ }
+
+ break;
+ case '&':
+ case '\0': /* fin symbol */
+ if (!is_key) {
+ data_string *ds;
+ /* we need at least a = since the last & */
+
+ /* terminate the value */
+ qrystr->ptr[i] = '\0';
+
+ if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
+ ds = data_string_init();
+ }
+ buffer_copy_string_len(ds->key, key, strlen(key));
+ buffer_copy_string_len(ds->value, val, strlen(val));
+
+ array_insert_unique(get_params, (data_unset *)ds);
+ }
+
+ key = qrystr->ptr + i + 1;
+ val = NULL;
+ is_key = 1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+URIHANDLER_FUNC(mod_flv_streaming_path_handler) {
+ plugin_data *p = p_d;
+ int s_len;
+ size_t k;
+
+ UNUSED(srv);
+
+ if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
+
+ mod_flv_streaming_patch_connection(srv, con, p);
+
+ s_len = con->physical.path->used - 1;
+
+ for (k = 0; k < p->conf.extensions->used; k++) {
+ data_string *ds = (data_string *)p->conf.extensions->data[k];
+ int ct_len = ds->value->used - 1;
+
+ if (ct_len > s_len) continue;
+ if (ds->value->used == 0) continue;
+
+ if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
+ data_string *get_param;
+ stat_cache_entry *sce = NULL;
+ buffer *b;
+ int start;
+ char *err = NULL;
+ /* if there is a start=[0-9]+ in the header use it as start,
+ * otherwise send the full file */
+
+ array_reset(p->get_params);
+ buffer_copy_string_buffer(p->query_str, con->uri.query);
+ split_get_params(srv, con, p->get_params, p->query_str);
+
+ if (NULL == (get_param = (data_string *)array_get_element(p->get_params, "start"))) {
+ return HANDLER_GO_ON;
+ }
+
+ /* too short */
+ if (get_param->value->used < 2) return HANDLER_GO_ON;
+
+ /* check if it is a number */
+ start = strtol(get_param->value->ptr, &err, 10);
+ if (*err != '\0') {
+ return HANDLER_GO_ON;
+ }
+
+ if (start <= 0) return HANDLER_GO_ON;
+
+ /* check if start is > filesize */
+ if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
+ return HANDLER_GO_ON;
+ }
+
+ if (start > sce->st.st_size) {
+ return HANDLER_GO_ON;
+ }
+
+ /* we are safe now, let's build a flv header */
+ b = chunkqueue_get_append_buffer(con->write_queue);
+ BUFFER_COPY_STRING_CONST(b, "FLV\x1\x1\0\0\0\x9\0\0\0\x9");
+
+ http_chunk_append_file(srv, con, con->physical.path, start, sce->st.st_size - start);
+
+ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv"));
+
+ con->file_finished = 1;
+
+ return HANDLER_FINISHED;
+ }
+ }
+
+ /* not found */
+ return HANDLER_GO_ON;
+}
+
+/* this function is called at dlopen() time and inits the callbacks */
+
+int mod_flv_streaming_plugin_init(plugin *p) {
+ p->version = LIGHTTPD_VERSION_ID;
+ p->name = buffer_init_string("flv_streaming");
+
+ p->init = mod_flv_streaming_init;
+ p->handle_physical = mod_flv_streaming_path_handler;
+ p->set_defaults = mod_flv_streaming_set_defaults;
+ p->cleanup = mod_flv_streaming_free;
+
+ p->data = NULL;
+
+ return 0;
+}