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; }