int main(int argc, char *argv[]) { LALFrStream *stream; REAL8TimeSeries *series; LIGOTimeGPS start; XLALSetErrorHandler(XLALAbortErrorHandler); parseargs(argc, argv); /* get the data */ stream = XLALFrStreamCacheOpen(cache); XLALGPSSetREAL8(&start, t0 - pad); series = XLALFrStreamInputREAL8TimeSeries(stream, channel, &start, dt + 2.0 * pad, 0); XLALFrStreamClose(stream); /* manipulate the data */ if (srate > 0) XLALResampleREAL8TimeSeries(series, 1.0 / srate); if (minfreq > 0) XLALHighPassREAL8TimeSeries(series, minfreq, 0.9, 8); if (maxfreq > 0) XLALLowPassREAL8TimeSeries(series, maxfreq, 0.9, 8); if (pad > 0) series = XLALResizeREAL8TimeSeries(series, pad / series->deltaT, dt / series->deltaT); if (df > 0) { /* we are computing a spectrum */ REAL8FrequencySeries *spectrum; REAL8FFTPlan *plan; REAL8Window *window; size_t seglen = 1.0 / (df * series->deltaT); /* make sure that the time series length is commensurate with seglen */ if (((2 * series->data->length) % seglen) != 0) { size_t newlen = ((2 * series->data->length) / seglen) * seglen; series = XLALResizeREAL8TimeSeries(series, 0, newlen); } spectrum = XLALCreateREAL8FrequencySeries(series->name, &series->epoch, 0.0, df, &lalDimensionlessUnit, seglen/2 + 1); plan = XLALCreateForwardREAL8FFTPlan(seglen, 0); window = XLALCreateHannREAL8Window(seglen); XLALREAL8AverageSpectrumWelch(spectrum, series, seglen, seglen/2, window, plan); if (minfreq > 0 || maxfreq > 0) { size_t first = minfreq / spectrum->deltaF; size_t last = maxfreq > 0 ? maxfreq / spectrum->deltaF : spectrum->data->length; spectrum = XLALResizeREAL8FrequencySeries(spectrum, first, last - first); } output_fs(outfile, spectrum); XLALDestroyREAL8Window(window); XLALDestroyREAL8FFTPlan(plan); XLALDestroyREAL8FrequencySeries(spectrum); } else { /* we are outputting a time series */ output_ts(outfile, series); } XLALDestroyREAL8TimeSeries(series); return 0; }
static ngx_int_t ngx_streaming_handler(ngx_http_request_t *r) { size_t root; ngx_int_t rc; ngx_uint_t level; ngx_str_t path; ngx_open_file_info_t of; ngx_http_core_loc_conf_t *clcf; if(!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) return NGX_HTTP_NOT_ALLOWED; if(r->uri.data[r->uri.len - 1] == '/') return NGX_DECLINED; rc = ngx_http_discard_request_body(r); if(rc != NGX_OK) return rc; mp4_split_options_t *options = mp4_split_options_init(r); if(r->args.len && !mp4_split_options_set(r, options, (const char *)r->args.data, r->args.len)) { mp4_split_options_exit(r, options); return NGX_DECLINED; } if(!options) return NGX_DECLINED; if(!ngx_http_map_uri_to_path(r, &path, &root, 1)) { mp4_split_options_exit(r, options); return NGX_HTTP_INTERNAL_SERVER_ERROR; } u_int m3u8 = 0; struct bucket_t *bucket = bucket_init(r); int result = 0; { if(ngx_strstr(path.data, "m3u8")) m3u8 = 1; char *ext = strrchr((const char *)path.data, '.'); strcpy(ext, ".mp4"); path.len = ((u_char *)ext - path.data) + 4; // ngx_open_and_stat_file in ngx_open_cached_file expects the name to be zero-terminated. path.data[path.len] = '\0'; } ngx_log_t *nlog = r->connection->log; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, nlog, 0, "http mp4 filename: \"%s\"", path.data); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_memzero(&of, sizeof(ngx_open_file_info_t)); of.read_ahead = clcf->read_ahead; of.directio = NGX_MAX_OFF_T_VALUE; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; if(ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { mp4_split_options_exit(r, options); switch(of.err) { case 0: return NGX_HTTP_INTERNAL_SERVER_ERROR; case NGX_ENOENT: case NGX_ENOTDIR: case NGX_ENAMETOOLONG: level = NGX_LOG_ERR; rc = NGX_HTTP_NOT_FOUND; break; case NGX_EACCES: level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; break; default: level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; break; } if(rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) { ngx_log_error(level, nlog, of.err, ngx_open_file_n " \"%s\" failed", path.data); } return rc; } if(!of.is_file) { mp4_split_options_exit(r, options); if(ngx_close_file(of.fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, ngx_close_file_n " \"%s\" failed", path.data); } return NGX_DECLINED; } ngx_file_t *file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); if(file == NULL) { mp4_split_options_exit(r, options); return NGX_HTTP_INTERNAL_SERVER_ERROR; } file->fd = of.fd; file->name = path; file->log = nlog; mp4_context_t *mp4_context = mp4_open(r, file, of.size, MP4_OPEN_MOOV); if(!mp4_context) { mp4_split_options_exit(r, options); ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, "mp4_open failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } mp4_context->root = root; if(m3u8) { if((result = mp4_create_m3u8(mp4_context, bucket))) { char action[50]; sprintf(action, "ios_playlist&segments=%d", result); view_count(mp4_context, (char *)path.data, options ? options->hash : NULL, action); } r->allow_ranges = 0; // dirty hack r->headers_out.content_type.data = (u_char *)"application/vnd.apple.mpegurl"; r->headers_out.content_type.len = 29; r->headers_out.content_type_len = r->headers_out.content_type.len; } else { result = output_ts(mp4_context, bucket, options); if(!options || !result) { mp4_close(mp4_context); ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, "output_ts failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } char action[50] = "ios_view"; view_count(mp4_context, (char *)path.data, options->hash, action); r->allow_ranges = 1; } mp4_close(mp4_context); mp4_split_options_exit(r, options); result = result == 0 ? 415 : 200; r->root_tested = !r->error_page; if(result && bucket) { nlog->action = "sending mp4 to client"; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, nlog, 0, "content_length: %d", bucket->content_length); r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = bucket->content_length; r->headers_out.last_modified_time = of.mtime; if(ngx_http_set_content_type(r) != NGX_OK) return NGX_HTTP_INTERNAL_SERVER_ERROR; if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } //ngx_table_elt_t *h = ngx_list_push(&r->headers_out.headers); //if(h == NULL) return NGX_HTTP_INTERNAL_SERVER_ERROR; //h->hash = 1; //h->key.len = sizeof(X_MOD_HLS_KEY) - 1; //h->key.data = (u_char *)X_MOD_HLS_KEY; //h->value.len = sizeof(X_MOD_HLS_VERSION) - 1; //h->value.data = (u_char *)X_MOD_HLS_VERSION; rc = ngx_http_send_header(r); if(rc == NGX_ERROR || rc > NGX_OK || r->header_only) { ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, ngx_close_file_n "ngx_http_send_header failed"); return rc; } return ngx_http_output_filter(r, bucket->first); } else return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE; }
static ngx_int_t ngx_estreaming_handler(ngx_http_request_t * r) { size_t root; ngx_int_t rc; ngx_uint_t level; ngx_str_t path; ngx_open_file_info_t of; ngx_http_core_loc_conf_t *clcf; video_buffer *destination; if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) return NGX_HTTP_NOT_ALLOWED; if (r->uri.data[r->uri.len - 1] == '/') return NGX_DECLINED; rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) return rc; mp4_split_options_t * options = mp4_split_options_init(r); if (!ngx_http_map_uri_to_path(r, &path, &root, 1)) { mp4_split_options_exit(r, options); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (r->args.len && !mp4_split_options_set(r, options, (const char *) r->args.data, r->args.len)) { mp4_split_options_exit(r, options); return NGX_DECLINED; } if (!options) return NGX_DECLINED; ngx_log_t * nlog = r->connection->log; struct bucket_t * bucket = bucket_init(r); int result = 0; u_int m3u8 = 0, len_ = 0; int64_t duration = 0; if (ngx_memcmp(r->exten.data, "mp4", r->exten.len) == 0) { return ngx_http_mp4_handler(r); } else if (ngx_memcmp(r->exten.data, "m3u8", r->exten.len) == 0) { m3u8 = 1; } else if (ngx_memcmp(r->exten.data, "len", r->exten.len) == 0) {// this is for length request len_ = 1; } else if (ngx_memcmp(r->exten.data, "ts", r->exten.len) == 0) { // don't do anything } else { return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE; } // change file name to mp4 // in order to lookup file in filesystem char *ext = strrchr((const char *) path.data, '.'); strcpy(ext, ".mp4"); path.len = ((u_char *) ext - path.data) + 4; path.data[path.len] = '\0'; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_memzero(&of, sizeof (ngx_open_file_info_t)); of.read_ahead = clcf->read_ahead; of.directio = NGX_MAX_OFF_T_VALUE; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { mp4_split_options_exit(r, options); switch (of.err) { case 0: return NGX_HTTP_INTERNAL_SERVER_ERROR; case NGX_ENOENT: case NGX_ENOTDIR: case NGX_ENAMETOOLONG: level = NGX_LOG_ERR; rc = NGX_HTTP_NOT_FOUND; break; case NGX_EACCES: level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; break; default: level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; break; } if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) { ngx_log_error(level, nlog, of.err, ngx_open_file_n " \"%s\" failed", path.data); } return rc; } if (!of.is_file) { mp4_split_options_exit(r, options); if (ngx_close_file(of.fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, ngx_close_file_n " \"%s\" failed", path.data); } return NGX_DECLINED; } /* move atom to beginning of file if it's in the last*/ hls_conf_t *mlcf; mlcf = ngx_http_get_module_loc_conf(r, ngx_http_estreaming_module); if (mlcf->mp4_enhance == 1) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "examine mp4 filename: \"%V\"", &path); if (ngx_http_enable_fast_start(&path, of.fd, r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } ngx_file_t *file = ngx_pcalloc(r->pool, sizeof (ngx_file_t)); if (file == NULL) { mp4_split_options_exit(r, options); return NGX_HTTP_INTERNAL_SERVER_ERROR; } file->fd = of.fd; file->name = path; file->log = nlog; mp4_context_t *mp4_context = mp4_open(r, file, of.size, MP4_OPEN_MOOV); if (!mp4_context) { mp4_split_options_exit(r, options); ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, "mp4_open failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } mp4_context->root = root; if (m3u8 || len_) { int ret, video_width = 0; // only request for master playlist need to use ffmpeg api to open video // this takes time and cpu, we should avoid it as much as possible if ((m3u8) && (options->adbr || options->org)) goto no_ffmpeg; AVFormatContext *fmt_ctx = NULL; unsigned int i; av_register_all(); if ((ret = avformat_open_input(&fmt_ctx, (const char*) path.data, NULL, NULL)) < 0) { mp4_split_options_exit(r, options); mp4_close(mp4_context); if (fmt_ctx) avformat_close_input(&fmt_ctx); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) { if (fmt_ctx) avformat_close_input(&fmt_ctx); mp4_close(mp4_context); mp4_split_options_exit(r, options); av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (len_) { duration = fmt_ctx->duration; u_char *buffer = (u_char *) ngx_palloc(mp4_context->r->pool, 10 * sizeof (char)); u_char * p = buffer; if (buffer == NULL) { mp4_split_options_exit(r, options); mp4_close(mp4_context); if (fmt_ctx) avformat_close_input(&fmt_ctx); return NGX_HTTP_INTERNAL_SERVER_ERROR; } // this is stolen from ffmpeg source code if (duration != AV_NOPTS_VALUE) { duration = duration + 5000; int secs; secs = duration / AV_TIME_BASE; p = ngx_sprintf(p, "%02d\n", secs); } else { p = ngx_sprintf(p, "N/A\n"); } bucket_insert(bucket, buffer, p - buffer); ngx_pfree(mp4_context->r->pool, buffer); if (fmt_ctx) avformat_close_input(&fmt_ctx); r->allow_ranges = 0; result = 1; goto response; } else { for (i = 0; i < fmt_ctx->nb_streams; i++) { AVStream *stream; AVCodecContext *codec_ctx; stream = fmt_ctx->streams[i]; codec_ctx = stream->codec; if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { av_log(NULL, AV_LOG_ERROR, "source video w:%d", codec_ctx->width); if (video_width == 0) { video_width = codec_ctx->width; } else if ((video_width != 0) && (video_width < codec_ctx->width)) { // has 2 video streams video_width = codec_ctx->width; } else break; } } avformat_close_input(&fmt_ctx); } // finish getting video width no_ffmpeg: if ((result = mp4_create_m3u8(mp4_context, bucket, options, video_width))) { char action[50]; sprintf(action, "ios_playlist&segments=%d", result); view_count(mp4_context, (char *) path.data, options ? options->hash : NULL, action); } r->allow_ranges = 0; } else { result = output_ts(mp4_context, bucket, options); if (!options || !result) { mp4_close(mp4_context); ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, "output_ts failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (options->adbr) { destination = ngx_pcalloc(r->pool, sizeof (video_buffer)); destination->data = NULL; destination->len = 0; destination->pool = r->pool; if (ngx_estreaming_adaptive_bitrate(r, bucket->first, destination, options) == NGX_OK) { ngx_buf_t *b = ngx_pcalloc(r->pool, sizeof (ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } bucket->first->buf = b; bucket->first->next = NULL; b->pos = destination->data; b->last = destination->data + (destination->len * sizeof (unsigned char)); b->memory = 1; b->last_buf = 1; bucket->content_length = destination->len; } ngx_pfree(r->pool, destination); } char action[50] = "ios_view"; view_count(mp4_context, (char *) path.data, options->hash, action); r->allow_ranges = 1; } response: mp4_close(mp4_context); mp4_split_options_exit(r, options); result = result == 0 ? 415 : 200; r->root_tested = !r->error_page; if (result && bucket) { nlog->action = "sending mp4 to client"; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, nlog, 0, "content_length: %d", bucket->content_length); if (bucket->content_length == 0) return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = bucket->content_length; r->headers_out.last_modified_time = of.mtime; if (m3u8) { r->headers_out.content_type.len = sizeof ("application/vnd.apple.mpegurl") - 1; r->headers_out.content_type.data = (u_char *) "application/vnd.apple.mpegurl"; } else if (len_) { r->headers_out.content_type.len = sizeof ("text/html") - 1; r->headers_out.content_type.data = (u_char *) "text/html"; } else { r->headers_out.content_type.len = sizeof ("video/MP2T") - 1; r->headers_out.content_type.data = (u_char *) "video/MP2T"; } rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, ngx_close_file_n "ngx_http_send_header failed"); return rc; } return ngx_http_output_filter(r, bucket->first); } else return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE; }