FLV streaming module for Apache2
/** | |
* Copyright 2006 Paul Querna | |
* (modified by Artur Bodera 2009, see http://tperspective.blogspot.com/2009/02/apache-flv-streaming-done-right.html) | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
#include "httpd.h" | |
#include "http_core.h" | |
#include "http_config.h" | |
#include "http_protocol.h" | |
#include "http_log.h" | |
#include "apr_buckets.h" | |
#define FLVX_HANDLER "flv-stream" | |
#define FLVX_HEADER "FLV\x1\x1\0\0\0\x9\0\0\0\x9" | |
// #define FLVX_HEADER "FLV\x1\x5\0\0\0\x9\0\0\0\0\x12\0" | |
#define FLVX_HEADER_LEN (sizeof(FLVX_HEADER)-1) | |
static apr_off_t get_start(request_rec *r) | |
{ | |
apr_off_t start = 0; | |
char *p = NULL; | |
if (!r->args) { | |
return start; | |
} | |
p = strstr(r->args, "start="); | |
if (p) { | |
p = p + 6; | |
apr_strtoff(&start, p, NULL, 10); | |
} | |
return start; | |
} | |
static int drive_flvx(request_rec *r) | |
{ | |
apr_finfo_t fi; | |
apr_bucket_brigade *bb; | |
apr_off_t offset = 0; | |
apr_off_t length = 0; | |
apr_file_t *fp = NULL; | |
apr_status_t rv = APR_SUCCESS; | |
rv = apr_stat(&fi, r->filename, APR_FINFO_SIZE, r->pool); | |
if (rv) { | |
/* Let the core handle it. */ | |
return DECLINED; | |
} | |
/* Open the file */ | |
rv = apr_file_open(&fp, r->filename, APR_READ, | |
APR_OS_DEFAULT, r->pool); | |
if (rv) { | |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, | |
"file permissions deny server access: %s", r->filename); | |
return HTTP_FORBIDDEN; | |
} | |
offset = get_start(r); | |
if (offset != 0 && offset < fi.size) { | |
length = fi.size - offset; | |
} | |
else { | |
length = fi.size; | |
} | |
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); | |
if (offset != 0) { | |
rv = apr_brigade_write(bb, NULL, NULL, FLVX_HEADER, FLVX_HEADER_LEN); | |
if (rv) { | |
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, | |
"unable to write flv header in brigade"); | |
return HTTP_INTERNAL_SERVER_ERROR; | |
} | |
} | |
apr_brigade_insert_file(bb, fp, offset, length, r->pool); | |
ap_set_content_type(r, "video/x-flv"); | |
ap_set_content_length(r, length + FLVX_HEADER_LEN); | |
ap_update_mtime(r, r->finfo.mtime); | |
ap_set_last_modified(r); | |
return ap_pass_brigade(r->output_filters, bb); | |
} | |
static int flvx_handler(request_rec *r) | |
{ | |
if ((!r->handler) || | |
(strcmp(r->handler, FLVX_HANDLER))) { | |
return DECLINED; | |
} | |
r->allowed |= (AP_METHOD_BIT << M_GET); | |
if (r->method_number != M_GET) { | |
return HTTP_METHOD_NOT_ALLOWED; | |
} | |
return drive_flvx(r); | |
} | |
static const command_rec flvx_cmds[] = | |
{ | |
{NULL} | |
}; | |
static void register_hooks(apr_pool_t *p) | |
{ | |
ap_hook_handler(flvx_handler, NULL, NULL, APR_HOOK_MIDDLE); | |
} | |
module AP_MODULE_DECLARE_DATA flvx_module = | |
{ | |
STANDARD20_MODULE_STUFF, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
flvx_cmds, | |
register_hooks | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment