[modules] Add mod_flv
parent
4ef800e871
commit
c72ee99ad7
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* mod_flv - flash pseudo streaming
|
||||
*
|
||||
* Description:
|
||||
* mod_flv lets you stream .flv files in a way that flash players can seek into positions in the timeline.
|
||||
*
|
||||
* Setups:
|
||||
* none
|
||||
*
|
||||
* Options:
|
||||
* none
|
||||
*
|
||||
* Actions:
|
||||
* flv;
|
||||
* - enables .flv pseudo streaming
|
||||
*
|
||||
* Example config:
|
||||
* if phys.path =$ ".flv" {
|
||||
* flv;
|
||||
* }
|
||||
*
|
||||
* Tip:
|
||||
* Use caching and bandwidth throttling to save traffic.
|
||||
* To prevent the player from buffering at the beginning, use a small burst threshold.
|
||||
*
|
||||
* if phys.path =$ ".flv" {
|
||||
* expire "access 1 month";
|
||||
* io.throttle 500kbyte => 150kbyte;
|
||||
* flv;
|
||||
* }
|
||||
*
|
||||
* This config will make browsers cache videos for 1 month and limit bandwidth to 150 kilobyte/s after 500 kilobytes.
|
||||
*
|
||||
* Todo:
|
||||
* - flv audio container support?
|
||||
*
|
||||
* Author:
|
||||
* Copyright (c) 2010 Thomas Porzelt
|
||||
* License:
|
||||
* MIT, see COPYING file in the lighttpd 2 tree
|
||||
*/
|
||||
|
||||
#include <lighttpd/base.h>
|
||||
|
||||
LI_API gboolean mod_flv_init(liModules *mods, liModule *mod);
|
||||
LI_API gboolean mod_flv_free(liModules *mods, liModule *mod);
|
||||
|
||||
|
||||
static liHandlerResult flv(liVRequest *vr, gpointer param, gpointer *context) {
|
||||
gchar *start;
|
||||
guint len;
|
||||
goffset pos;
|
||||
liHandlerResult res;
|
||||
gboolean cachable;
|
||||
struct stat st;
|
||||
int err;
|
||||
int fd = -1;
|
||||
|
||||
UNUSED(context);
|
||||
UNUSED(param);
|
||||
|
||||
if (li_vrequest_is_handled(vr))
|
||||
return LI_HANDLER_GO_ON;
|
||||
|
||||
res = li_stat_cache_get(vr, vr->physical.path, &st, &err, &fd);
|
||||
|
||||
if (res == LI_HANDLER_WAIT_FOR_EVENT)
|
||||
return res;
|
||||
|
||||
if (res == LI_HANDLER_ERROR) {
|
||||
/* open or fstat failed */
|
||||
|
||||
if (fd != -1) {
|
||||
while(-1 == close(fd)) {
|
||||
if (errno != EINTR)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!li_vrequest_handle_direct(vr)) {
|
||||
return LI_HANDLER_ERROR;
|
||||
}
|
||||
|
||||
switch (err) {
|
||||
case ENOENT:
|
||||
case ENOTDIR:
|
||||
vr->response.http_status = 404;
|
||||
return LI_HANDLER_GO_ON;
|
||||
case EACCES:
|
||||
vr->response.http_status = 403;
|
||||
return LI_HANDLER_GO_ON;
|
||||
default:
|
||||
VR_ERROR(vr, "stat() or open() for '%s' failed: %s", vr->physical.path->str, g_strerror(err));
|
||||
return LI_HANDLER_ERROR;
|
||||
}
|
||||
} else if (S_ISDIR(st.st_mode)) {
|
||||
if (fd != -1) {
|
||||
while(-1 == close(fd)) {
|
||||
if (errno != EINTR)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return LI_HANDLER_GO_ON;
|
||||
} else if (!S_ISREG(st.st_mode)) {
|
||||
if (fd != -1) {
|
||||
while(-1 == close(fd)) {
|
||||
if (errno != EINTR)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!li_vrequest_handle_direct(vr)) {
|
||||
return LI_HANDLER_ERROR;
|
||||
}
|
||||
|
||||
vr->response.http_status = 403;
|
||||
} else {
|
||||
liChunkFile *cf;
|
||||
|
||||
#ifdef FD_CLOEXEC
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
|
||||
if (!li_vrequest_handle_direct(vr)) {
|
||||
while(-1 == close(fd)) {
|
||||
if (errno != EINTR)
|
||||
break;
|
||||
}
|
||||
return LI_HANDLER_ERROR;
|
||||
}
|
||||
|
||||
if (li_querystring_find(vr->request.uri.query, CONST_STR_LEN("start"), &start, &len)) {
|
||||
guint i;
|
||||
pos = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (start[i] >= '0' && start[i] <= '9') {
|
||||
pos *= 10;
|
||||
pos += start[i] - '0';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
li_etag_set_header(vr, &st, &cachable);
|
||||
if (cachable) {
|
||||
vr->response.http_status = 304;
|
||||
while(-1 == close(fd)) {
|
||||
if (errno != EINTR)
|
||||
break;
|
||||
}
|
||||
return LI_HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
|
||||
vr->response.http_status = 200;
|
||||
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv"));
|
||||
|
||||
if (pos < 0 || pos > st.st_size)
|
||||
pos = 0;
|
||||
|
||||
if (pos != 0)
|
||||
li_chunkqueue_append_mem(vr->out, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9"));
|
||||
|
||||
cf = li_chunkfile_new(NULL, fd, FALSE);
|
||||
li_chunkqueue_append_chunkfile(vr->out, cf, pos, st.st_size - pos);
|
||||
li_chunkfile_release(cf);
|
||||
}
|
||||
|
||||
return LI_HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
static liAction* flv_create(liServer *srv, liPlugin* p, liValue *val, gpointer userdata) {
|
||||
|
||||
UNUSED(userdata);
|
||||
|
||||
if (val) {
|
||||
ERROR(srv, "%s", "flv does not take any parameters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return li_action_new_function(flv, NULL, NULL, p);
|
||||
}
|
||||
|
||||
static const liPluginAction actions[] = {
|
||||
{ "flv", flv_create, NULL },
|
||||
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static void plugin_flv_init(liServer *srv, liPlugin *p, gpointer userdata) {
|
||||
UNUSED(srv); UNUSED(userdata);
|
||||
|
||||
p->actions = actions;
|
||||
|
||||
}
|
||||
|
||||
|
||||
gboolean mod_flv_init(liModules *mods, liModule *mod) {
|
||||
UNUSED(mod);
|
||||
|
||||
MODULE_VERSION_CHECK(mods);
|
||||
|
||||
mod->config = li_plugin_register(mods->main, "mod_flv", plugin_flv_init, NULL);
|
||||
|
||||
return mod->config != NULL;
|
||||
}
|
||||
|
||||
gboolean mod_flv_free(liModules *mods, liModule *mod) {
|
||||
if (mod->config)
|
||||
li_plugin_free(mods->main, mod->config);
|
||||
|
||||
return TRUE;
|
||||
}
|
Loading…
Reference in New Issue