void *kalloc(unsigned int len){ Bucket *bdesc; struct _bucket_dir *bdir; void *retval = NULL; for(bdir = bucket_dir;bdir->size;bdir++) if(bdir->size > len) break; if(!bdir->size) panic("Don't alloc the memory!\n"); lock(); for(bdesc = bdir->bucket;bdesc;bdesc = bdesc->next) if(bdesc->freeptr) break; if(isnull(bdesc)){ char *cp = NULL; if(isnull(free_bucket)) bucket_init(); bdesc = free_bucket; free_bucket = free_bucket->next; bdesc->refcnt = 0; bdesc->bucket_size = bdir->size; bdesc->page = bdesc->freeptr = cp = (void *)(get_kfree_page()); if(isnull(cp)) panic("Out of memory in kernel malloc()\n"); for(int i = PAGE_SIZE / bdir->size;i > 1;i--){ *((char **)cp) = cp + bdir->size; cp += bdir->size; } *((char **)cp) = NULL; bdesc->next = bdir->bucket; bdir->bucket = bdesc; } retval = bdesc->freeptr; bdesc->freeptr = *((void **) retval); bdesc->refcnt++; unlock(); return retval; }
/*! \brief Aceita novas conexoes e aloca no vetor de clientes * * \param[in] r_server A estrutura servidor para a conexao de um cliente * \param[in] velocity A velocidade de transmissao em kb/s * * \return -1 caso aconteça algum erro * \return 0 caso OK */ int server_make_connection(server *r_server) { int connfd = -1; struct sockaddr_in cliaddr; socklen_t cli_length = sizeof(cliaddr); client_node *new_client = NULL; /* Select nao pode monitoras sockets alem de FD_SETSIZE */ if (FD_SETSIZE == r_server->l_clients.size) return -1; connfd = accept(r_server->listenfd, (struct sockaddr *) &cliaddr, &cli_length); if (0 > connfd) return -1; if (!(new_client = client_node_allocate(connfd))) { close(connfd); return -1; } client_node_append(new_client, &r_server->l_clients); bucket_init(r_server->velocity, &new_client->bucket); new_client->status = READ_REQUEST; r_server->maxfd_number = MAX(connfd, r_server->maxfd_number); return 0; }
extern bucket_t* bucket_init_file(uint64_t offset, uint64_t size) { bucket_t* bucket = bucket_init(BUCKET_TYPE_FILE); bucket->offset_ = offset; bucket->size_ = size; return bucket; }
extern bucket_t* bucket_init_memory(void const* buf, uint64_t size) { bucket_t* bucket = bucket_init(BUCKET_TYPE_MEMORY); bucket->buf_ = malloc((size_t)size); memcpy(bucket->buf_, buf, (size_t)size); bucket->size_ = size; return bucket; }
int main(int argc, char **argv) { struct bucket_conf c; struct leaky_bucket b; time_t start_time; time_t event_time; int ret; int i; #ifdef TEST_LEAKY_BUCKET_DEBUG printf("Testing with a rate of " RATE_STRING "\n"); #endif ret = bucket_conf_init(&c, RATE_STRING); if (ret) return ret; bucket_init(&b); start_time = b.tstamp; for (i = 1; i <= TOTAL_EVENTS; i++) { event_time = start_time + i * SECONDS_PER_EVENT; ret = __bucket_account(&c, &b, 1, event_time); #ifdef TEST_LEAKY_BUCKET_DEBUG if (ret) printf("Logging entry %d at %ld %ld\n", i, event_time - start_time, b.tstamp); #else if (i < THRESHOLD_EVENTS_PER_PERIOD) { if (!ret){ fprintf(stderr, "Did not log initial events - FAIL.\n"); return -1; } } else { if (!(i % EVENTS_PER_LOGGED_EVENT) && !ret) { fprintf(stderr, "Did not log initial events - FAIL.\n"); return -1; } } #endif } return 0; }
static uint32_t woov_write( struct mp4_context_t const* mp4_context, struct woov_t* woov, struct bucket_t** buckets) { unsigned int i; uint32_t woov_size=0; bucket_t* woov_bucket = bucket_init(BUCKET_TYPE_MEMORY); bucket_insert_tail(buckets, woov_bucket); for(i = 0; i != woov->moov->tracks_; ++i) { trak_t* trak = woov->moov->traks_[i]; samples_t const* first = (samples_t const*)&trak->samples_[0]; samples_t const* last = (samples_t const*)&trak->samples_[trak->samples_size_]; // info need to update trak->mdia_->minf_->stbl_->stts_ = stts_create(mp4_context, first, last); //time-to-sample trak->mdia_->minf_->stbl_->ctts_ = ctts_create(mp4_context, first, last); //composition time-to-sample table trak->mdia_->minf_->stbl_->stsz_ = stsz_create(mp4_context, first, last); //sample sizes (framing) trak->mdia_->minf_->stbl_->stss_ = stss_create(mp4_context, first, last); //sync sample // update trak duration trak->mdia_->mdhd_->duration_ = last->pts_ - first->pts_; trak->tkhd_->duration_ = trak_time_to_moov_time(trak->mdia_->mdhd_->duration_, woov->moov->mvhd_->timescale_, trak->mdia_->mdhd_->timescale_); // update movie duration if(trak->tkhd_->duration_ > woov->moov->mvhd_->duration_) { woov->moov->mvhd_->duration_ = trak->tkhd_->duration_ ; } } woov_bucket->buf_ = malloc( 16 * 1024 * 1024 ); woov_bucket->size_ = woov_size = moov_write(woov->moov, (unsigned char*)woov_bucket->buf_); //write_32((unsigned char*)woov_bucket->buf_ + 4, FOURCC('m', 'o', 'o', 'v')); write_32((unsigned char*)woov_bucket->buf_ + 4, FOURCC('f', 'r', 'e', 'e')); return woov_size; }
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 int merge_invertedlist_page(struct posting *p,struct bucket *bkt, struct cache *cache,struct freemap *freemap) { unsigned int docnum; unsigned int lastdoc; unsigned int firstdoc; int len_firstdoc; int fd; off_t offset; unsigned int size; unsigned short invertedlen; unsigned int add_size; struct vector *v; struct bucket tmp; struct page *page; int page_data_size; int available_size; char *ptr; assert(p); v = p->invertedlist; len_firstdoc = vbyte_decompress(v->vector,v->vector+v->len,&firstdoc); if(len_firstdoc <= 0) goto exit; docnum = *((unsigned int*)bkt->record); lastdoc = *((unsigned int*)((char*)bkt->record+sizeof(unsigned int))); assert(firstdoc > lastdoc); page_data_size = PAGE_SIZE - sizeof(struct HEAD); invertedlen = bkt->head->num; /* special use for such kind of page,bkt->head->num no longer store the number of records, it stores invertedlen instead. */ add_size = v->len - len_firstdoc; bkt->head->num += (v->len-len_firstdoc+vbyte_len(firstdoc-lastdoc)); *((unsigned int*)bkt->record) += p->count; ((unsigned int*)bkt->record)[1] = p->lastdoc; /* switch to the last page */ tmp = *bkt; while(tmp.head->next.fd != 0) { fd = tmp.head->next.fd; offset = tmp.head->next.offset; page = cache_pagein(cache,fd,offset); if(page == NULL) goto exit; bucket_load(&tmp,page); } /* add invertedlist in the page */ if((char*)tmp.record+tmp.head->ptr + vbyte_len(firstdoc-lastdoc) < (char*)tmp.head + PAGE_SIZE) tmp.head->ptr += vbyte_compress((char*)tmp.record+tmp.head->ptr,(char*)tmp.head+PAGE_SIZE,firstdoc-lastdoc); else { size = PAGE_SIZE; if(freemap_malloc(freemap,&size,&fd,&offset) != 0) goto exit; page = cache_newpage(cache,fd,offset); if(page == NULL) goto exit; bucket_init(&tmp,page,0); tmp.head->next.fd = fd; tmp.head->next.offset = offset; tmp.head->ptr = 0; tmp.head->ptr += vbyte_compress((char*)tmp.record+tmp.head->ptr,(char*)tmp.head+PAGE_SIZE,firstdoc-lastdoc); } ptr = v->vector+len_firstdoc; while(add_size > 0) { if((char*)tmp.record + tmp.head->ptr + add_size < (char*)tmp.head + PAGE_SIZE) { memcpy((char*)tmp.record+tmp.head->ptr,ptr,add_size); tmp.head->ptr += add_size; add_size = 0; } else { available_size = PAGE_SIZE-sizeof(struct HEAD)-tmp.head->ptr; memcpy((char*)tmp.record+tmp.head->ptr,ptr,available_size); ptr += available_size; tmp.head->ptr += available_size; add_size -= available_size; size = PAGE_SIZE; if(freemap_malloc(freemap,&size,&fd,&offset) != 0) goto exit; page = cache_newpage(cache,fd,offset); if(page == NULL) goto exit; tmp.head->next.fd = fd; tmp.head->next.offset = offset; bucket_init(&tmp,page,0); tmp.head->ptr = 0; } } return 0; exit: return -1; }
bucket_t* bucket_split(bucket_t* bucket1, bool (*split_funct)(void* data, unsigned int key), void* data) { assert(bucket1 != NULL && split_funct != NULL); unsigned int i; unsigned int curr_node_size; bool error = false; bucket_t* bucket2 = NULL; bucket_t** dst_bucket_ptr = NULL; bucket_t* src_bucket = NULL; bucket_node_t* src_bucket_node = NULL; /* Allocate memory for the new bucket */ if(!error && (bucket2 = bucket_init(bucket1->node_capacity)) == NULL) error = true; /* Make a copy of the old bucket's head */ if((src_bucket = bucket_init(bucket1->node_capacity)) == NULL) error = true; if(!error) { /* Move internal data from the old bucket' head to the new bucket head. No deep copy is done, the chain is moved too. */ *src_bucket = *bucket1; /* Reset the given bucket's head */ *bucket1 = *bucket2; /* Initialize src_bucket_node */ src_bucket_node = src_bucket->chain; } /* For every bucket_node in the chain... */ while(!error && src_bucket_node != NULL) { /* Get the current bucket_node's size */ curr_node_size = bucket_node_size(src_bucket, src_bucket_node); /* For every entry in the bucket_node... */ for(i=0; !error && i < curr_node_size; i++) { /* Call split_funct to get the target bucket */ if(!(*split_funct)(data, src_bucket_node->key[i])) dst_bucket_ptr = &bucket1; else dst_bucket_ptr = &bucket2; /* Append to the appropriate new bucket (entries are already sorted) */ if(!bucket_node_insert(*dst_bucket_ptr, bucket_size(*dst_bucket_ptr), src_bucket_node->key[i], src_bucket_node->value[i])) error = true; } src_bucket_node = src_bucket_node->overflow; } if(error) { /* Restore the old bucket in its original condition */ *bucket1 = *src_bucket; /* Free allocated memory */ bucket_destroy(&bucket2); /* Prevent old bucket chain from being destroyed */ src_bucket->chain = NULL; } /* Destroy the old bucket */ bucket_destroy(&src_bucket); return bucket2; }
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; }
extern int mp4_fragment_file(struct mp4_context_t const* mp4_context, struct bucket_t** buckets) { uint64_t filepos = 0; int result = 1; moov_t* moov = mp4_context->moov; moov_t* fmoov; struct woov_t* woov = NULL; mfra_t* mfra; if(!moov_build_index(mp4_context, mp4_context->moov)) { return 0; } // Start with the ftyp { unsigned char ftyp[28]; unsigned char* buffer = ftyp; buffer = write_32(buffer, 28); buffer = write_32(buffer, FOURCC('f', 't', 'y', 'p')); buffer = write_32(buffer, FOURCC('a', 'v', 'c', '1')); // major_brand buffer = write_32(buffer, 0); // minor_version buffer = write_32(buffer, FOURCC('i', 's', 'o', 'm')); // compatible_brands buffer = write_32(buffer, FOURCC('i', 's', 'o', '2')); buffer = write_32(buffer, FOURCC('f', 'r', 'a', 'g')); bucket_insert_tail(buckets, bucket_init_memory(ftyp, sizeof(ftyp))); filepos += sizeof(ftyp); } { uint32_t i; struct trak_weight_t{ int w; void* v; }wtrack[MAX_TRACKS]; for(i = 0; i < moov->tracks_; i++) { if(moov->traks_[i]->mdia_->hdlr_->handler_type_ == FOURCC('s', 'o', 'u', 'n')) {wtrack[i].w=1;wtrack[i].v=moov->traks_[i];} else if(moov->traks_[i]->mdia_->hdlr_->handler_type_ == FOURCC('v', 'i', 'd', 'e')) {wtrack[i].w=2;wtrack[i].v=moov->traks_[i];} else {wtrack[i].w=3;wtrack[i].v=moov->traks_[i];} } for (i = 1; i < moov->tracks_; ++i) { unsigned int j; for (j= moov->tracks_ - 1; j>=i;j--) { if(wtrack[j].w < wtrack[j-1].w) { struct trak_weight_t t = wtrack[j]; wtrack[j] = wtrack[j-1]; wtrack[j-1] = t; } } } for (i = 0; i < moov->tracks_; ++i) { moov->traks_[i] = wtrack[i].v; moov->traks_[i]->tkhd_->track_id_=i+1; } moov->mvhd_->next_track_id_=i+1; } // A fragmented MPEG4 file starts with a MOOV atom with only the mandatory // atoms fmoov = moov_init(); { unsigned int i; mvex_t* mvex = mvex_init(); fmoov->mvhd_ = mvhd_copy(moov->mvhd_); fmoov->mvhd_->duration_ = 0; fmoov->tracks_ = moov->tracks_; fmoov->mvex_ = mvex; for(i = 0; i != moov->tracks_; ++i) { unsigned int s; trak_t* trak = moov->traks_[i]; trak_t* ftrak = trak_init(); mdia_t* mdia = trak->mdia_; mdia_t* fmdia = mdia_init(); minf_t* minf = mdia->minf_; minf_t* fminf = minf_init(); stbl_t* stbl = minf->stbl_; stbl_t* fstbl = stbl_init(); fmoov->traks_[i] = ftrak; ftrak->tkhd_ = tkhd_copy(trak->tkhd_); ftrak->tkhd_->duration_ = 0; ftrak->mdia_ = fmdia; ftrak->samples_size_ = trak->samples_size_; ftrak->samples_ = (samples_t*) malloc((trak->samples_size_ + 1) * sizeof(samples_t)); memcpy(ftrak->samples_, trak->samples_, (trak->samples_size_ + 1) * sizeof(samples_t)); ftrak->smoothes_size_ = trak->smoothes_size_; ftrak->smoothes_ = (struct smooth_t*) malloc((trak->samples_size_ + 1) * sizeof(struct smooth_t)); memcpy(ftrak->smoothes_, trak->smoothes_, (trak->smoothes_size_ + 1) * sizeof(struct smooth_t)); fmdia->mdhd_ = mdhd_copy(mdia->mdhd_); // convert trak's timescale and duration fmdia->mdhd_->version_ = 1; fmdia->mdhd_->timescale_ = 10000000; fmdia->mdhd_->duration_ = 0; // trak_time_to_moov_time(fmdia->mdhd_->duration_, // fmdia->mdhd_->timescale_, mdia->mdhd_->timescale_); fmdia->hdlr_ = hdlr_copy(mdia->hdlr_); fmdia->minf_ = fminf; fminf->smhd_ = minf->smhd_ == NULL ? NULL : smhd_copy(minf->smhd_); fminf->vmhd_ = minf->vmhd_ == NULL ? NULL : vmhd_copy(minf->vmhd_); fminf->dinf_ = dinf_copy(minf->dinf_); fminf->stbl_ = fstbl; fstbl->stts_ = stts_init(); fstbl->ctts_ = ctts_init(); fstbl->stsz_ = stsz_init(); fstbl->stsc_ = stsc_init(); fstbl->stco_ = stco_init(); fstbl->stsd_ = stsd_copy(stbl->stsd_); for(s = 0; s != ftrak->samples_size_ + 1; ++s) { // SmoothStreaming uses a fixed 10000000 timescale ftrak->samples_[s].pts_ = trak_time_to_moov_time( ftrak->samples_[s].pts_, ftrak->mdia_->mdhd_->timescale_, trak->mdia_->mdhd_->timescale_); ftrak->samples_[s].cto_ = (unsigned int)(trak_time_to_moov_time( ftrak->samples_[s].cto_, ftrak->mdia_->mdhd_->timescale_, trak->mdia_->mdhd_->timescale_)); } { // update trak duration samples_t const* first = (samples_t const*)&ftrak->samples_[0]; samples_t const* last = (samples_t const*)&ftrak->samples_[ftrak->samples_size_]; ftrak->mdia_->mdhd_->duration_ = last->pts_ - first->pts_; ftrak->tkhd_->duration_ = trak_time_to_moov_time(ftrak->mdia_->mdhd_->duration_, fmoov->mvhd_->timescale_, ftrak->mdia_->mdhd_->timescale_); // update movie duration if(ftrak->tkhd_->duration_ > fmoov->mvhd_->duration_) { fmoov->mvhd_->duration_ = ftrak->tkhd_->duration_ ; } } { trex_t* trex = trex_init(); trex->track_id_ = trak->tkhd_->track_id_; trex->default_sample_description_index_ = 1; mvex->trexs_[mvex->tracks_] = trex; ++mvex->tracks_; } } { unsigned char* moov_data = mp4_context->moov_data; uint32_t moov_size = moov_write(fmoov, moov_data); bucket_insert_tail(buckets, bucket_init_memory(moov_data, moov_size)); filepos += moov_size; } } woov = woov_init(mp4_context, fmoov); mfra = mfra_init(); mfra->tracks_ = fmoov->tracks_; { unsigned int i; unsigned int tfra_entries = 0; for(i = 0; i != fmoov->tracks_; ++i) { trak_t const* trak = fmoov->traks_[i]; struct tfra_t* tfra = tfra_init(); mfra->tfras_[i] = tfra; tfra->version_ = 1; tfra->flags_ = 0; tfra->track_id_ = trak->tkhd_->track_id_; tfra->length_size_of_traf_num_ = 1; tfra->length_size_of_trun_num_ = 1; tfra->length_size_of_sample_num_ = 1; // count the number of smooth sync samples (nr of moofs) tfra->number_of_entry_ = 0; { unsigned int start; for(start = 0; start != trak->samples_size_; ++start) { { if(trak->samples_[start].is_smooth_ss_) { ++tfra->number_of_entry_; } } } } tfra->table_ = (tfra_table_t*) malloc(tfra->number_of_entry_ * sizeof(tfra_table_t)); tfra_entries += tfra->number_of_entry_; // next track } { unsigned int tfra_index = 0; trak_t const* base_trak = fmoov->traks_[0]; while(tfra_index != base_trak->smoothes_size_) { // insert moof bucket { moof_t* moof = moof_init(); bucket_t* bucket = bucket_init(BUCKET_TYPE_MEMORY); bucket_insert_tail(buckets, bucket); // create moof and write samples moof_create(mp4_context, fmoov, woov, moof, mfra, filepos, tfra_index+1, buckets, 0 /* OUTPUT_FORMAT_MP4 */); // if(options->output_format == OUTPUT_FORMAT_MP4) { unsigned int samples_count = 0; unsigned char* moof_data = NULL; unsigned int moof_size = 0; for(i = 0;i < moof->tracks_; ++i) samples_count += moof->trafs_[i]->trun_->sample_count_; moof_data = (unsigned char*)malloc(8192 + (samples_count) * 12); moof_size = moof_write(moof, moof_data); // now that we know the size of the moof atom, we know where the mdat // will start. We patch the 'data_offset' field to skip the // moof atom and the mdat header. moof->trafs_[0]->trun_->data_offset_ = moof_size + ATOM_PREAMBLE_SIZE; moof_size = moof_write(moof, moof_data); bucket->buf_ = malloc(moof_size); bucket->size_ = moof_size; memcpy(bucket->buf_, moof_data, (size_t)bucket->size_); free(moof_data); } moof_exit(moof); // advance filepos for moof and mdat atom while(*buckets != bucket) { filepos += bucket->size_; bucket = bucket->next_; } } // next fragment ++tfra_index; } } moov_exit(fmoov); { uint32_t woov_size = woov_write(mp4_context, woov, buckets); int offset = 0; offset += 28; // ftyp offset += woov_size; //woov offset += ATOM_PREAMBLE_SIZE; //mdat woov->mdat_size+=8; // header; if(woov->mdat_size > UINT32_MAX) { offset+=8; woov->mdat_size+=8; } for(i = 0; i != woov->moov->tracks_; ++i) { trak_t* trak = woov->moov->traks_[i]; stco_shift_offsets_inplace( (unsigned char*)trak->mdia_->minf_->stbl_->stco_->stco_inplace_, (int)offset); } write_32(woov_data + 8, woov_size); write_64(woov_data + 12, woov->mdat_size); bucket_insert_tail(buckets, bucket_init_memory(woov_data, sizeof(woov_data))); woov_exit(woov); } // Write the Movie Fragment Random Access (MFRA) atom { unsigned char* mfra_data = (unsigned char*)malloc(8192 + tfra_entries * 28); uint32_t mfra_size = mfra_write(mfra, mfra_data); bucket_insert_tail(buckets, bucket_init_memory(mfra_data, mfra_size)); mfra_exit(mfra); free(mfra_data); } } return result; }