コード例 #1
0
vod_status_t
hds_muxer_init_fragment(
	request_context_t* request_context,
	hds_fragment_config_t* conf,
	uint32_t segment_index,
	mpeg_metadata_t *mpeg_metadata,
	read_cache_state_t* read_cache_state,
	write_callback_t write_callback,
	void* write_context,
	bool_t size_only,
	vod_str_t* header, 
	size_t* total_fragment_size,
	hds_muxer_state_t** processor_state)
{
	mpeg_stream_metadata_t* cur_stream;
	hds_muxer_stream_state_t* stream_state;
	input_frame_t* cur_frame;
	input_frame_t* frames_end;
	hds_muxer_state_t* state;
	vod_status_t rc;
	uint32_t track_id = 1;
	uint32_t* output_offset;
	uint32_t frame_metadata_size;
	size_t afra_atom_size;
	size_t moof_atom_size;
	size_t traf_atom_size;
	size_t mdat_atom_size;
	size_t result_size;
	u_char* p;

	// initialize the muxer state
	rc = hds_muxer_init_state(request_context, mpeg_metadata, read_cache_state, write_callback, write_context, &state);
	if (rc != VOD_OK)
	{
		vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_muxer_init_fragment: hds_muxer_init_state failed %i", rc);
		return rc;
	}

	// get moof atom size
	moof_atom_size = 
		ATOM_HEADER_SIZE + 
		ATOM_HEADER_SIZE + sizeof(mfhd_atom_t);
	mdat_atom_size = ATOM_HEADER_SIZE;
	state->codec_config_size = 0;
	for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
	{
		moof_atom_size += hds_get_traf_atom_size(cur_stream);

		frame_metadata_size = tag_size_by_media_type[cur_stream->media_info.media_type] + sizeof(uint32_t);
		state->codec_config_size += frame_metadata_size + cur_stream->media_info.extra_data_size;

		mdat_atom_size += cur_stream->total_frames_size + cur_stream->frame_count * frame_metadata_size;
	}

	mdat_atom_size += mpeg_metadata->video_key_frame_count * state->codec_config_size;

	// get the fragment header size
	if (conf->generate_moof_atom)
	{
		afra_atom_size = ATOM_HEADER_SIZE + sizeof(afra_atom_t) + sizeof(afra_entry_t) * mpeg_metadata->video_key_frame_count;
	}
	else
	{
		afra_atom_size = 0;
		moof_atom_size = 0;
	}

	result_size =
		afra_atom_size +
		moof_atom_size +
		ATOM_HEADER_SIZE;		// mdat

	// audio only - output the codec config up front, video - output the codec config before every key frame
	if (mpeg_metadata->video_key_frame_count == 0)
	{
		result_size += state->codec_config_size;
		mdat_atom_size += state->codec_config_size;
	}

	*total_fragment_size =
		afra_atom_size +
		moof_atom_size +
		mdat_atom_size;

	// head request optimization
	if (size_only)
	{
		return VOD_OK;
	}

	// allocate the response
	header->data = vod_alloc(request_context->pool, result_size);
	if (header->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_muxer_init_fragment: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	p = header->data;

	if (conf->generate_moof_atom)
	{
		// afra
		p = hds_write_afra_atom_header(p, afra_atom_size, mpeg_metadata->video_key_frame_count);

		p = hds_calculate_output_offsets_and_write_afra_entries(state, ATOM_HEADER_SIZE, afra_atom_size + moof_atom_size, p);

		// moof
		write_atom_header(p, moof_atom_size, 'm', 'o', 'o', 'f');

		// moof.mfhd
		p = mp4_builder_write_mfhd_atom(p, segment_index);

		for (stream_state = state->first_stream; stream_state < state->last_stream; stream_state++)
		{
			cur_stream = stream_state->metadata;

			// moof.traf
			traf_atom_size = hds_get_traf_atom_size(cur_stream);
			write_atom_header(p, traf_atom_size, 't', 'r', 'a', 'f');

			// moof.traf.tfhd
			p = hds_write_tfhd_atom(p, track_id, ATOM_HEADER_SIZE + sizeof(afra_atom_t)+moof_atom_size);

			// moof.traf.trun
			frames_end = cur_stream->frames + cur_stream->frame_count;
			switch (cur_stream->media_info.media_type)
			{
			case MEDIA_TYPE_VIDEO:
				for (cur_frame = cur_stream->frames, output_offset = stream_state->first_frame_output_offset;
					cur_frame < frames_end;
					cur_frame++, output_offset++)
				{
					p = hds_write_single_video_frame_trun_atom(p, cur_frame, *output_offset);
				}
				break;

			case MEDIA_TYPE_AUDIO:
				for (cur_frame = cur_stream->frames, output_offset = stream_state->first_frame_output_offset;
					cur_frame < frames_end;
					cur_frame++, output_offset++)
				{
					p = hds_write_single_audio_frame_trun_atom(p, cur_frame, *output_offset);
				}
				break;
			}
		}
	}
	else
	{
		// calculate the output offsets
		hds_calculate_output_offsets_and_write_afra_entries(state, 0, 0, NULL);
	}

	// mdat
	write_atom_header(p, mdat_atom_size, 'm', 'd', 'a', 't');

	if (mpeg_metadata->video_key_frame_count == 0)
	{
		p = hds_muxer_write_codec_config(p, state, state->first_stream->next_frame_dts);
	}

	header->len = p - header->data;

	if (header->len != result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"hds_muxer_init_fragment: result length %uz exceeded allocated length %uz",
			header->len, result_size);
		return VOD_UNEXPECTED;
	}

	*processor_state = state;

	return VOD_OK;
}
コード例 #2
0
ファイル: rate_filter.c プロジェクト: opshu/nginx-vod-module
vod_status_t
rate_filter_parse(
	void* ctx,
	vod_json_value_t* element,
	void** result)
{
	media_filter_parse_context_t* context = ctx;
	media_clip_rate_filter_t* filter;
	media_range_t* new_range;
	media_range_t* old_range;
	vod_json_value_t* params[RATE_FILTER_PARAM_COUNT];
	vod_json_value_t* source;
	vod_json_value_t* rate;
	uint32_t old_duration;
	vod_status_t rc;

	vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0,
		"rate_filter_parse: started");

	vod_memzero(params, sizeof(params));
		
	vod_json_get_object_values(
		element,
		&rate_filter_hash,
		params);

	rate = params[RATE_FILTER_PARAM_RATE];
	source = params[RATE_FILTER_PARAM_SOURCE];

	if (rate == NULL || source == NULL)
	{
		vod_log_error(VOD_LOG_ERR, context->request_context->log, 0,
			"rate_filter_parse: \"rate\" and \"source\" are mandatory for rate filter");
		return VOD_BAD_MAPPING;
	}

	if (rate->v.num.denom > 100)
	{
		vod_log_error(VOD_LOG_ERR, context->request_context->log, 0,
			"rate_filter_parse: invalid rate, only 2 decimal points are allowed");
		return VOD_BAD_MAPPING;
	}
	
	if (rate->v.num.nom < 0 ||
		rate->v.num.denom > (uint64_t)rate->v.num.nom * 2 || (uint64_t)rate->v.num.nom > rate->v.num.denom * 2)
	{
		vod_log_error(VOD_LOG_ERR, context->request_context->log, 0,
			"rate_filter_parse: invalid rate %L/%uL, must be between 0.5 and 2", rate->v.num.nom, rate->v.num.denom);
		return VOD_BAD_MAPPING;
	}

	filter = vod_alloc(context->request_context->pool, sizeof(*filter) + sizeof(filter->base.sources[0]));
	if (filter == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0,
			"rate_filter_parse: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}
	filter->base.sources = (void*)(filter + 1);
	filter->base.source_count = 1;

	filter->base.type = MEDIA_CLIP_RATE_FILTER;
	filter->base.audio_filter = &rate_filter;
	filter->rate.nom = rate->v.num.nom;
	filter->rate.denom = rate->v.num.denom;

	old_range = context->range;
	if (old_range != NULL)
	{
		new_range = vod_alloc(context->request_context->pool, sizeof(*new_range));
		if (new_range == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0,
				"rate_filter_parse: vod_alloc failed (2)");
			return VOD_ALLOC_FAILED;
		}

		new_range->start = (old_range->start * filter->rate.nom) / filter->rate.denom;
		new_range->end = (old_range->end * filter->rate.nom) / filter->rate.denom;
		new_range->timescale = old_range->timescale;

		context->range = new_range;
	}

	old_duration = context->duration;
	context->duration = ((uint64_t)old_duration * filter->rate.nom) / filter->rate.denom;

	rc = media_set_parse_clip(
		context, 
		source, 
		&filter->base,
		&filter->base.sources[0]);
	if (rc != VOD_JSON_OK)
	{
		return rc;
	}

	context->range = old_range;
	context->duration = old_duration;

	*result = &filter->base;

	vod_log_debug2(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0,
		"rate_filter_parse: done, rate=%uD/%uD", filter->rate.nom, filter->rate.denom);

	return VOD_OK;
}
コード例 #3
0
static vod_status_t
m3u8_builder_build_required_tracks_string(
	request_context_t* request_context, 
	bool_t include_file_index,
	mpeg_metadata_t* mpeg_metadata, 
	vod_str_t* required_tracks)
{
	mpeg_stream_metadata_t* cur_stream;
	uint32_t printed_file_indexes;
	uint32_t file_index;
	u_char* p;
	size_t result_size;

	result_size = mpeg_metadata->streams.nelts * (sizeof("-v") - 1 + vod_get_int_print_len(mpeg_metadata->max_track_index + 1));
	if (include_file_index)
	{
		result_size += mpeg_metadata->streams.nelts * (sizeof("-f") - 1 + vod_get_int_print_len(mpeg_metadata->first_stream->file_info.file_index + 1));
	}
	p = vod_alloc(request_context->pool, result_size + 1);
	if (p == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_required_tracks_string: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	required_tracks->data = p;

	if (include_file_index)
	{
		printed_file_indexes = 0;
		for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
		{
			file_index = cur_stream->file_info.file_index;
			if ((printed_file_indexes & (1 << file_index)) != 0)
			{
				continue;
			}

			p = vod_sprintf(p, "-f%uD", file_index + 1);
			printed_file_indexes |= (1 << file_index);
		}
	}

	for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
	{
		*p++ = '-';
		switch (cur_stream->media_info.media_type)
		{
		case MEDIA_TYPE_VIDEO:
			*p++ = 'v';
			break;

		case MEDIA_TYPE_AUDIO:
			*p++ = 'a';
			break;

		default:
			continue;
		}

		p = vod_sprintf(p, "%uD", cur_stream->track_index + 1);
	}
	
	required_tracks->len = p - required_tracks->data;

	if (required_tracks->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_required_tracks_string: result length %uz exceeded allocated length %uz", 
			required_tracks->len, result_size);
		return VOD_UNEXPECTED;
	}

	return VOD_OK;
}
コード例 #4
0
static vod_status_t
hds_muxer_init_state(
	request_context_t* request_context,
	mpeg_metadata_t *mpeg_metadata,
	read_cache_state_t* read_cache_state,
	write_callback_t write_callback,
	void* write_context,
	hds_muxer_state_t** result)
{
	mpeg_stream_metadata_t* cur_stream;
	hds_muxer_stream_state_t* stream_state;
	hds_muxer_state_t* state;
	vod_status_t rc;

	// allocate the state and stream states
	state = vod_alloc(request_context->pool, sizeof(*state));
	if (state == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_muxer_init_state: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	state->first_stream = vod_alloc(
		request_context->pool, 
		sizeof(state->first_stream[0]) * mpeg_metadata->streams.nelts);
	if (state->first_stream == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_muxer_init_state: vod_alloc failed (2)");
		return VOD_ALLOC_FAILED;
	}
	state->last_stream = state->first_stream + mpeg_metadata->streams.nelts;
	state->request_context = request_context;
	state->cur_frame = NULL;

	state->read_cache_state = read_cache_state;
	write_buffer_init(&state->write_buffer_state, request_context, write_callback, write_context);

	stream_state = state->first_stream;
	for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++, stream_state++)
	{
		// initialize the stream
		stream_state->metadata = cur_stream;
		stream_state->media_type = cur_stream->media_info.media_type;
		stream_state->timescale = cur_stream->media_info.timescale;
		stream_state->frames_file_index = cur_stream->frames_file_index;
		stream_state->first_frame = cur_stream->frames;
		stream_state->last_frame = cur_stream->frames + cur_stream->frame_count;

		stream_state->first_frame_time_offset = cur_stream->first_frame_time_offset;
		stream_state->next_frame_time_offset = cur_stream->first_frame_time_offset;
		stream_state->next_frame_dts = rescale_time(stream_state->next_frame_time_offset, stream_state->timescale, HDS_TIMESCALE);

		stream_state->cur_frame = stream_state->first_frame;

		stream_state->first_frame_input_offset = cur_stream->frame_offsets;
		stream_state->cur_frame_input_offset = stream_state->first_frame_input_offset;

		stream_state->first_frame_output_offset = vod_alloc(
			request_context->pool,
			cur_stream->frame_count * sizeof(uint32_t));
		if (stream_state->first_frame_output_offset == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"hds_muxer_init_state: vod_alloc failed (3)");
			return VOD_ALLOC_FAILED;
		}
		stream_state->cur_frame_output_offset = stream_state->first_frame_output_offset;

		if (cur_stream->media_info.media_type == MEDIA_TYPE_AUDIO)
		{
			rc = hds_get_sound_info(request_context, &cur_stream->media_info, &stream_state->sound_info);
			if (rc != VOD_OK)
			{
				vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
					"hds_muxer_init_state: hds_get_sound_info failed %i", rc);
				return rc;
			}
		}
	}

	*result = state;

	return VOD_OK;
}
コード例 #5
0
vod_status_t
m3u8_builder_build_index_playlist(
	request_context_t* request_context,
	m3u8_config_t* conf,
	vod_str_t* base_url,
	vod_str_t* segments_base_url,
	bool_t include_file_index,
	hls_encryption_params_t* encryption_params,
	segmenter_conf_t* segmenter_conf,
	mpeg_metadata_t* mpeg_metadata,
	vod_str_t* result)
{
	segment_durations_t segment_durations;
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item;
	vod_str_t extinf;
	uint32_t segment_index;
	uint32_t last_segment_index;
	vod_str_t required_tracks;
	uint32_t scale;
	size_t segment_length;
	size_t result_size;
	vod_status_t rc;
	u_char* p;

	// build the required tracks string
	rc = m3u8_builder_build_required_tracks_string(
		request_context, 
		include_file_index,
		mpeg_metadata, 
		&required_tracks);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// get the segment durations
	rc = segmenter_conf->get_segment_durations(
		request_context,
		segmenter_conf,
		mpeg_metadata->longest_stream,
		MEDIA_TYPE_COUNT,
		&segment_durations);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// get the required buffer length
	segment_length = sizeof("#EXTINF:.000,\n") - 1 + vod_get_int_print_len(vod_div_ceil(mpeg_metadata->duration_millis, 1000)) +
		segments_base_url->len + conf->segment_file_name_prefix.len + 1 + vod_get_int_print_len(segment_durations.segment_count) + required_tracks.len + sizeof(".ts\n") - 1;

	result_size =
		sizeof(M3U8_HEADER_PART1) + VOD_INT64_LEN + 
		sizeof(M3U8_HEADER_PART2) + VOD_INT64_LEN + 
		segment_length * segment_durations.segment_count +
		sizeof(m3u8_footer);

	if (encryption_params->type != HLS_ENC_NONE)
	{
		result_size +=
			sizeof(encryption_key_tag_part1) - 1 +
			sizeof(encryption_type_sample_aes) - 1 + 
			sizeof(encryption_key_tag_part2) - 1 +
			base_url->len +
			conf->encryption_key_file_name.len + 
			sizeof("-f") - 1 + VOD_INT32_LEN + 
			sizeof(encryption_key_tag_part3) - 1;
	}

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_index_playlist: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	// write the header
	p = vod_sprintf(
		result->data,
		M3U8_HEADER_PART1,
		(segmenter_conf->max_segment_duration + 500) / 1000);
	
	if (encryption_params->type != HLS_ENC_NONE)
	{
		p = vod_copy(p, encryption_key_tag_part1, sizeof(encryption_key_tag_part1) - 1);
		switch (encryption_params->type)
		{
		case HLS_ENC_SAMPLE_AES:
			p = vod_copy(p, encryption_type_sample_aes, sizeof(encryption_type_sample_aes) - 1);
			break;

		default:		// HLS_ENC_AES_128
			p = vod_copy(p, encryption_type_aes_128, sizeof(encryption_type_aes_128) - 1);
			break;
		}
		p = vod_copy(p, encryption_key_tag_part2, sizeof(encryption_key_tag_part2) - 1);
		p = vod_copy(p, base_url->data, base_url->len);
		p = vod_copy(p, conf->encryption_key_file_name.data, conf->encryption_key_file_name.len);
		if (include_file_index)
		{
			p = vod_sprintf(p, "-f%uD", mpeg_metadata->first_stream->file_info.file_index + 1);
		}
		p = vod_copy(p, encryption_key_tag_part3, sizeof(encryption_key_tag_part3) - 1);
	}

	p = vod_sprintf(
		p,
		M3U8_HEADER_PART2,
		conf->m3u8_version);

	// write the segments
	scale = conf->m3u8_version >= 3 ? 1000 : 1;
	last_item = segment_durations.items + segment_durations.item_count;

	for (cur_item = segment_durations.items; cur_item < last_item; cur_item++)
	{
		segment_index = cur_item->segment_index;
		last_segment_index = segment_index + cur_item->repeat_count;

		// write the first segment
		extinf.data = p;
		p = m3u8_builder_append_extinf_tag(p, rescale_time(cur_item->duration, segment_durations.timescale, scale), scale);
		extinf.len = p - extinf.data;
		p = m3u8_builder_append_segment_name(p, segments_base_url, &conf->segment_file_name_prefix, segment_index, &required_tracks);
		segment_index++;

		// write any additional segments
		for (; segment_index < last_segment_index; segment_index++)
		{
			p = vod_copy(p, extinf.data, extinf.len);
			p = m3u8_builder_append_segment_name(p, segments_base_url, &conf->segment_file_name_prefix, segment_index, &required_tracks);
		}
	}

	// write the footer
	p = vod_copy(p, m3u8_footer, sizeof(m3u8_footer) - 1);

	result->len = p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_index_playlist: result length %uz exceeded allocated length %uz", 
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	
	return VOD_OK;
}
コード例 #6
0
vod_status_t
m3u8_builder_build_master_playlist(
	request_context_t* request_context,
	m3u8_config_t* conf,
	vod_str_t* base_url,
	bool_t include_file_index,
	mpeg_metadata_t* mpeg_metadata,
	vod_str_t* result)
{
	WALK_STREAMS_BY_FILES_VARS(cur_file_streams);
	mpeg_stream_metadata_t* stream;
	media_info_t* video;
	media_info_t* audio = NULL;
	uint32_t bitrate;
	u_char* p;
	size_t max_video_stream_inf;
	size_t max_audio_stream_inf;
	size_t result_size;

	// calculate the result size
	max_video_stream_inf = 
		sizeof(m3u8_stream_inf_video) - 1 + 3 * VOD_INT32_LEN + MAX_CODEC_NAME_SIZE +
		MAX_CODEC_NAME_SIZE + 1 +
		sizeof(m3u8_stream_inf_suffix) - 1;
	max_audio_stream_inf = 
		sizeof(m3u8_stream_inf_audio) + VOD_INT32_LEN + MAX_CODEC_NAME_SIZE +
		sizeof(m3u8_stream_inf_suffix) - 1;
	result_size = 
		sizeof(m3u8_header) + 
		mpeg_metadata->stream_count[MEDIA_TYPE_VIDEO] * max_video_stream_inf +
		mpeg_metadata->stream_count[MEDIA_TYPE_AUDIO] * max_audio_stream_inf;

	WALK_STREAMS_BY_FILES_START(cur_file_streams, mpeg_metadata)

		stream = (cur_file_streams[MEDIA_TYPE_VIDEO] != NULL ?
			cur_file_streams[MEDIA_TYPE_VIDEO] :
			cur_file_streams[MEDIA_TYPE_AUDIO]);
		if (base_url->len != 0)
		{
			result_size += base_url->len;
			result_size += stream->file_info.uri.len + 1;
		}
		result_size += conf->index_file_name_prefix.len;
		result_size += sizeof("-f-v-a") - 1 + VOD_INT32_LEN * 3;
		result_size += sizeof(m3u8_url_suffix) - 1;
	
	WALK_STREAMS_BY_FILES_END(cur_file_streams, mpeg_metadata)

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_master_playlist: vod_alloc failed (2)");
		return VOD_ALLOC_FAILED;
	}

	// write the header
	p = vod_copy(result->data, m3u8_header, sizeof(m3u8_header) - 1);

	// write the streams
	WALK_STREAMS_BY_FILES_START(cur_file_streams, mpeg_metadata)

		// write the stream information
		if (cur_file_streams[MEDIA_TYPE_VIDEO] != NULL)
		{
			stream = cur_file_streams[MEDIA_TYPE_VIDEO];
			video = &stream->media_info;
			bitrate = video->bitrate;
			if (cur_file_streams[MEDIA_TYPE_AUDIO] != NULL)
			{
				audio = &cur_file_streams[MEDIA_TYPE_AUDIO]->media_info;
				bitrate += audio->bitrate;
			}
			p = vod_sprintf(p, m3u8_stream_inf_video, 
				bitrate, 
				(uint32_t)video->u.video.width,
				(uint32_t)video->u.video.height,
				&video->codec_name);
			if (cur_file_streams[MEDIA_TYPE_AUDIO] != NULL)
			{
				*p++ = ',';
				p = vod_copy(p, audio->codec_name.data, audio->codec_name.len);
			}
		}
		else
		{
			stream = cur_file_streams[MEDIA_TYPE_AUDIO];
			audio = &stream->media_info;
			p = vod_sprintf(p, m3u8_stream_inf_audio, audio->bitrate, &audio->codec_name);
		}
		p = vod_copy(p, m3u8_stream_inf_suffix, sizeof(m3u8_stream_inf_suffix) - 1);

		// write the stream url
		if (base_url->len != 0)
		{
			// absolute url only
			p = vod_copy(p, base_url->data, base_url->len);
			p = vod_copy(p, stream->file_info.uri.data, stream->file_info.uri.len);
			*p++ = '/';
		}

		p = vod_copy(p, conf->index_file_name_prefix.data, conf->index_file_name_prefix.len);
		if (base_url->len == 0 && include_file_index)
		{
			p = vod_sprintf(p, "-f%uD", stream->file_info.file_index + 1);
		}

		if (cur_file_streams[MEDIA_TYPE_VIDEO] != NULL)
		{
			p = vod_sprintf(p, "-v%uD", cur_file_streams[MEDIA_TYPE_VIDEO]->track_index + 1);
		}

		if (cur_file_streams[MEDIA_TYPE_AUDIO] != NULL)
		{
			p = vod_sprintf(p, "-a%uD", cur_file_streams[MEDIA_TYPE_AUDIO]->track_index + 1);
		}

		p = vod_copy(p, m3u8_url_suffix, sizeof(m3u8_url_suffix) - 1);

	WALK_STREAMS_BY_FILES_END(cur_file_streams, mpeg_metadata)

	result->len = p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_master_playlist: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}

	return VOD_OK;
}
コード例 #7
0
vod_status_t
audio_encoder_init(
	request_context_t* request_context,
	audio_encoder_params_t* params,
	vod_array_t* frames_array,
	void** result)
{
	audio_encoder_state_t* state;
	AVCodecContext* encoder;
	int avrc;

	if (!initialized)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_encoder_init: module failed to initialize successfully");
		return VOD_UNEXPECTED;
	}

	state = vod_alloc(request_context->pool, sizeof(*state));
	if (state == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
			"audio_encoder_init: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	// init the encoder
	encoder = avcodec_alloc_context3(encoder_codec);
	if (encoder == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_encoder_init: avcodec_alloc_context3 failed");
		return VOD_ALLOC_FAILED;
	}

	state->encoder = encoder;

	encoder->sample_fmt = AUDIO_ENCODER_INPUT_SAMPLE_FORMAT;
	encoder->time_base.num = 1;
	encoder->time_base.den = params->timescale;
	encoder->sample_rate = params->sample_rate;
	encoder->channel_layout = params->channel_layout;
	encoder->channels = params->channels;
	encoder->bit_rate = params->bitrate;
	encoder->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;		// make the codec generate the extra data

	avrc = avcodec_open2(encoder, encoder_codec, NULL);
	if (avrc < 0)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_encoder_init: avcodec_open2 failed %d", avrc);
		audio_encoder_free(state);
		return VOD_UNEXPECTED;
	}

	state->request_context = request_context;
	state->frames_array = frames_array;

	*result = state;

	return VOD_OK;
}
コード例 #8
0
vod_status_t
m3u8_builder_build_iframe_playlist(
	request_context_t* request_context,
	m3u8_config_t* conf,
	hls_muxer_conf_t* muxer_conf,
	vod_str_t* base_url,
	bool_t include_file_index,
	segmenter_conf_t* segmenter_conf,
	mpeg_metadata_t* mpeg_metadata,
	vod_str_t* result)
{
	hls_encryption_params_t encryption_params;
	write_segment_context_t ctx;
	size_t iframe_length;
	size_t result_size;
	hls_muxer_state_t muxer_state;
	bool_t simulation_supported;
	vod_status_t rc; 
	uint32_t segment_count;

	// iframes list is not supported with encryption, since:
	// 1. AES-128 - the IV of each key frame is not known in advance
	// 2. SAMPLE-AES - the layout of the TS files is not known in advance due to emulation prevention
	encryption_params.type = HLS_ENC_NONE;
	encryption_params.key = NULL;
	encryption_params.iv = NULL;

	// initialize the muxer
	rc = hls_muxer_init(
		&muxer_state, 
		request_context, 
		muxer_conf, 
		&encryption_params, 
		0, 
		mpeg_metadata, 
		NULL, 
		NULL, 
		NULL, 
		&simulation_supported);
	if (rc != VOD_OK)
	{
		return rc;
	}

	if (!simulation_supported)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_iframe_playlist: simulation not supported for this file, cant create iframe playlist");
		return VOD_BAD_REQUEST;
	}

	// build the required tracks string
	rc = m3u8_builder_build_required_tracks_string(
		request_context, 
		include_file_index,
		mpeg_metadata, 
		&ctx.required_tracks);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// calculate the required buffer length
	segment_count = segmenter_conf->get_segment_count(segmenter_conf, mpeg_metadata->duration_millis);
	if (segment_count == INVALID_SEGMENT_COUNT)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_iframe_playlist: segment count is invalid");
		return VOD_BAD_DATA;
	}

	iframe_length = sizeof("#EXTINF:.000,\n") - 1 + vod_get_int_print_len(vod_div_ceil(mpeg_metadata->duration_millis, 1000)) +
		sizeof(byte_range_tag_format) + VOD_INT32_LEN + vod_get_int_print_len(MAX_FRAME_SIZE) - (sizeof("%uD%uD") - 1) +
		base_url->len + conf->segment_file_name_prefix.len + 1 + vod_get_int_print_len(segment_count) + ctx.required_tracks.len + sizeof(".ts\n") - 1;

	result_size =
		conf->iframes_m3u8_header_len +
		iframe_length * mpeg_metadata->video_key_frame_count +
		sizeof(m3u8_footer);

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_iframe_playlist: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	// fill out the buffer
	ctx.p = vod_copy(result->data, conf->iframes_m3u8_header, conf->iframes_m3u8_header_len);

	if (mpeg_metadata->video_key_frame_count > 0)
	{
		ctx.base_url = base_url;
		ctx.segment_file_name_prefix = &conf->segment_file_name_prefix;
	
		rc = hls_muxer_simulate_get_iframes(&muxer_state, segmenter_conf, mpeg_metadata, m3u8_builder_append_iframe_string, &ctx);
		if (rc != VOD_OK)
		{
			return rc;
		}
	}

	ctx.p = vod_copy(ctx.p, m3u8_footer, sizeof(m3u8_footer) - 1);
	result->len = ctx.p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_iframe_playlist: result length %uz exceeded allocated length %uz", 
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	
	return VOD_OK;
}
コード例 #9
0
ファイル: m3u8_builder.c プロジェクト: cuiwm/nginx-vod-module
vod_status_t
m3u8_builder_build_master_playlist(
	request_context_t* request_context,
	m3u8_config_t* conf,
	vod_str_t* base_url,
	media_set_t* media_set,
	vod_str_t* result)
{
	media_sequence_t* cur_sequence;	
	media_info_t* video;
	media_info_t* audio = NULL;
	uint32_t sequence_index;
	uint32_t bitrate;
	u_char* p;
	size_t max_video_stream_inf;
	size_t result_size;
	uint32_t main_media_type;
	media_track_t* cur_track;
	media_track_t* audio_track;

	// calculate the result size
	max_video_stream_inf =
		sizeof(m3u8_stream_inf_video) - 1 + 3 * VOD_INT32_LEN + MAX_CODEC_NAME_SIZE +
		MAX_CODEC_NAME_SIZE + 1 +
		sizeof(m3u8_stream_inf_suffix) - 1;
	result_size = sizeof(m3u8_header);	
	for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
	{
		main_media_type = cur_sequence->track_count[MEDIA_TYPE_VIDEO] != 0 ? MEDIA_TYPE_VIDEO : MEDIA_TYPE_AUDIO;
		for (cur_track = cur_sequence->filtered_clips[0].first_track; cur_track < cur_sequence->filtered_clips[0].last_track; cur_track++)
		{			
			if (cur_track->media_info.media_type != main_media_type)
			{
				continue;
			}
			if (base_url->len != 0)
			{
				result_size += base_url->len + 1;
				if (cur_track->file_info.uri.len > 0)
				{
					result_size += cur_track->file_info.uri.len;
				}
				else
				{
					result_size += media_set->uri.len;
				}
			}
			result_size += max_video_stream_inf; // using only video since it's larger than audio
			result_size += conf->index_file_name_prefix.len;
			result_size += sizeof("-f-v-a") - 1 + VOD_INT32_LEN * 3;
			result_size += sizeof(m3u8_url_suffix) - 1;
		}
	}

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_master_playlist: vod_alloc failed (2)");
		return VOD_ALLOC_FAILED;
	}

	// write the header

	p = vod_copy(result->data, m3u8_header, sizeof(m3u8_header) - 1);
	// write the streams
	for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
	{
		audio_track = cur_sequence->filtered_clips[0].longest_track[MEDIA_TYPE_AUDIO];
		main_media_type = cur_sequence->track_count[MEDIA_TYPE_VIDEO] != 0 ? MEDIA_TYPE_VIDEO : MEDIA_TYPE_AUDIO;
		for (cur_track = cur_sequence->filtered_clips[0].first_track; cur_track < cur_sequence->filtered_clips[0].last_track; cur_track++)
		{
			if (cur_track->media_info.media_type != main_media_type)
			{
				continue;
			}
			// write the track information
			if (main_media_type == MEDIA_TYPE_VIDEO)
			{
				video = &cur_track->media_info;
				bitrate = video->bitrate;
				if (audio_track != NULL)
				{
					audio = &audio_track->media_info;
					bitrate += audio->bitrate;
				}
				p = vod_sprintf(p, m3u8_stream_inf_video,
						bitrate,
						(uint32_t)video->u.video.width,
						(uint32_t)video->u.video.height,
						&video->codec_name);
				if (audio_track != NULL)
				{
					*p++ = ',';
					p = vod_copy(p, audio->codec_name.data, audio->codec_name.len);
				}
			}
			else
			{
				audio = &cur_track->media_info;
				p = vod_sprintf(p, m3u8_stream_inf_audio, audio->bitrate, &audio->codec_name);
			}
			p = vod_copy(p, m3u8_stream_inf_suffix, sizeof(m3u8_stream_inf_suffix) - 1);
			// write the track url
			sequence_index = cur_sequence->index;
			if (base_url->len != 0)
			{
				// absolute url only
				p = vod_copy(p, base_url->data, base_url->len);
				if (cur_track->file_info.uri.len != 0)
				{
					p = vod_copy(p, cur_track->file_info.uri.data, cur_track->file_info.uri.len);
					sequence_index = INVALID_SEQUENCE_INDEX;		// no need to pass the sequence index since we have a direct uri
				}
				else
				{
					p = vod_copy(p, media_set->uri.data, media_set->uri.len);
				}
				*p++ = '/';
			}

			p = vod_copy(p, conf->index_file_name_prefix.data, conf->index_file_name_prefix.len);
			if (media_set->has_multi_sequences && sequence_index != INVALID_SEQUENCE_INDEX)
			{
				p = vod_sprintf(p, "-f%uD", cur_sequence->index + 1);
			}

			if (main_media_type == MEDIA_TYPE_VIDEO)
			{
				p = vod_sprintf(p, "-v%uD", cur_track->index + 1);
			}

			if (audio_track != NULL)
			{
				p = vod_sprintf(p, "-a%uD", audio_track->index + 1);
			}

			p = vod_copy(p, m3u8_url_suffix, sizeof(m3u8_url_suffix) - 1);

		}
	}

	result->len = p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_master_playlist: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	return VOD_OK;
}
コード例 #10
0
ファイル: m3u8_builder.c プロジェクト: cuiwm/nginx-vod-module
vod_status_t
m3u8_builder_build_index_playlist(
	request_context_t* request_context,
	m3u8_config_t* conf,
	vod_str_t* base_url,
	vod_str_t* segments_base_url,
	request_params_t* request_params,
	hls_encryption_params_t* encryption_params,
	media_set_t* media_set,
	vod_str_t* result)
{
	segment_durations_t segment_durations;
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item;
	segmenter_conf_t* segmenter_conf = media_set->segmenter_conf;
	uint64_t duration_millis;
	uint32_t sequence_index;
	vod_str_t extinf;
	uint32_t segment_index;
	uint32_t last_segment_index;
	vod_str_t tracks_spec;
	uint32_t scale;
	size_t segment_length;
	size_t result_size;
	vod_status_t rc;
	u_char* p;

	sequence_index = media_set->has_multi_sequences ? media_set->sequences[0].index : INVALID_SEQUENCE_INDEX;

	// build the required tracks string
	rc = manifest_utils_build_request_params_string(
		request_context,
		media_set->track_count,
		INVALID_SEGMENT_INDEX,
		sequence_index,
		request_params->tracks_mask,
		&tracks_spec);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// get the segment durations
	rc = segmenter_conf->get_segment_durations(
		request_context,
		segmenter_conf,
		media_set,
		NULL,
		MEDIA_TYPE_NONE,
		&segment_durations);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// get the required buffer length
	duration_millis = segment_durations.end_time - segment_durations.start_time;
	last_segment_index = media_set->initial_segment_index + segment_durations.segment_count;
	segment_length = sizeof("#EXTINF:.000,\n") - 1 + vod_get_int_print_len(vod_div_ceil(duration_millis, 1000)) +
		segments_base_url->len + conf->segment_file_name_prefix.len + 1 + vod_get_int_print_len(last_segment_index) + tracks_spec.len + sizeof(".ts\n") - 1;

	result_size =
		sizeof(M3U8_HEADER_PART1) + VOD_INT64_LEN +
		sizeof(M3U8_HEADER_VOD) +
		sizeof(M3U8_HEADER_PART2) + VOD_INT64_LEN + VOD_INT32_LEN +
		segment_length * segment_durations.segment_count +
		segment_durations.discontinuities * (sizeof(m3u8_discontinuity) - 1) +
		sizeof(m3u8_footer);

	if (encryption_params->type != HLS_ENC_NONE)
	{
		result_size +=
			sizeof(encryption_key_tag_method) - 1 +
			sizeof(encryption_type_sample_aes) - 1 +
			sizeof(encryption_key_tag_uri) - 1 + 
			2;			// '"', '\n'

		if (encryption_params->key_uri.len != 0)
		{
			result_size += encryption_params->key_uri.len;
		}
		else
		{
			result_size += base_url->len +
				conf->encryption_key_file_name.len +
				sizeof("-f") - 1 + VOD_INT32_LEN +
				sizeof(encryption_key_extension) - 1;
		}

		if (conf->encryption_key_format.len != 0)
		{
			result_size +=
				sizeof(encryption_key_tag_key_format) +				// '"'
				conf->encryption_key_format.len;
		}

		if (conf->encryption_key_format_versions.len != 0)
		{
			result_size +=
				sizeof(encryption_key_tag_key_format_versions) +	// '"'
				conf->encryption_key_format_versions.len;
		}
	}

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_index_playlist: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	// write the header
	p = vod_sprintf(
		result->data,
		M3U8_HEADER_PART1,
		(segmenter_conf->max_segment_duration + 500) / 1000);

	if (media_set->type == MEDIA_SET_VOD)
	{
		p = vod_copy(p, M3U8_HEADER_VOD, sizeof(M3U8_HEADER_VOD) - 1);
	}

	if (encryption_params->type != HLS_ENC_NONE)
	{
		p = vod_copy(p, encryption_key_tag_method, sizeof(encryption_key_tag_method) - 1);
		switch (encryption_params->type)
		{
		case HLS_ENC_SAMPLE_AES:
			p = vod_copy(p, encryption_type_sample_aes, sizeof(encryption_type_sample_aes) - 1);
			break;

		default:		// HLS_ENC_AES_128
			p = vod_copy(p, encryption_type_aes_128, sizeof(encryption_type_aes_128) - 1);
			break;
		}

		// uri
		p = vod_copy(p, encryption_key_tag_uri, sizeof(encryption_key_tag_uri) - 1);
		if (encryption_params->key_uri.len != 0)
		{
			p = vod_copy(p, encryption_params->key_uri.data, encryption_params->key_uri.len);
		}
		else
		{
			p = vod_copy(p, base_url->data, base_url->len);
			p = vod_copy(p, conf->encryption_key_file_name.data, conf->encryption_key_file_name.len);
			if (sequence_index != INVALID_SEQUENCE_INDEX)
			{
				p = vod_sprintf(p, "-f%uD", sequence_index + 1);
			}
			p = vod_copy(p, encryption_key_extension, sizeof(encryption_key_extension) - 1);
		}
		*p++ = '"';

		// keyformat
		if (conf->encryption_key_format.len != 0)
		{
			p = vod_copy(p, encryption_key_tag_key_format, sizeof(encryption_key_tag_key_format) - 1);
			p = vod_copy(p, conf->encryption_key_format.data, conf->encryption_key_format.len);
			*p++ = '"';
		}

		// keyformatversions
		if (conf->encryption_key_format_versions.len != 0)
		{
			p = vod_copy(p, encryption_key_tag_key_format_versions, sizeof(encryption_key_tag_key_format_versions) - 1);
			p = vod_copy(p, conf->encryption_key_format_versions.data, conf->encryption_key_format_versions.len);
			*p++ = '"';
		}

		*p++ = '\n';
	}

	p = vod_sprintf(
		p,
		M3U8_HEADER_PART2,
		conf->m3u8_version, 
		media_set->initial_segment_index + 1);

	// write the segments
	scale = conf->m3u8_version >= 3 ? 1000 : 1;
	last_item = segment_durations.items + segment_durations.item_count;

	for (cur_item = segment_durations.items; cur_item < last_item; cur_item++)
	{
		segment_index = cur_item->segment_index;
		last_segment_index = segment_index + cur_item->repeat_count;

		if (cur_item->discontinuity)
		{
			p = vod_copy(p, m3u8_discontinuity, sizeof(m3u8_discontinuity) - 1);
		}

		// write the first segment
		extinf.data = p;
		p = m3u8_builder_append_extinf_tag(p, rescale_time(cur_item->duration, segment_durations.timescale, scale), scale);
		extinf.len = p - extinf.data;
		p = m3u8_builder_append_segment_name(p, segments_base_url, &conf->segment_file_name_prefix, segment_index, &tracks_spec);
		segment_index++;

		// write any additional segments
		for (; segment_index < last_segment_index; segment_index++)
		{
			p = vod_copy(p, extinf.data, extinf.len);
			p = m3u8_builder_append_segment_name(p, segments_base_url, &conf->segment_file_name_prefix, segment_index, &tracks_spec);
		}
	}

	// write the footer
	if (media_set->presentation_end)
	{
		p = vod_copy(p, m3u8_footer, sizeof(m3u8_footer) - 1);
	}

	result->len = p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_index_playlist: result length %uz exceeded allocated length %uz", 
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	
	return VOD_OK;
}
コード例 #11
0
ファイル: m3u8_builder.c プロジェクト: cuiwm/nginx-vod-module
vod_status_t
m3u8_builder_build_iframe_playlist(
	request_context_t* request_context,
	m3u8_config_t* conf,
	hls_muxer_conf_t* muxer_conf,
	vod_str_t* base_url,
	request_params_t* request_params,
	media_set_t* media_set,
	vod_str_t* result)
{
	hls_encryption_params_t encryption_params;
	write_segment_context_t ctx;
	segment_durations_t segment_durations;
	segmenter_conf_t* segmenter_conf = media_set->segmenter_conf;
	size_t iframe_length;
	size_t result_size;
	uint64_t duration_millis;
	uint32_t sequence_index;
	vod_status_t rc; 

	sequence_index = media_set->has_multi_sequences ? media_set->sequences[0].index : INVALID_SEQUENCE_INDEX;

	// iframes list is not supported with encryption, since:
	// 1. AES-128 - the IV of each key frame is not known in advance
	// 2. SAMPLE-AES - the layout of the TS files is not known in advance due to emulation prevention
	encryption_params.type = HLS_ENC_NONE;
	encryption_params.key = NULL;
	encryption_params.iv = NULL;

	// build the required tracks string
	rc = manifest_utils_build_request_params_string(
		request_context, 
		media_set->track_count,
		INVALID_SEGMENT_INDEX,
		sequence_index,
		request_params->tracks_mask,
		&ctx.tracks_spec);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// get segment durations
	if (segmenter_conf->align_to_key_frames)
	{
		rc = segmenter_get_segment_durations_accurate(
			request_context,
			segmenter_conf,
			media_set,
			NULL,
			MEDIA_TYPE_NONE,
			&segment_durations);
	}
	else
	{
		rc = segmenter_get_segment_durations_estimate(
			request_context,
			segmenter_conf,
			media_set,
			NULL,
			MEDIA_TYPE_NONE,
			&segment_durations);
	}

	if (rc != VOD_OK)
	{
		return rc;
	}

	duration_millis = segment_durations.end_time - segment_durations.start_time;
	iframe_length = sizeof("#EXTINF:.000,\n") - 1 + vod_get_int_print_len(vod_div_ceil(duration_millis, 1000)) +
		sizeof(byte_range_tag_format) + VOD_INT32_LEN + vod_get_int_print_len(MAX_FRAME_SIZE) - (sizeof("%uD%uD") - 1) +
		base_url->len + conf->segment_file_name_prefix.len + 1 + vod_get_int_print_len(segment_durations.segment_count) + ctx.tracks_spec.len + sizeof(".ts\n") - 1;

	result_size =
		conf->iframes_m3u8_header_len +
		iframe_length * media_set->sequences[0].video_key_frame_count +
		sizeof(m3u8_footer);

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_iframe_playlist: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	// fill out the buffer
	ctx.p = vod_copy(result->data, conf->iframes_m3u8_header, conf->iframes_m3u8_header_len);

	if (media_set->sequences[0].video_key_frame_count > 0)
	{
		ctx.base_url = base_url;
		ctx.segment_file_name_prefix = &conf->segment_file_name_prefix;
	
		rc = hls_muxer_simulate_get_iframes(
			request_context,
			&segment_durations, 
			muxer_conf,
			&encryption_params,
			media_set, 
			m3u8_builder_append_iframe_string, 
			&ctx);
		if (rc != VOD_OK)
		{
			return rc;
		}
	}

	ctx.p = vod_copy(ctx.p, m3u8_footer, sizeof(m3u8_footer) - 1);
	result->len = ctx.p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_iframe_playlist: result length %uz exceeded allocated length %uz", 
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	
	return VOD_OK;
}
コード例 #12
0
vod_status_t
mp4_encrypt_video_get_fragment_writer(
	segment_writer_t* result,
	request_context_t* request_context,
	media_set_t* media_set,
	uint32_t segment_index,
	bool_t single_nalu_per_frame,
	mp4_encrypt_video_build_fragment_header_t build_fragment_header,
	segment_writer_t* segment_writer,
	const u_char* iv, 
	vod_str_t* fragment_header, 
	size_t* total_fragment_size)
{
	media_sequence_t* sequence = &media_set->sequences[0];
	mp4_encrypt_video_state_t* state;
	vod_status_t rc;
	uint32_t initial_size;
	bool_t ignore;

	// allocate the state
	state = vod_alloc(request_context->pool, sizeof(*state));
	if (state == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"mp4_encrypt_video_get_fragment_writer: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	rc = mp4_encrypt_init_state(&state->base, request_context, media_set, segment_index, segment_writer, iv);
	if (rc != VOD_OK)
	{
		vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"mp4_encrypt_video_get_fragment_writer: mp4_encrypt_init_state failed %i", rc);
		return rc;
	}

	if (!mp4_encrypt_move_to_next_frame(&state->base, &ignore))
	{
		// an empty segment - write won't be called so we need to write the header here
		state->auxiliary_data.start = NULL;
		state->auxiliary_data.pos = NULL;
		state->auxiliary_sample_sizes = NULL;
		state->auxiliary_sample_sizes_pos = NULL;
		state->default_auxiliary_sample_size = 0;
		state->saiz_sample_count = 0;

		rc = build_fragment_header(state, fragment_header, total_fragment_size);
		if (rc != VOD_OK)
		{
			vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"mp4_encrypt_video_get_fragment_writer: write_fragment_header failed %i", rc);
			return rc;
		}

		return VOD_OK;
	}
	
	if (single_nalu_per_frame)
	{
		// each frame is a single nal unit, can generate the auxiliary data and write the header now
		state->build_fragment_header = NULL;
		state->single_nalu_warning_printed = FALSE;

		rc = mp4_encrypt_video_snpf_build_auxiliary_data(state);
		if (rc != VOD_OK)
		{
			return rc;
		}

		rc = build_fragment_header(state, fragment_header, total_fragment_size);
		if (rc != VOD_OK)
		{
			vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"mp4_encrypt_video_get_fragment_writer: write_fragment_header failed %i", rc);
			return rc;
		}

		result->write_tail = mp4_encrypt_video_snpf_write_buffer;
	}
	else
	{
		state->build_fragment_header = build_fragment_header;

		// for progressive AVC a frame usually contains a single nalu, except the first frame which may contain codec copyright info
		initial_size =
			(sizeof(cenc_sample_auxiliary_data_t) + sizeof(cenc_sample_auxiliary_data_subsample_t)) * sequence->total_frame_count +
			sizeof(cenc_sample_auxiliary_data_subsample_t);
		rc = vod_dynamic_buf_init(&state->auxiliary_data, request_context, initial_size);
		if (rc != VOD_OK)
		{
			vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"mp4_encrypt_video_get_fragment_writer: vod_dynamic_buf_init failed %i", rc);
			return rc;
		}

		state->auxiliary_sample_sizes = vod_alloc(request_context->pool, sequence->total_frame_count);
		if (state->auxiliary_sample_sizes == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"mp4_encrypt_video_get_fragment_writer: vod_alloc failed (2)");
			return VOD_ALLOC_FAILED;
		}
		state->auxiliary_sample_sizes_pos = state->auxiliary_sample_sizes;

		result->write_tail = mp4_encrypt_video_write_buffer;
	}

	// init writing for the first track
	rc = mp4_encrypt_video_init_track(state, state->base.cur_clip->first_track);
	if (rc != VOD_OK)
	{
		return rc;
	}

	result->write_head = NULL;
	result->context = state;

	return VOD_OK;
}
コード例 #13
0
vod_status_t
edash_packager_build_mpd(
	request_context_t* request_context,
	dash_manifest_config_t* conf,
	vod_str_t* base_url,
	media_set_t* media_set,
	bool_t drm_single_key,
	vod_str_t* result)
{
	write_content_protection_context_t context;
	dash_manifest_extensions_t extensions;
	media_sequence_t* cur_sequence;
	drm_system_info_t* cur_info;
	tags_writer_t content_prot_writer;
	drm_info_t* drm_info;
	size_t representation_tags_size;
	size_t cur_drm_tags_size;
	size_t cur_pssh_size;
	size_t max_pssh_size = 0;
	vod_status_t rc;

	representation_tags_size = 0;

	for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
	{
		drm_info = (drm_info_t*)cur_sequence->drm_info;

		cur_drm_tags_size = sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC) - 1;

		for (cur_info = drm_info->pssh_array.first; cur_info < drm_info->pssh_array.last; cur_info++)
		{
			if (vod_memcmp(cur_info->system_id, edash_playready_system_id, sizeof(edash_playready_system_id)) == 0)
			{
				cur_drm_tags_size +=
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_V2_PART1) - 1 +
					VOD_GUID_LENGTH +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_V2_PART2) - 1 +
					VOD_GUID_LENGTH +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_PART3) - 1 +
					vod_base64_encoded_length(cur_info->data.len) +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_PART4) - 1;
			}
			else
			{
				cur_pssh_size = ATOM_HEADER_SIZE + sizeof(pssh_atom_t) + cur_info->data.len;
				if (cur_pssh_size > max_pssh_size)
				{
					max_pssh_size = cur_pssh_size;
				}

				cur_drm_tags_size +=
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART1) - 1 +
					VOD_GUID_LENGTH +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART2) - 1 +
					VOD_GUID_LENGTH +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART3) - 1 +
					vod_base64_encoded_length(cur_pssh_size) +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART4) - 1;

				continue;
			}
		}

		representation_tags_size += cur_drm_tags_size * cur_sequence->total_track_count;
	}

	context.write_playready_kid = conf->write_playready_kid;
	if (max_pssh_size > 0)
	{
		context.temp_buffer = vod_alloc(request_context->pool, max_pssh_size);
		if (context.temp_buffer == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"edash_packager_build_mpd: vod_alloc failed");
			return VOD_ALLOC_FAILED;
		}
	}

	content_prot_writer.size = representation_tags_size;
	content_prot_writer.write = edash_packager_write_content_protection;
	content_prot_writer.context = &context;

	if (drm_single_key)
	{
		// write the ContentProtection tags under AdaptationSet
		extensions.adaptation_set = content_prot_writer;
		vod_memzero(&extensions.representation, sizeof(extensions.representation));
	}
	else
	{
		// write the ContentProtection tags under Representation
		vod_memzero(&extensions.adaptation_set, sizeof(extensions.adaptation_set));
		extensions.representation = content_prot_writer;
	}

	rc = dash_packager_build_mpd(
		request_context,
		conf,
		base_url,
		media_set,
		&extensions,
		result);
	if (rc != VOD_OK)
	{
		vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"edash_packager_build_mpd: dash_packager_build_mpd failed %i", rc);
		return rc;
	}

	return VOD_OK;
}