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