Esempio n. 1
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,
	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;
}
Esempio n. 2
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;
}
Esempio n. 3
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,
	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 = m3u8_builder_build_required_tracks_string(
		request_context, 
		media_set,
		sequence_index,
		request_params,
		&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;
}
Esempio n. 4
0
vod_status_t
m3u8_builder_build_iframe_playlist(
    request_context_t* request_context,
    m3u8_config_t* 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)
{
    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;

    // initialize the muxer
    rc = hls_muxer_init(&muxer_state, request_context, 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 + m3u8_builder_get_int_print_len(vod_div_ceil(mpeg_metadata->duration_millis, 1000)) +
                    sizeof(byte_range_tag_format) + VOD_INT32_LEN + m3u8_builder_get_int_print_len(MAX_FRAME_SIZE) - (sizeof("%uD%uD") - 1) +
                    base_url->len + conf->segment_file_name_prefix.len + 1 + m3u8_builder_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;
}