示例#1
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;
}
vod_status_t
hds_packager_build_manifest(
	request_context_t* request_context,
	hds_manifest_config_t* conf,
	vod_str_t* manifest_id,
	segmenter_conf_t* segmenter_conf,
	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;
	segment_durations_t* segment_durations;
	uint32_t bitrate;
	uint32_t index;
	uint32_t abst_atom_size;
	uint32_t max_abst_atom_size = 0;
	uint32_t total_stream_count = mpeg_metadata->stream_count[MEDIA_TYPE_VIDEO] + mpeg_metadata->stream_count[MEDIA_TYPE_AUDIO];
	size_t result_size;
	vod_status_t rc;
	u_char* temp_buffer;
	u_char* p;

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

	// calculate the result size
	result_size = 
		sizeof(HDS_MANIFEST_HEADER) - 1 + 2 * VOD_INT32_LEN + manifest_id->len + 
		sizeof(HDS_MANIFEST_FOOTER);

	index = 0;
	WALK_STREAMS_BY_FILES_START(cur_file_streams, mpeg_metadata)

		rc = segmenter_conf->get_segment_durations(
			request_context,
			segmenter_conf,
			cur_file_streams,
			MEDIA_TYPE_COUNT,
			&segment_durations[index]);
		if (rc != VOD_OK)
		{
			return rc;
		}

		abst_atom_size = ABST_BASE_ATOM_SIZE + (segment_durations[index].item_count + 1) * sizeof(afrt_entry_t);
		if (abst_atom_size > max_abst_atom_size)
		{
			max_abst_atom_size = abst_atom_size;
		}

		result_size += 
			sizeof(HDS_BOOTSTRAP_HEADER) - 1 + VOD_INT32_LEN + 
				vod_base64_encoded_length(abst_atom_size) +
			sizeof(HDS_BOOTSTRAP_FOOTER) - 1;
		
		result_size += 
			sizeof(HDS_MEDIA_HEADER_PREFIX_VIDEO) - 1 + 3 * VOD_INT32_LEN +
			conf->fragment_file_name_prefix.len + sizeof("-f-v-a-") - 1 + 3 * VOD_INT32_LEN + 
			sizeof(HDS_MEDIA_HEADER_SUFFIX) - 1 + VOD_INT32_LEN + 
				vod_base64_encoded_length(amf0_max_total_size) +
			sizeof(HDS_MEDIA_FOOTER) - 1;

		index++;

	WALK_STREAMS_BY_FILES_END(cur_file_streams, mpeg_metadata)

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

	temp_buffer = vod_alloc(request_context->pool, MAX(amf0_max_total_size, max_abst_atom_size));
	if (temp_buffer == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_packager_build_manifest: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	// print the manifest header
	p = vod_sprintf(result->data, HDS_MANIFEST_HEADER,
		manifest_id,
		(uint32_t)(mpeg_metadata->duration_millis / 1000),
		(uint32_t)(mpeg_metadata->duration_millis % 1000));

	// bootstrap tags
	index = 0;
	WALK_STREAMS_BY_FILES_START(cur_file_streams, mpeg_metadata)

		p = vod_sprintf(p, HDS_BOOTSTRAP_HEADER, index);

		p = hds_write_base64_abst_atom(p, temp_buffer, &segment_durations[index]);

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

		index++;
		
	WALK_STREAMS_BY_FILES_END(cur_file_streams, mpeg_metadata)

	// media tags
	index = 0;
	WALK_STREAMS_BY_FILES_START(cur_file_streams, mpeg_metadata)

		if (cur_file_streams[MEDIA_TYPE_VIDEO] != NULL)
		{
			stream = cur_file_streams[MEDIA_TYPE_VIDEO];
			bitrate = stream->media_info.bitrate;
			if (cur_file_streams[MEDIA_TYPE_AUDIO] != NULL)
			{
				bitrate += cur_file_streams[MEDIA_TYPE_AUDIO]->media_info.bitrate;
			}

			p = vod_sprintf(p, HDS_MEDIA_HEADER_PREFIX_VIDEO,
				bitrate / 1000,
				(uint32_t)stream->media_info.u.video.width,
				(uint32_t)stream->media_info.u.video.height);
		}
		else
		{
			stream = cur_file_streams[MEDIA_TYPE_AUDIO];
			p = vod_sprintf(p, HDS_MEDIA_HEADER_PREFIX_AUDIO,
				stream->media_info.bitrate / 1000);
		}

		// url
		p = vod_copy(p, conf->fragment_file_name_prefix.data, conf->fragment_file_name_prefix.len);
		if (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++ = '-';

		p = vod_sprintf(p, HDS_MEDIA_HEADER_SUFFIX, index++);
		
		p = hds_amf0_write_base64_metadata(p, temp_buffer, cur_file_streams);

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

	WALK_STREAMS_BY_FILES_END(cur_file_streams, mpeg_metadata)

	// manifest footer
	p = vod_copy(p, HDS_MANIFEST_FOOTER, sizeof(HDS_MANIFEST_FOOTER) - 1);

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

	vod_free(request_context->pool, temp_buffer);

	return VOD_OK;
}