static u_char*
dash_packager_write_segment_list(
	u_char* p,
	write_period_context_t* context,
	uint32_t start_number,
	u_char* clip_spec,
	media_sequence_t* cur_sequence,
	media_track_t* cur_track,
	uint32_t segment_count)
{
	dash_manifest_config_t* conf = context->conf;
	media_set_t* media_set = context->media_set;
	vod_str_t track_spec;
	vod_str_t cur_base_url;
	u_char track_spec_buffer[MAX_TRACK_SPEC_LENGTH];
	uint32_t sequence_index = cur_sequence->index;
	uint32_t i;

	track_spec.data = track_spec_buffer;

	// build the base url
	dash_packager_get_segment_list_base_url(context, cur_track, &cur_base_url, &sequence_index);

	// get the track specification
	dash_packager_get_track_spec(
		&track_spec,
		media_set,
		sequence_index,
		cur_track->index,
		cur_track->media_info.media_type);

	// write the header
	p = vod_sprintf(p,
		VOD_DASH_MANIFEST_SEGMENT_LIST_HEADER,
		media_set->segmenter_conf->segment_duration,
		context->clip_index == 0 ? media_set->initial_segment_clip_relative_index + 1 : 1,
		&cur_base_url,
		&conf->init_file_name_prefix,
		clip_spec,
		&track_spec, 
		&dash_codecs[cur_track->media_info.codec_id].init_file_ext);

	// write the urls
	for (i = 0; i < segment_count; i++)
	{
		p = vod_sprintf(p,
			VOD_DASH_MANIFEST_SEGMENT_URL,
			&cur_base_url,
			&conf->fragment_file_name_prefix,
			start_number + i + 1,
			&track_spec,
			&dash_codecs[cur_track->media_info.codec_id].frag_file_ext);
	}

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

	return p;
}
Exemple #2
0
static u_char* 
dash_packager_write_mpd_period(
	u_char* p,
	u_char* base_url_temp_buffer,
	segment_durations_t* segment_durations,
	segment_duration_item_t** cur_duration_items,
	uint32_t clip_index,
	dash_manifest_config_t* conf,
	vod_str_t* base_url,
	segmenter_conf_t* segmenter_conf,
	media_set_t* media_set,
	write_tags_callback_t write_representation_tags,
	void* representation_tags_writer_context)
{
	media_clip_filtered_t* cur_clip;
	media_sequence_t* cur_sequence;
	media_track_t* last_track;
	media_track_t* cur_track;
	vod_str_t representation_id;
	u_char representation_id_buffer[MAX_TRACK_SPEC_LENGTH];
	uint32_t filtered_clip_index;
	uint32_t max_width = 0;
	uint32_t max_height = 0;
	uint32_t max_framerate_duration = 0;
	uint32_t max_framerate_timescale = 0;
	uint32_t segment_count = 0;

	representation_id.data = representation_id_buffer;

	if (media_set->use_discontinuity)
	{
		p = vod_sprintf(p,
			VOD_DASH_MANIFEST_PERIOD_DURATION_HEADER,
			clip_index,
			media_set->durations[clip_index] / 1000,
			media_set->durations[clip_index] % 1000);
	}
	else
	{
		p = vod_copy(p, VOD_DASH_MANIFEST_PERIOD_HEADER, sizeof(VOD_DASH_MANIFEST_PERIOD_HEADER) - 1);
	}

	// Note: clip_index can be greater than clip count when consistentSequenceMediaInfo is true
	filtered_clip_index = clip_index < media_set->clip_count ? clip_index : 0;

	// video adaptation set
	if (media_set->track_count[MEDIA_TYPE_VIDEO] != 0)
	{
		// get the max width, height and frame rate
		cur_track = media_set->filtered_tracks + filtered_clip_index * media_set->total_track_count;
		last_track = cur_track + media_set->total_track_count;
		for (; cur_track < last_track; cur_track++)
		{
			if (cur_track->media_info.media_type != MEDIA_TYPE_VIDEO)
			{
				continue;
			}

			if (cur_track->media_info.u.video.width > max_width)
			{
				max_width = cur_track->media_info.u.video.width;
			}

			if (cur_track->media_info.u.video.height > max_height)
			{
				max_height = cur_track->media_info.u.video.height;
			}

			if (max_framerate_duration == 0 ||
				cur_track->media_info.timescale * max_framerate_duration >
				max_framerate_timescale * cur_track->media_info.min_frame_duration)
			{
				max_framerate_duration = cur_track->media_info.min_frame_duration;
				max_framerate_timescale = cur_track->media_info.timescale;
			}
		}

		// print the header
		p = vod_sprintf(p,
			VOD_DASH_MANIFEST_VIDEO_HEADER,
			max_width,
			max_height,
			(uint32_t)max_framerate_timescale / max_framerate_duration,
			(uint32_t)(((uint64_t)max_framerate_timescale * 1000) / max_framerate_duration % 1000));

		// print the segment template
		switch (conf->manifest_format)
		{
		case FORMAT_SEGMENT_TEMPLATE:
			p = dash_packager_write_segment_template(
				p,
				conf,
				segmenter_conf,
				base_url);
			break;

		case FORMAT_SEGMENT_TIMELINE:
			p = dash_packager_write_segment_timeline(
				p,
				conf,
				&segment_durations[MEDIA_TYPE_VIDEO],
				&cur_duration_items[MEDIA_TYPE_VIDEO],
				base_url);
			break;

		case FORMAT_SEGMENT_LIST:
			if (media_set->use_discontinuity)
			{
				segment_count = dash_packager_get_cur_clip_segment_count(
					&segment_durations[MEDIA_TYPE_VIDEO],
					&cur_duration_items[MEDIA_TYPE_VIDEO]);
			}
			else
			{
				segment_count = segment_durations[MEDIA_TYPE_VIDEO].segment_count;
			}
			break;
		}

		// print the representations
		for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
		{
			cur_clip = &cur_sequence->filtered_clips[filtered_clip_index];
			last_track = cur_clip->last_track;
			for (cur_track = cur_clip->first_track; cur_track < last_track; cur_track++)
			{
				if (cur_track->media_info.media_type != MEDIA_TYPE_VIDEO)
				{
					continue;
				}

				dash_packager_get_track_spec(
					&representation_id, media_set, clip_index, cur_sequence->index, cur_track->index, 'v');

				p = vod_sprintf(p,
					VOD_DASH_MANIFEST_VIDEO_PREFIX,
					&representation_id,
					&cur_track->media_info.codec_name,
					(uint32_t)cur_track->media_info.u.video.width,
					(uint32_t)cur_track->media_info.u.video.height,
					(uint32_t)(cur_track->media_info.timescale / cur_track->media_info.min_frame_duration),
					(uint32_t)(((uint64_t)cur_track->media_info.timescale * 1000) / cur_track->media_info.min_frame_duration % 1000),
					cur_track->media_info.bitrate
					);

				if (conf->manifest_format == FORMAT_SEGMENT_LIST)
				{
					p = dash_packager_write_segment_list(
						p,
						conf,
						media_set,
						segmenter_conf,
						base_url,
						base_url_temp_buffer,
						clip_index,
						cur_sequence,
						cur_track,
						segment_count);
				}

				// write any additional tags
				if (write_representation_tags != NULL)
				{
					p = write_representation_tags(representation_tags_writer_context, p, cur_track);
				}

				p = vod_copy(p, VOD_DASH_MANIFEST_VIDEO_SUFFIX, sizeof(VOD_DASH_MANIFEST_VIDEO_SUFFIX) - 1);
			}
		}

		// print the footer
		p = vod_copy(p, VOD_DASH_MANIFEST_VIDEO_FOOTER, sizeof(VOD_DASH_MANIFEST_VIDEO_FOOTER) - 1);
	}

	// audio adaptation set
	if (media_set->track_count[MEDIA_TYPE_AUDIO] != 0)
	{
		// print the header
		p = vod_copy(p, VOD_DASH_MANIFEST_AUDIO_HEADER, sizeof(VOD_DASH_MANIFEST_AUDIO_HEADER) - 1);

		// print the segment template
		switch (conf->manifest_format)
		{
		case FORMAT_SEGMENT_TEMPLATE:
			p = dash_packager_write_segment_template(
				p,
				conf,
				segmenter_conf,
				base_url);
			break;

		case FORMAT_SEGMENT_TIMELINE:
			p = dash_packager_write_segment_timeline(
				p,
				conf,
				&segment_durations[MEDIA_TYPE_AUDIO],
				&cur_duration_items[MEDIA_TYPE_AUDIO],
				base_url);
			break;

		case FORMAT_SEGMENT_LIST:
			if (media_set->use_discontinuity)
			{
				segment_count = dash_packager_get_cur_clip_segment_count(
					&segment_durations[MEDIA_TYPE_AUDIO],
					&cur_duration_items[MEDIA_TYPE_AUDIO]);
			}
			else
			{
				segment_count = segment_durations[MEDIA_TYPE_AUDIO].segment_count;
			}
			break;
		}
		// print the representations
		for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
		{
			cur_clip = &cur_sequence->filtered_clips[filtered_clip_index];
			last_track = cur_clip->last_track;
			for (cur_track = cur_clip->first_track; cur_track < last_track; cur_track++)
			{
				if (cur_track->media_info.media_type != MEDIA_TYPE_AUDIO)
				{
					continue;
				}

				dash_packager_get_track_spec(
					&representation_id, media_set, clip_index, cur_sequence->index, cur_track->index, 'a');

				p = vod_sprintf(p,
					VOD_DASH_MANIFEST_AUDIO_PREFIX,
					&representation_id,
					&cur_track->media_info.codec_name,
					cur_track->media_info.u.audio.sample_rate,
					cur_track->media_info.bitrate);

				if (conf->manifest_format == FORMAT_SEGMENT_LIST)
				{
					p = dash_packager_write_segment_list(
						p,
						conf,
						media_set,
						segmenter_conf,
						base_url,
						base_url_temp_buffer,
						clip_index,
						cur_sequence,
						cur_track,
						segment_count);
				}

				// write any additional tags
				if (write_representation_tags != NULL)
				{
					p = write_representation_tags(representation_tags_writer_context, p, cur_track);
				}

				p = vod_copy(p, VOD_DASH_MANIFEST_AUDIO_SUFFIX, sizeof(VOD_DASH_MANIFEST_AUDIO_SUFFIX) - 1);
			}
		}

		// print the footer
		p = vod_copy(p, VOD_DASH_MANIFEST_AUDIO_FOOTER, sizeof(VOD_DASH_MANIFEST_AUDIO_FOOTER) - 1);
	}

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

	return p;
}
Exemple #3
0
static u_char*
dash_packager_write_segment_list(
	u_char* p,
	dash_manifest_config_t* conf,
	media_set_t* media_set,
	segmenter_conf_t* segmenter_conf,
	vod_str_t* base_url,
	u_char* base_url_temp_buffer,
	uint32_t clip_index,
	media_sequence_t* cur_sequence,
	media_track_t* cur_track,
	uint32_t segment_count)
{
	int media_type_char = cur_track->media_info.media_type == MEDIA_TYPE_VIDEO ? 'v' : 'a';
	vod_str_t track_spec;
	vod_str_t cur_base_url;
	u_char track_spec_buffer[MAX_TRACK_SPEC_LENGTH];
	uint32_t sequence_index = cur_sequence->index;
	uint32_t i;

	track_spec.data = track_spec_buffer;

	// build the base url
	if (base_url->len != 0)
	{
		cur_base_url.data = base_url_temp_buffer;
		base_url_temp_buffer = vod_copy(base_url_temp_buffer, base_url->data, base_url->len);
		if (cur_track->file_info.uri.len != 0)
		{
			base_url_temp_buffer = vod_copy(base_url_temp_buffer, 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
		{
			base_url_temp_buffer = vod_copy(base_url_temp_buffer, media_set->uri.data, media_set->uri.len);
		}
		*base_url_temp_buffer++ = '/';
		cur_base_url.len = base_url_temp_buffer - cur_base_url.data;
	}
	else
	{
		cur_base_url.data = NULL;
		cur_base_url.len = 0;
	}

	// get the track specification
	dash_packager_get_track_spec(
		&track_spec,
		media_set,
		clip_index,
		sequence_index,
		cur_track->index,
		media_type_char);

	// write the header
	p = vod_sprintf(p,
		VOD_DASH_MANIFEST_SEGMENT_LIST_HEADER,
		segmenter_conf->segment_duration,
		&cur_base_url,
		&conf->init_file_name_prefix,
		&track_spec);

	// write the urls
	for (i = 0; i < segment_count; i++)
	{
		p = vod_sprintf(p,
			VOD_DASH_MANIFEST_SEGMENT_URL,
			&cur_base_url,
			&conf->fragment_file_name_prefix,
			i + 1,
			&track_spec);
	}

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

	return p;
}
static u_char* 
dash_packager_write_mpd_period(
	u_char* p,
	write_period_context_t* context)
{
	segment_duration_item_t** cur_duration_items;
	media_sequence_t* cur_sequence;
	adaptation_set_t* adaptation_set;
	media_track_t* reference_track = NULL;
	media_track_t** cur_track_ptr;
	media_track_t* cur_track;
	media_set_t* media_set = context->media_set;
	const char* lang_code;
	vod_str_t representation_id;
	vod_str_t cur_base_url;
	vod_str_t frame_rate;
	u_char representation_id_buffer[MAX_TRACK_SPEC_LENGTH];
	u_char frame_rate_buffer[VOD_DASH_MAX_FRAME_RATE_LEN];
	u_char clip_spec[MAX_CLIP_SPEC_LENGTH];
	uint64_t clip_start_offset;
	uint32_t clip_duration;
	uint32_t filtered_clip_offset;
	uint32_t max_width = 0;
	uint32_t max_height = 0;
	uint32_t max_framerate_duration = 0;
	uint32_t segment_count = 0;
	uint32_t start_number;
	uint32_t media_type;
	uint32_t adapt_id = 1;
	uint32_t subtitle_adapt_id = 0;
	uint32_t sequence_index;

	frame_rate.data = frame_rate_buffer;
	representation_id.data = representation_id_buffer;

	if (media_set->use_discontinuity)
	{
		clip_duration = media_set->timing.durations[context->clip_index];
		switch (media_set->type)
		{
		case MEDIA_SET_VOD:
			p = vod_sprintf(p,
				VOD_DASH_MANIFEST_PERIOD_HEADER_DURATION,
				media_set->initial_clip_index + context->clip_index,
				clip_duration / 1000,
				clip_duration % 1000);
			break;

		case MEDIA_SET_LIVE:
			clip_start_offset = context->clip_start_time - context->segment_base_time;

			if (context->clip_index + 1 < media_set->timing.total_count &&
				media_set->timing.times[context->clip_index] + clip_duration !=
				media_set->timing.times[context->clip_index + 1])
			{
				// there is a gap after this clip, output start time and duration
				clip_duration += media_set->timing.times[context->clip_index] - context->clip_start_time;

				p = vod_sprintf(p,
					VOD_DASH_MANIFEST_PERIOD_HEADER_START_DURATION,
					media_set->initial_clip_index + context->clip_index,
					clip_start_offset / 1000,
					clip_start_offset % 1000,
					clip_duration / 1000,
					clip_duration % 1000);
			}
			else
			{
				// last clip / no gap, output only the start time
				p = vod_sprintf(p,
					VOD_DASH_MANIFEST_PERIOD_HEADER_START,
					media_set->initial_clip_index + context->clip_index,
					clip_start_offset / 1000,
					clip_start_offset % 1000);
			}
			break;
		}
	}
	else
	{
		switch (media_set->type)
		{
		case MEDIA_SET_VOD:
			p = vod_copy(p, VOD_DASH_MANIFEST_PERIOD_HEADER, sizeof(VOD_DASH_MANIFEST_PERIOD_HEADER) - 1);
			break;

		case MEDIA_SET_LIVE:
			p = vod_copy(p, VOD_DASH_MANIFEST_PERIOD_HEADER_START_ZERO, sizeof(VOD_DASH_MANIFEST_PERIOD_HEADER_START_ZERO) - 1);
			break;
		}
	}

	// Note: clip_index can be greater than clip count when consistentSequenceMediaInfo is true
	filtered_clip_offset = context->clip_index < media_set->clip_count ? 
		context->clip_index * media_set->total_track_count : 0;

	dash_packager_get_clip_spec(clip_spec, media_set, context->clip_index);

	// print the adaptation sets
	for (adaptation_set = context->adaptation_sets.first, cur_duration_items = context->cur_duration_items;
		adaptation_set < context->adaptation_sets.last;
		adaptation_set++, cur_duration_items++)
	{
		media_type = adaptation_set->type;
		switch (media_type)
		{
		case MEDIA_TYPE_VIDEO:
			// get the max width, height and frame rate
			for (cur_track_ptr = adaptation_set->first;
				cur_track_ptr < adaptation_set->last;
				cur_track_ptr++)
			{
				cur_track = (*cur_track_ptr) + filtered_clip_offset;

				if (cur_track->media_info.u.video.width > max_width)
				{
					max_width = cur_track->media_info.u.video.width;
				}

				if (cur_track->media_info.u.video.height > max_height)
				{
					max_height = cur_track->media_info.u.video.height;
				}

				if (max_framerate_duration == 0 ||
					max_framerate_duration > cur_track->media_info.min_frame_duration)
				{
					max_framerate_duration = cur_track->media_info.min_frame_duration;
				}
			}

			reference_track = adaptation_set->last[-1] + filtered_clip_offset;		// Note: taking the last track only for compatiblity with past versions of this module

			// print the header
			dash_packager_write_frame_rate(
				max_framerate_duration,
				DASH_TIMESCALE,
				&frame_rate);

			p = vod_sprintf(p,
				VOD_DASH_MANIFEST_ADAPTATION_HEADER_VIDEO,
				adapt_id++,
				max_width,
				max_height,
				&frame_rate);
			break;

		case MEDIA_TYPE_AUDIO:
			reference_track = (*adaptation_set->first) + filtered_clip_offset;
			if (context->adaptation_sets.multi_audio)
			{
				p = vod_sprintf(p, VOD_DASH_MANIFEST_ADAPTATION_HEADER_AUDIO_LANG, 
					adapt_id++, 
					lang_get_rfc_5646_name(reference_track->media_info.language),
					&reference_track->media_info.label);
			}
			else
			{
				p = vod_sprintf(p, VOD_DASH_MANIFEST_ADAPTATION_HEADER_AUDIO, 
					adapt_id++);
			}
			break;

		case MEDIA_TYPE_SUBTITLE:
			cur_track = (*adaptation_set->first) + filtered_clip_offset;
			cur_sequence = cur_track->file_info.source->sequence;

			sequence_index = cur_sequence->index;
			if (context->conf->manifest_format == FORMAT_SEGMENT_LIST)
			{
				dash_packager_get_segment_list_base_url(context, cur_track, &cur_base_url, &sequence_index);
			}
			else
			{
				cur_base_url = context->base_url;
			}

			dash_packager_get_track_spec(
				&representation_id,
				media_set,
				sequence_index,
				cur_track->index,
				cur_track->media_info.media_type);

			if (representation_id.len > 0 && representation_id.data[representation_id.len - 1] == '-')
			{
				representation_id.len--;
			}

			lang_code = lang_get_rfc_5646_name(cur_track->media_info.language);
			p = vod_sprintf(p, VOD_DASH_MANIFEST_ADAPTATION_SUBTITLE, 
				lang_code,
				&cur_track->media_info.label,
				lang_code,
				subtitle_adapt_id++, 
				&cur_base_url,
				&context->conf->subtitle_file_name_prefix,
				clip_spec,
				&representation_id);
			continue;
		}

		if (context->extensions.adaptation_set.write != NULL)
		{
			p = context->extensions.adaptation_set.write(
				context->extensions.adaptation_set.context,
				p,
				reference_track);
		}

		// get the segment index start number
		start_number = (*cur_duration_items)[0].segment_index;

		// print the segment template
		switch (context->conf->manifest_format)
		{
		case FORMAT_SEGMENT_TEMPLATE:
			// increment cur_duration_items (don't really need the count)
			dash_packager_get_cur_clip_segment_count(
				&context->segment_durations[media_type],
				cur_duration_items);

			p = dash_packager_write_segment_template(
				p,
				context->conf,
				start_number,
				context->clip_index == 0 ? media_set->initial_segment_clip_relative_index : 0,
				clip_spec,
				media_set,
				reference_track,
				&context->base_url);
			break;

		case FORMAT_SEGMENT_TIMELINE:
			p = dash_packager_write_segment_timeline(
				p,
				context->conf,
				start_number,
				context->clip_start_time,
				clip_spec,
				reference_track,
				&context->segment_durations[media_type],
				cur_duration_items,
				&context->base_url);
			break;

		case FORMAT_SEGMENT_LIST:
			if (media_set->use_discontinuity)
			{
				segment_count = dash_packager_get_cur_clip_segment_count(
					&context->segment_durations[media_type],
					cur_duration_items);
			}
			else
			{
				segment_count = context->segment_durations[media_type].segment_count;
			}
			break;
		}

		// print the representations
		for (cur_track_ptr = adaptation_set->first;
			cur_track_ptr < adaptation_set->last;
			cur_track_ptr++)
		{
			cur_track = (*cur_track_ptr) + filtered_clip_offset;
			cur_sequence = cur_track->file_info.source->sequence;

			dash_packager_get_track_spec(
				&representation_id, 
				media_set, 
				cur_sequence->index, 
				cur_track->index, 
				cur_track->media_info.media_type);

			switch (media_type)
			{
			case MEDIA_TYPE_VIDEO:
				dash_packager_write_frame_rate(
					cur_track->media_info.min_frame_duration,
					DASH_TIMESCALE,
					&frame_rate);

				p = vod_sprintf(p,
					VOD_DASH_MANIFEST_REPRESENTATION_HEADER_VIDEO,
					&representation_id,
					&dash_codecs[cur_track->media_info.codec_id].mime_type,
					&cur_track->media_info.codec_name,
					(uint32_t)cur_track->media_info.u.video.width,
					(uint32_t)cur_track->media_info.u.video.height,
					&frame_rate,
					cur_track->media_info.bitrate
					);
				break;

			case MEDIA_TYPE_AUDIO:
				p = vod_sprintf(p,
					VOD_DASH_MANIFEST_REPRESENTATION_HEADER_AUDIO,
					&representation_id,
					&dash_codecs[cur_track->media_info.codec_id].mime_type,
					&cur_track->media_info.codec_name,
					cur_track->media_info.u.audio.sample_rate,
					cur_track->media_info.bitrate);
				break;
			}

			if (context->conf->manifest_format == FORMAT_SEGMENT_LIST)
			{
				p = dash_packager_write_segment_list(
					p,
					context,
					start_number,
					clip_spec,
					cur_sequence,
					cur_track,
					segment_count);
			}

			// write any additional tags
			if (context->extensions.representation.write != NULL)
			{
				p = context->extensions.representation.write(
					context->extensions.representation.context,
					p, 
					cur_track);
			}

			p = vod_copy(p, VOD_DASH_MANIFEST_REPRESENTATION_FOOTER, sizeof(VOD_DASH_MANIFEST_REPRESENTATION_FOOTER) - 1);
		}

		// print the footer
		p = vod_copy(p, VOD_DASH_MANIFEST_ADAPTATION_FOOTER, sizeof(VOD_DASH_MANIFEST_ADAPTATION_FOOTER) - 1);
	}

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

	return p;
}