Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
/*! \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;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
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;
}
Exemplo n.º 8
0
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;
}
Exemplo n.º 9
0
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;
}
Exemplo n.º 11
0
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;
}