Exemple #1
0
vod_status_t 
mss_packager_build_manifest(
	request_context_t* request_context, 
	segmenter_conf_t* segmenter_conf, 
	mpeg_metadata_t* mpeg_metadata, 
	size_t extra_tags_size,
	mss_write_tags_callback_t write_extra_tags,
	void* extra_tags_writer_context,
	vod_str_t* result)
{
	mpeg_stream_metadata_t* cur_stream;
	segment_durations_t segment_durations[MEDIA_TYPE_COUNT];
	uint64_t duration_100ns;
	uint32_t media_type;
	uint32_t stream_index;
	uint32_t bitrate;
	vod_status_t rc;
	size_t result_size;
	u_char* p;

	// calculate the result size
	result_size = 
		sizeof(MSS_MANIFEST_HEADER) - 1 + VOD_INT64_LEN + 
		extra_tags_size +
		sizeof(MSS_MANIFEST_FOOTER);
	for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++)
	{
		if (mpeg_metadata->longest_stream[media_type] == NULL)
		{
			continue;
		}

		rc = segmenter_conf->get_segment_durations(
			request_context,
			segmenter_conf,
			&mpeg_metadata->longest_stream[media_type],
			1,
			&segment_durations[media_type]);
		if (rc != VOD_OK)
		{
			return rc;
		}

		result_size +=
			sizeof(MSS_STREAM_INDEX_HEADER) - 1 + 2 * sizeof(MSS_STREAM_TYPE_VIDEO) + 2 * VOD_INT32_LEN +
			sizeof(MSS_STREAM_INDEX_FOOTER);

		result_size += segment_durations[media_type].segment_count * (sizeof(MSS_CHUNK_TAG) + VOD_INT32_LEN + VOD_INT64_LEN);
	}

	for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
	{
		switch (cur_stream->media_info.media_type)
		{
		case MEDIA_TYPE_VIDEO:
			result_size += 
				sizeof(MSS_VIDEO_QUALITY_LEVEL_HEADER) - 1 + 4 * VOD_INT32_LEN + cur_stream->media_info.extra_data_size * 2 + 
				sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1;
			break;

		case MEDIA_TYPE_AUDIO:
			result_size += 
				sizeof(MSS_AUDIO_QUALITY_LEVEL_HEADER) - 1 + 6 * VOD_INT32_LEN + cur_stream->media_info.extra_data_size * 2 +
				sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1;
			break;
		}
	}

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

	duration_100ns = rescale_time(mpeg_metadata->duration, mpeg_metadata->timescale, MSS_TIMESCALE);
	p = vod_sprintf(result->data, MSS_MANIFEST_HEADER, duration_100ns);

	if (mpeg_metadata->longest_stream[MEDIA_TYPE_VIDEO] != NULL)
	{
		p = vod_sprintf(p, 
			MSS_STREAM_INDEX_HEADER, 
			MSS_STREAM_TYPE_VIDEO,
			mpeg_metadata->stream_count[MEDIA_TYPE_VIDEO],
			segment_durations[MEDIA_TYPE_VIDEO].segment_count,
			MSS_STREAM_TYPE_VIDEO);

		stream_index = 0;
		for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
		{
			if (cur_stream->media_info.media_type != MEDIA_TYPE_VIDEO)
			{
				continue;
			}

			bitrate = cur_stream->media_info.bitrate;
			bitrate = mss_encode_indexes(bitrate, cur_stream->file_info.file_index, cur_stream->track_index);
			p = vod_sprintf(p, MSS_VIDEO_QUALITY_LEVEL_HEADER,
				stream_index++,
				bitrate,
				(uint32_t)cur_stream->media_info.u.video.width,
				(uint32_t)cur_stream->media_info.u.video.height);

			p = mss_append_hex_string(p, cur_stream->media_info.extra_data, cur_stream->media_info.extra_data_size);

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

		p = mss_write_manifest_chunks(p, &segment_durations[MEDIA_TYPE_VIDEO]);

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

	if (mpeg_metadata->longest_stream[MEDIA_TYPE_AUDIO] != NULL)
	{
		p = vod_sprintf(p, MSS_STREAM_INDEX_HEADER, 
			MSS_STREAM_TYPE_AUDIO,
			mpeg_metadata->stream_count[MEDIA_TYPE_AUDIO],
			segment_durations[MEDIA_TYPE_AUDIO].segment_count,
			MSS_STREAM_TYPE_AUDIO);

		stream_index = 0;
		for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
		{
			if (cur_stream->media_info.media_type != MEDIA_TYPE_AUDIO)
			{
				continue;
			}

			bitrate = cur_stream->media_info.bitrate;
			bitrate = mss_encode_indexes(bitrate, cur_stream->file_info.file_index, cur_stream->track_index);
			p = vod_sprintf(p, MSS_AUDIO_QUALITY_LEVEL_HEADER,
				stream_index++,
				bitrate,
				cur_stream->media_info.u.audio.sample_rate,
				(uint32_t)cur_stream->media_info.u.audio.channels,
				(uint32_t)cur_stream->media_info.u.audio.bits_per_sample,
				(uint32_t)cur_stream->media_info.u.audio.packet_size);

			p = mss_append_hex_string(p, cur_stream->media_info.extra_data, cur_stream->media_info.extra_data_size);

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

		p = mss_write_manifest_chunks(p, &segment_durations[MEDIA_TYPE_AUDIO]);

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

	if (write_extra_tags != NULL)
	{
		p = write_extra_tags(extra_tags_writer_context, p, mpeg_metadata);
	}

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

	result->len = p - result->data;
	
	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"mss_packager_build_manifest: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	
	return VOD_OK;
}
vod_status_t 
mss_packager_build_manifest(
	request_context_t* request_context, 
	mss_manifest_config_t* conf,
	media_set_t* media_set,
	size_t extra_tags_size,
	mss_write_tags_callback_t write_extra_tags,
	void* extra_tags_writer_context,
	vod_str_t* result)
{
	segmenter_conf_t* segmenter_conf = media_set->segmenter_conf;
	media_sequence_t* cur_sequence;
	media_track_t* last_track;
	media_track_t* cur_track;
	segment_durations_t segment_durations[MEDIA_TYPE_COUNT];
	uint64_t duration_100ns;
	uint32_t media_type;
	uint32_t stream_index;
	uint32_t bitrate;
	vod_status_t rc;
	size_t result_size;
	u_char* p;

	if (media_set->use_discontinuity)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"mss_packager_build_manifest: discontinuity is not supported in MSS");
		return VOD_BAD_MAPPING;
	}

	// calculate the result size
	result_size = 
		sizeof(MSS_MANIFEST_HEADER_PREFIX) - 1 + VOD_INT64_LEN + sizeof(MSS_MANIFEST_HEADER_SUFFIX) - 1 +
		extra_tags_size +
		sizeof(MSS_MANIFEST_FOOTER);
	if (media_set->type == MEDIA_SET_LIVE)
	{
		result_size += sizeof(MSS_MANIFEST_HEADER_LIVE_ATTRIBUTES) - 1 + VOD_INT64_LEN + VOD_INT32_LEN;
	}

	for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++)
	{
		if (media_set->track_count[media_type] == 0)
		{
			continue;
		}

		rc = segmenter_conf->get_segment_durations(
			request_context,
			segmenter_conf,
			media_set,
			NULL,
			media_type,
			&segment_durations[media_type]);
		if (rc != VOD_OK)
		{
			return rc;
		}

		result_size +=
			sizeof(MSS_STREAM_INDEX_HEADER) - 1 + 2 * sizeof(MSS_STREAM_TYPE_VIDEO) + 2 * VOD_INT32_LEN +
			sizeof(MSS_STREAM_INDEX_FOOTER);

		switch (media_set->type)
		{
		case MEDIA_SET_VOD:
			result_size += segment_durations[media_type].segment_count * (sizeof(MSS_CHUNK_TAG) + VOD_INT32_LEN + VOD_INT64_LEN);
			break;

		case MEDIA_SET_LIVE:
			result_size += segment_durations[media_type].segment_count * (sizeof(MSS_CHUNK_TAG_LIVE) + VOD_INT64_LEN) + 
				sizeof(MSS_CHUNK_TAG_LIVE_FIRST) + 2 * VOD_INT64_LEN;
			break;
		}
	}

	mss_packager_remove_redundant_tracks(conf->duplicate_bitrate_threshold,	media_set);

	cur_track = media_set->filtered_tracks;
	last_track = cur_track + media_set->total_track_count;
	for (; cur_track < last_track; cur_track++)
	{
		switch (cur_track->media_info.media_type)
		{
		case MEDIA_TYPE_VIDEO:
			result_size +=
				sizeof(MSS_VIDEO_QUALITY_LEVEL_HEADER) - 1 + 4 * VOD_INT32_LEN + cur_track->media_info.extra_data_size * 2 +
				sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1;
			break;

		case MEDIA_TYPE_AUDIO:
			result_size +=
				sizeof(MSS_AUDIO_QUALITY_LEVEL_HEADER) - 1 + 6 * VOD_INT32_LEN + cur_track->media_info.extra_data_size * 2 +
				sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1;
			break;
		}
	}

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

	// header
	if (media_set->type != MEDIA_SET_LIVE)
	{
		duration_100ns = mss_rescale_millis(media_set->total_duration);
	}
	else
	{
		duration_100ns = 0;
	}

	p = vod_sprintf(result->data, MSS_MANIFEST_HEADER_PREFIX, duration_100ns);
	if (media_set->type == MEDIA_SET_LIVE)
	{
		media_type = media_set->track_count[MEDIA_TYPE_VIDEO] != 0 ? MEDIA_TYPE_VIDEO : MEDIA_TYPE_AUDIO;
		p = vod_sprintf(p, MSS_MANIFEST_HEADER_LIVE_ATTRIBUTES, 
			mss_rescale_millis(segment_durations[media_type].segment_count * segmenter_conf->segment_duration),
			MSS_LOOK_AHEAD_COUNT);
	}
	p = vod_copy(p, MSS_MANIFEST_HEADER_SUFFIX, sizeof(MSS_MANIFEST_HEADER_SUFFIX) - 1);

	if (media_set->track_count[MEDIA_TYPE_VIDEO] != 0)
	{
		p = vod_sprintf(p, 
			MSS_STREAM_INDEX_HEADER, 
			MSS_STREAM_TYPE_VIDEO,
			media_set->track_count[MEDIA_TYPE_VIDEO],
			segment_durations[MEDIA_TYPE_VIDEO].segment_count,
			MSS_STREAM_TYPE_VIDEO);

		stream_index = 0;
		for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
		{
			last_track = cur_sequence->filtered_clips[0].last_track;
			for (cur_track = cur_sequence->filtered_clips[0].first_track; cur_track < last_track; cur_track++)
			{
				if (cur_track->media_info.media_type != MEDIA_TYPE_VIDEO)
				{
					continue;
				}

				bitrate = cur_track->media_info.bitrate;
				bitrate = mss_encode_indexes(bitrate, cur_sequence->index, cur_track->index);
				p = vod_sprintf(p, MSS_VIDEO_QUALITY_LEVEL_HEADER,
					stream_index++,
					bitrate,
					(uint32_t)cur_track->media_info.u.video.width,
					(uint32_t)cur_track->media_info.u.video.height);

				p = mss_append_hex_string(p, cur_track->media_info.extra_data, cur_track->media_info.extra_data_size);

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

		switch (media_set->type)
		{
		case MEDIA_SET_VOD:
			p = mss_write_manifest_chunks(p, &segment_durations[MEDIA_TYPE_VIDEO]);
			break;

		case MEDIA_SET_LIVE:
			p = mss_write_manifest_chunks_live(p, &segment_durations[MEDIA_TYPE_VIDEO]);
			break;
		}

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

	if (media_set->track_count[MEDIA_TYPE_AUDIO] != 0)
	{
		p = vod_sprintf(p, MSS_STREAM_INDEX_HEADER, 
			MSS_STREAM_TYPE_AUDIO,
			media_set->track_count[MEDIA_TYPE_AUDIO],
			segment_durations[MEDIA_TYPE_AUDIO].segment_count,
			MSS_STREAM_TYPE_AUDIO);

		stream_index = 0;
		for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
		{
			last_track = cur_sequence->filtered_clips[0].last_track;
			for (cur_track = cur_sequence->filtered_clips[0].first_track; cur_track < last_track; cur_track++)
			{
				if (cur_track->media_info.media_type != MEDIA_TYPE_AUDIO)
				{
					continue;
				}

				bitrate = cur_track->media_info.bitrate;
				bitrate = mss_encode_indexes(bitrate, cur_sequence->index, cur_track->index);
				p = vod_sprintf(p, MSS_AUDIO_QUALITY_LEVEL_HEADER,
					stream_index++,
					bitrate,
					cur_track->media_info.u.audio.sample_rate,
					(uint32_t)cur_track->media_info.u.audio.channels,
					(uint32_t)cur_track->media_info.u.audio.bits_per_sample,
					(uint32_t)cur_track->media_info.u.audio.packet_size);

				p = mss_append_hex_string(p, cur_track->media_info.extra_data, cur_track->media_info.extra_data_size);

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

		switch (media_set->type)
		{
		case MEDIA_SET_VOD:
			p = mss_write_manifest_chunks(p, &segment_durations[MEDIA_TYPE_AUDIO]);
			break;

		case MEDIA_SET_LIVE:
			p = mss_write_manifest_chunks_live(p, &segment_durations[MEDIA_TYPE_AUDIO]);
			break;
		}

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

	if (write_extra_tags != NULL)
	{
		p = write_extra_tags(extra_tags_writer_context, p, media_set);
	}

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

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