static void
m3u8_builder_append_iframe_string(void* context, uint32_t segment_index, uint32_t frame_duration, uint32_t frame_start, uint32_t frame_size)
{
	write_segment_context_t* ctx = (write_segment_context_t*)context;

	ctx->p = m3u8_builder_append_extinf_tag(ctx->p, frame_duration, 1000);
	ctx->p = vod_sprintf(ctx->p, byte_range_tag_format, frame_size, frame_start);
	ctx->p = m3u8_builder_append_segment_name(
		ctx->p, 
		ctx->base_url,
		ctx->segment_file_name_prefix, 
		segment_index, 
		&ctx->tracks_spec);
}
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 = m3u8_builder_build_required_tracks_string(
		request_context,
		media_set,
		sequence_index,
		request_params,
		&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_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 (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_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 (sequence_index != INVALID_SEQUENCE_INDEX)
		{
			p = vod_sprintf(p, "-f%uD", sequence_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, 
		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->type == MEDIA_SET_VOD)
	{
		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;
}
Beispiel #3
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,
    bool_t encryption_enabled,
    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 + m3u8_builder_get_int_print_len(vod_div_ceil(mpeg_metadata->duration_millis, 1000)) +
                     segments_base_url->len + conf->segment_file_name_prefix.len + 1 + m3u8_builder_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_enabled)
    {
        result_size +=
            sizeof(encryption_key_tag_prefix) - 1 +
            base_url->len +
            conf->encryption_key_file_name.len +
            sizeof("-f") - 1 + VOD_INT32_LEN +
            sizeof(encryption_key_tag_postfix) - 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_enabled)
    {
        p = vod_copy(p, encryption_key_tag_prefix, sizeof(encryption_key_tag_prefix) - 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_postfix, sizeof(encryption_key_tag_postfix)-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;
}