void *bucket_split(Bucket *bucket, Bucket *buckets, uint32_t bucket_index, Heap *heap, uint32_t block_size) { uint32_t first_block_index = bucket_get_first_block_index(bucket); Block *first_block_header = block_get_header(heap, first_block_index); uint32_t first_block_size = block_get_size(first_block_header); // Calculate the remaining size of the first block. uint32_t rem_size = first_block_size - block_size; // Create a new header for the new block we have created. uint32_t alloc_index = first_block_index + block_size_to_bytes(rem_size); Block *alloc_header = block_get_header(heap, alloc_index); uint32_t next_block_index = block_get_next_index(first_block_header); Block *next_block_header = block_get_header(heap, next_block_index); // If we are simply replacing the block, we don't need to update the previous // and next indexes of memory, since they will not change: no blocks are // being created or merged. if (rem_size != 0) { block_set_size(alloc_header, block_size); block_set_prev_index(alloc_header, first_block_index); block_set_next_index(alloc_header, next_block_index); } block_set_allocated(alloc_header, true); // If there is no left over space in the block, there is no need to split it. // Simply remove this block from the bucket and return an address to it. if (rem_size == 0) { bucket_remove(bucket, heap, first_block_index); return &heap->memory[block_index_skip_header(alloc_index)]; } // Update the header of the remaining part of the block. block_set_next_index(first_block_header, alloc_index); block_set_size(first_block_header, rem_size); // Update the header of the next block. next_block_header = block_get_header(heap, next_block_index); block_set_prev_index(next_block_header, alloc_index); // If the remaining space from the block is now too small for its bucket, it // is removed from this bucket and reinserted into the bucket structure. uint32_t first_block_bucket_index = block_get_bucket_index( first_block_header); if (first_block_bucket_index != bucket_index) { bucket_remove(bucket, heap, first_block_index); bucket_insert(&buckets[first_block_bucket_index], heap, first_block_index); } return &heap->memory[block_index_skip_header(alloc_index)]; }
void smap_put(smap *map, char *key, int value) { if (!map) { return; } size_t hash = hash_string(key) % map->num_buckets; bucket buck = map->buckets[hash]; for (size_t i = 0; i < buck.num_pairs; i += 1) { if (!strcmp(key, buck.pairs[i].key)) { buck.pairs[i].val = value; return; } } if ((map->num_pairs + 1) < map->num_buckets * LOAD_FACTOR) { bucket_insert(map->buckets + hash, key, value); map->num_pairs += 1; } else { expand(map); smap_put(map, key, value); } }
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; }
/* * Bucket-sort algorithm. */ extern void bucketsort(int *array, int n) { int max; /* Maximum number. */ int i, j; /* Loop indexes. */ int range; /* Bucket range. */ struct minibucket *minib; /* Working mini-bucket. */ struct message *msg; /* Working message. */ struct bucket **todo; /* Todo buckets. */ struct bucket **done; /* Done buckets. */ uint64_t start, end; /* Timers. */ /* Setup slaves. */ open_noc_connectors(); spawn_slaves(); sync_slaves(); todo = smalloc(NUM_BUCKETS*sizeof(struct bucket *)); done = smalloc(NUM_BUCKETS*sizeof(struct bucket *)); for (i = 0; i < NUM_BUCKETS; i++) { done[i] = bucket_create(); todo[i] = bucket_create(); } /* Find max number in the array. */ start = timer_get(); max = INT_MIN; for (i = 0; i < n; i++) { /* Found. */ if (array[i] > max) max = array[i]; } /* Distribute numbers. */ range = max/NUM_BUCKETS; for (i = 0; i < n; i++) { j = array[i]/range; if (j >= NUM_BUCKETS) j = NUM_BUCKETS - 1; bucket_insert(&todo[j], array[i]); } end = timer_get(); master += timer_diff(start, end); /* Sort buckets. */ j = 0; for (i = 0; i < NUM_BUCKETS; i++) { while (bucket_size(todo[i]) > 0) { minib = bucket_pop(todo[i]); /* Send message. */ msg = message_create(SORTWORK, i, minib->size); message_send(outfd[j], msg); message_destroy(msg); /* Send data. */ communication += data_send(outfd[j], minib->elements, minib->size*sizeof(int)); minibucket_destroy(minib); j++; /* * Slave processes are busy. * So let's wait for results. */ if (j == nclusters) { /* Receive results. */ for (/* NOOP */ ; j > 0; j--) { /* Receive message. */ msg = message_receive(infd[nclusters - j]); /* Receive mini-bucket. */ minib = minibucket_create(); minib->size = msg->u.sortresult.size; communication += data_receive(infd[nclusters -j], minib->elements, minib->size*sizeof(int)); bucket_push(done[msg->u.sortresult.id], minib); message_destroy(msg); } } } } /* Receive results. */ for (/* NOOP */ ; j > 0; j--) { /* Receive message. */ msg = message_receive(infd[j - 1]); /* Receive bucket. */ minib = minibucket_create(); minib->size = msg->u.sortresult.size; communication += data_receive(infd[j - 1], minib->elements, minib->size*sizeof(int)); bucket_push(done[msg->u.sortresult.id], minib); message_destroy(msg); } start = timer_get(); rebuild_array(done, array); end = timer_get(); master += timer_diff(start, end); /* House keeping. */ for (i = 0; i < NUM_BUCKETS; i++) { bucket_destroy(todo[i]); bucket_destroy(done[i]); } free(done); free(todo); join_slaves(); close_noc_connectors(); }