From 38a6a176f8dadf44699e57f5f2c3c7b853db7a7e Mon Sep 17 00:00:00 2001 From: Sebastian Annies Date: Mon, 20 Jun 2022 08:56:06 +0200 Subject: [PATCH 1/7] initial throw --- ngx_http_vod_hls.c | 39 ++++++- vod/hls/hls_muxer.c | 234 +++++++++++++++++++++++++++++++++++++++++ vod/hls/hls_muxer.h | 10 ++ vod/hls/m3u8_builder.c | 59 ++++++++++- vod/hls/m3u8_builder.h | 2 + 5 files changed, 337 insertions(+), 7 deletions(-) diff --git a/ngx_http_vod_hls.c b/ngx_http_vod_hls.c index 7d48dca1..488459fb 100644 --- a/ngx_http_vod_hls.c +++ b/ngx_http_vod_hls.c @@ -258,7 +258,38 @@ ngx_http_vod_hls_handle_master_playlist( { ngx_http_vod_loc_conf_t* conf = submodule_context->conf; ngx_str_t base_url = ngx_null_string; - vod_status_t rc; + hls_encryption_params_t encryption_params; + vod_status_t rc; + +#if (NGX_HAVE_OPENSSL_EVP) + rc = ngx_http_vod_hls_init_encryption_params(&encryption_params, submodule_context, container_format); + if (rc != NGX_OK) + { + return rc; + } + + if (encryption_params.type != HLS_ENC_NONE) + { + if (conf->hls.encryption_key_uri != NULL) + { + if (ngx_http_complex_value( + submodule_context->r, + conf->hls.encryption_key_uri, + &encryption_params.key_uri) != NGX_OK) + { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, + "ngx_http_vod_hls_handle_index_playlist: ngx_http_complex_value failed"); + return NGX_ERROR; + } + } + else + { + encryption_params.key_uri.len = 0; + } + } +#else + encryption_params.type = HLS_ENC_NONE; +#endif // NGX_HAVE_OPENSSL_EVP if (conf->hls.absolute_master_urls) { @@ -275,6 +306,8 @@ ngx_http_vod_hls_handle_master_playlist( conf->hls.encryption_method, &base_url, &submodule_context->media_set, + &conf->hls.mpegts_muxer_config, + &encryption_params, response); if (rc != VOD_OK) { @@ -873,8 +906,8 @@ ngx_http_vod_hls_handle_vtt_segment( } static const ngx_http_vod_request_t hls_master_request = { - 0, - PARSE_FLAG_DURATION_LIMITS_AND_TOTAL_SIZE | PARSE_FLAG_KEY_FRAME_BITRATE | PARSE_FLAG_CODEC_NAME | PARSE_FLAG_PARSED_EXTRA_DATA_SIZE | PARSE_FLAG_CODEC_TRANSFER_CHAR, + REQUEST_FLAG_SINGLE_TRACK_PER_MEDIA_TYPE | REQUEST_FLAG_PARSE_ALL_CLIPS, + PARSE_FLAG_FRAMES_ALL_EXCEPT_OFFSETS | PARSE_FLAG_PARSED_EXTRA_DATA_SIZE, REQUEST_CLASS_OTHER, SUPPORTED_CODECS | VOD_CODEC_FLAG(WEBVTT), HLS_TIMESCALE, diff --git a/vod/hls/hls_muxer.c b/vod/hls/hls_muxer.c index 26630ddc..754d4ad2 100644 --- a/vod/hls/hls_muxer.c +++ b/vod/hls/hls_muxer.c @@ -1270,3 +1270,237 @@ hls_muxer_simulation_reset(hls_muxer_state_t* state) state->cur_frame = NULL; } + +vod_status_t +hls_muxer_simulate_get_segment_sizes( + request_context_t* request_context, + segment_durations_t* segment_durations, + hls_mpegts_muxer_conf_t* muxer_conf, + hls_encryption_params_t* encryption_params, + media_set_t* media_set, + vod_array_t* segments_sizes, + void* context) +{ + hls_muxer_stream_state_t* selected_stream; + segment_duration_item_t* cur_item; + segment_duration_item_t* last_item; + hls_muxer_state_t state; + input_frame_t* cur_frame; + uint32_t cur_frame_time; + + uint32_t frame_size = 0; + uint32_t frame_start_time = 0; + uint32_t first_frame_time = 0; + uint32_t end_time; + uint32_t segment_index = 0; + uint64_t cur_frame_dts; + uint64_t cur_frame_time_offset; + uint32_t repeat_count; + uint64_t segment_end; + bool_t simulation_supported; + bool_t last_frame; + vod_status_t rc; + uint32_t segment_size = 0; +#if (VOD_DEBUG) + off_t cur_frame_start; +#endif // VOD_DEBUG + + cur_item = segment_durations->items; + last_item = segment_durations->items + segment_durations->item_count; + + if (cur_item >= last_item) + { + return VOD_OK; + } + // initialize the muxer + rc = hls_muxer_init_base( + &state, + request_context, + muxer_conf, + encryption_params, + 0, + media_set, + &simulation_supported, + NULL); + if (rc != VOD_OK) + { + return rc; + } + + if (!simulation_supported) + { + vod_log_error(VOD_LOG_ERR, request_context->log, 0, + "hls_muxer_simulate_get_segment_sizes: simulation not supported for this file, cant create iframe playlist"); + return VOD_BAD_REQUEST; + } + + // initialize the repeat count, segment end, and the per stream limit + repeat_count = cur_item->repeat_count - 1; + segment_end = cur_item->duration; + + if (repeat_count <= 0 && (cur_item + 1 >= last_item || cur_item[1].discontinuity)) + { + hls_muxer_simulation_set_segment_limit_unlimited(&state); + } + else + { + hls_muxer_simulation_set_segment_limit(&state, segment_end, segment_durations->timescale); + } + + mpegts_encoder_simulated_start_segment(&state.queue); + for (;;) + { + + // get a frame + for (;;) + { + // choose a stream for the current frame + rc = hls_muxer_choose_stream(&state, &selected_stream); + if (rc == VOD_OK) + { + break; + } + + if (rc != VOD_NOT_FOUND) + { + return rc; + } + // update the limit for the next segment + if (repeat_count <= 0) + { + cur_item++; + if (cur_item >= last_item) + { + vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, + "goto done: done"); + goto done; + } + + repeat_count = cur_item->repeat_count; + } + + repeat_count--; + segment_end += cur_item->duration; + + if (repeat_count <= 0 && (cur_item + 1 >= last_item || cur_item[1].discontinuity)) + { + hls_muxer_simulation_set_segment_limit_unlimited(&state); + } + else + { + hls_muxer_simulation_set_segment_limit(&state, segment_end, segment_durations->timescale); + } + + // start the next segment + mpegts_encoder_simulated_start_segment(&state.queue); + segment_index++; + + } + // update the stream state + cur_frame = selected_stream->cur_frame; + selected_stream->cur_frame++; + cur_frame_time_offset = selected_stream->next_frame_time_offset; + cur_frame_dts = selected_stream->next_frame_time_offset; + selected_stream->next_frame_time_offset += cur_frame->duration; + + // flush any buffered frames if their delay becomes too big + hls_muxer_simulation_flush_delayed_streams(&state, selected_stream, cur_frame_dts); + + // check whether this is the last frame of the selected stream in this segment + last_frame = ((selected_stream->cur_frame >= selected_stream->cur_frame_part.last_frame && + selected_stream->cur_frame_part.next == NULL) || + selected_stream->next_frame_time_offset >= selected_stream->segment_limit); + + // write the frame +#if (VOD_DEBUG) + cur_frame_start = state.queue.cur_offset; +#endif // VOD_DEBUG + + segment_size += cur_frame->size; + hls_muxer_simulation_write_frame( + selected_stream, + cur_frame, + cur_frame_dts, + last_frame); + +#if (VOD_DEBUG) + if (cur_frame_start != state.queue.cur_offset) + { + vod_log_debug4(VOD_LOG_DEBUG_LEVEL, state.request_context->log, 0, + "hls_muxer_simulate_get_segment_sizes: wrote frame segment %uD packets %uD-%uD dts %L", + segment_index + 1, + (uint32_t)(cur_frame_start / MPEGTS_PACKET_SIZE + 1), + (uint32_t)(state.queue.cur_offset / MPEGTS_PACKET_SIZE + 1), + cur_frame_dts); + } +#endif // VOD_DEBUG + + + if (!selected_stream->is_first_segment_frame && selected_stream->prev_key_frame) + { + // get the frame time + cur_frame_time = rescale_time(selected_stream->prev_frame_pts, HLS_TIMESCALE, 1000); // in millis + if (frame_size != 0) + { + if (cur_frame_time > frame_start_time) + { + vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, + "hls_muxer_simulate_get_segment_sizes: segment %L size %L", segment_index, segment_size); + segment_size = 0; + } + } + else + { + first_frame_time = cur_frame_time; + } + + // save the info of the current keyframe + + frame_size = selected_stream->mpegts_encoder_state.last_frame_end_pos - + selected_stream->mpegts_encoder_state.last_frame_start_pos; + frame_start_time = cur_frame_time; + } + + if (last_frame && cur_frame[0].key_frame) + { + // get the frame time + cur_frame_time = rescale_time(cur_frame_time_offset + cur_frame[0].pts_delay, HLS_TIMESCALE, 1000); // in millis + if (frame_size != 0) + { + if (cur_frame_time > frame_start_time) + { + vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, + "hls_muxer_simulate_get_segment_sizes: segment %L size %L", segment_index, segment_size); + segment_size = 0; + } + } + else + { + first_frame_time = cur_frame_time; + } + + // save the info of the current keyframe + frame_size = selected_stream->mpegts_encoder_state.cur_frame_end_pos - + selected_stream->mpegts_encoder_state.cur_frame_start_pos; + frame_start_time = cur_frame_time; + + } + + selected_stream->prev_key_frame = cur_frame->key_frame; + selected_stream->prev_frame_pts = cur_frame_time_offset + cur_frame->pts_delay; + selected_stream->is_first_segment_frame = FALSE; + } + + done: + + // call the callback for the last frame + end_time = first_frame_time + state.video_duration; + if (frame_size != 0 && end_time > frame_start_time) + { + vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, + "hls_muxer_simulate_get_segment_sizes: segment %L size %L", segment_index, segment_size); + + } + + return VOD_OK; +} diff --git a/vod/hls/hls_muxer.h b/vod/hls/hls_muxer.h index 6684d343..f13781ea 100644 --- a/vod/hls/hls_muxer.h +++ b/vod/hls/hls_muxer.h @@ -112,4 +112,14 @@ vod_status_t hls_muxer_simulate_get_iframes( hls_get_iframe_positions_callback_t callback, void* context); + +vod_status_t hls_muxer_simulate_get_segment_sizes( + request_context_t* request_context, + segment_durations_t* segment_durations, + hls_mpegts_muxer_conf_t* muxer_conf, + hls_encryption_params_t* encryption_params, + media_set_t* media_set, + vod_array_t* segments_sizes, + void* context); + #endif // __HLS_MUXER_H__ diff --git a/vod/hls/m3u8_builder.c b/vod/hls/m3u8_builder.c index bffc4ced..be417a35 100644 --- a/vod/hls/m3u8_builder.c +++ b/vod/hls/m3u8_builder.c @@ -1,4 +1,5 @@ #include "m3u8_builder.h" +#include "hls_muxer.h" #include "../manifest_utils.h" #if (NGX_HAVE_OPENSSL_EVP) @@ -1064,7 +1065,11 @@ m3u8_builder_write_variants( m3u8_config_t* conf, vod_str_t* base_url, media_set_t* media_set, - media_track_t* group_audio_track) + media_track_t* group_audio_track, + hls_mpegts_muxer_conf_t* muxer_conf, + segment_durations_t* segment_durations, + hls_encryption_params_t* encryption_params, + request_context_t* request_context) { adaptation_set_t* adaptation_set = adaptation_sets->first; media_track_t** cur_track_ptr; @@ -1081,7 +1086,10 @@ m3u8_builder_write_variants( cur_track_ptr < adaptation_set->last; cur_track_ptr += muxed_tracks) { - // get the audio / video tracks + vod_array_t segments_sizes; + hls_muxer_simulate_get_segment_sizes(request_context, segment_durations, muxer_conf, encryption_params, media_set, &segments_sizes, request_context); + + // get the audio / video tracks if (muxed_tracks == MEDIA_TYPE_COUNT) { tracks[MEDIA_TYPE_VIDEO] = cur_track_ptr[MEDIA_TYPE_VIDEO]; @@ -1275,6 +1283,8 @@ m3u8_builder_build_master_playlist( vod_uint_t encryption_method, vod_str_t* base_url, media_set_t* media_set, + hls_mpegts_muxer_conf_t* muxer_conf, + hls_encryption_params_t* encryption_params, vod_str_t* result) { adaptation_sets_t adaptation_sets; @@ -1293,6 +1303,9 @@ m3u8_builder_build_master_playlist( size_t result_size; u_char* p; bool_t alternative_audio; + segment_durations_t segment_durations; + segmenter_conf_t* segmenter_conf = media_set->segmenter_conf; + // get the adaptations sets flags = ADAPTATION_SETS_FLAG_SINGLE_LANG_TRACK | ADAPTATION_SETS_FLAG_MULTI_AUDIO_CODEC; @@ -1458,6 +1471,36 @@ m3u8_builder_build_master_playlist( p = m3u8_builder_closed_captions_write(p, media_set); } + + 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) + { + vod_log_error(VOD_LOG_ERR, request_context->log, 0, + "ngx_http_vod_thumb_get_url: result length %uz exceeded allocated length %uz", + result->len, result_size); + return rc; + } // output variants if (variant_set_count > 1) { @@ -1472,7 +1515,11 @@ m3u8_builder_build_master_playlist( conf, base_url, media_set, - *cur_track_ptr); + *cur_track_ptr, + muxer_conf, + &segment_durations, + encryption_params, + request_context); } } else @@ -1483,7 +1530,11 @@ m3u8_builder_build_master_playlist( conf, base_url, media_set, - alternative_audio ? adaptation_sets.first_by_type[ADAPTATION_TYPE_AUDIO]->first[0] : NULL); + alternative_audio ? adaptation_sets.first_by_type[ADAPTATION_TYPE_AUDIO]->first[0] : NULL, + muxer_conf, + &segment_durations, + encryption_params, + request_context); } // iframes diff --git a/vod/hls/m3u8_builder.h b/vod/hls/m3u8_builder.h index 5b3dff95..7923c52d 100644 --- a/vod/hls/m3u8_builder.h +++ b/vod/hls/m3u8_builder.h @@ -41,6 +41,8 @@ vod_status_t m3u8_builder_build_master_playlist( vod_uint_t encryption_method, vod_str_t* base_url, media_set_t* media_set, + hls_mpegts_muxer_conf_t* muxer_conf, + hls_encryption_params_t* encryption_params, vod_str_t* result); vod_status_t m3u8_builder_build_index_playlist( From 5c58918d0ab7706eaccfc407d8f8426180938a8b Mon Sep 17 00:00:00 2001 From: Sebastian Annies Date: Mon, 20 Jun 2022 19:04:13 +0200 Subject: [PATCH 2/7] test with one file working --- vod/hls/hls_muxer.c | 110 ++++++++++++++--------------------------- vod/hls/hls_muxer.h | 4 +- vod/hls/m3u8_builder.c | 31 ++++-------- 3 files changed, 48 insertions(+), 97 deletions(-) diff --git a/vod/hls/hls_muxer.c b/vod/hls/hls_muxer.c index 754d4ad2..2d67b3db 100644 --- a/vod/hls/hls_muxer.c +++ b/vod/hls/hls_muxer.c @@ -196,7 +196,7 @@ hls_muxer_init_id3_stream( if (sequence_id->len != 0) { sequence_id_escape = vod_escape_json(NULL, sequence_id->data, sequence_id->len); - data_size = sizeof(ID3_TEXT_JSON_SEQUENCE_ID_PREFIX_FORMAT) + VOD_INT64_LEN + + data_size = sizeof(ID3_TEXT_JSON_SEQUENCE_ID_PREFIX_FORMAT) + VOD_INT64_LEN + sequence_id->len + sequence_id_escape + sizeof(ID3_TEXT_JSON_SEQUENCE_ID_SUFFIX); } @@ -907,7 +907,7 @@ hls_muxer_simulation_write_frame(hls_muxer_stream_state_t* selected_stream, inpu selected_stream->filter.simulated_flush_frame(&selected_stream->filter_context, last_frame); } -static void +static void hls_muxer_simulation_set_segment_limit( hls_muxer_state_t* state, uint64_t segment_end, @@ -1278,29 +1278,28 @@ hls_muxer_simulate_get_segment_sizes( hls_mpegts_muxer_conf_t* muxer_conf, hls_encryption_params_t* encryption_params, media_set_t* media_set, - vod_array_t* segments_sizes, - void* context) + uint32_t* bandwidth, + uint32_t* avg_bandwidth) { hls_muxer_stream_state_t* selected_stream; segment_duration_item_t* cur_item; segment_duration_item_t* last_item; hls_muxer_state_t state; input_frame_t* cur_frame; - uint32_t cur_frame_time; - - uint32_t frame_size = 0; - uint32_t frame_start_time = 0; - uint32_t first_frame_time = 0; - uint32_t end_time; uint32_t segment_index = 0; + uint32_t segment_bandwidth[segment_durations->segment_count]; + uint64_t cur_frame_dts; - uint64_t cur_frame_time_offset; uint32_t repeat_count; uint64_t segment_end; bool_t simulation_supported; bool_t last_frame; vod_status_t rc; - uint32_t segment_size = 0; + + uint32_t largest_segment = 0; + uint32_t sum_segments = 0; + uint32_t valid_segments = 0; + #if (VOD_DEBUG) off_t cur_frame_start; #endif // VOD_DEBUG @@ -1312,6 +1311,7 @@ hls_muxer_simulate_get_segment_sizes( { return VOD_OK; } + // initialize the muxer rc = hls_muxer_init_base( &state, @@ -1369,6 +1369,9 @@ hls_muxer_simulate_get_segment_sizes( if (repeat_count <= 0) { cur_item++; + vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, + "hls_muxer_simulate_get_segment_sizes: next segment duration"); + if (cur_item >= last_item) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, @@ -1380,8 +1383,11 @@ hls_muxer_simulate_get_segment_sizes( } repeat_count--; + vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, + "hls_muxer_simulate_get_segment_sizes: %L %L", segment_end, cur_item->duration); segment_end += cur_item->duration; + if (repeat_count <= 0 && (cur_item + 1 >= last_item || cur_item[1].discontinuity)) { hls_muxer_simulation_set_segment_limit_unlimited(&state); @@ -1391,7 +1397,13 @@ hls_muxer_simulate_get_segment_sizes( hls_muxer_simulation_set_segment_limit(&state, segment_end, segment_durations->timescale); } - // start the next segment + + vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, + "hls_muxer_simulate_get_segment_sizes: segment %L %L", segment_index + 1, state.queue.cur_offset ); + segment_bandwidth[segment_index] = cur_item->duration > 0 ? (state.queue.cur_offset / ((float)cur_item->duration/(float)segment_durations->timescale)): -1; + + + mpegts_encoder_simulated_start_segment(&state.queue); segment_index++; @@ -1399,7 +1411,6 @@ hls_muxer_simulate_get_segment_sizes( // update the stream state cur_frame = selected_stream->cur_frame; selected_stream->cur_frame++; - cur_frame_time_offset = selected_stream->next_frame_time_offset; cur_frame_dts = selected_stream->next_frame_time_offset; selected_stream->next_frame_time_offset += cur_frame->duration; @@ -1416,7 +1427,6 @@ hls_muxer_simulate_get_segment_sizes( cur_frame_start = state.queue.cur_offset; #endif // VOD_DEBUG - segment_size += cur_frame->size; hls_muxer_simulation_write_frame( selected_stream, cur_frame, @@ -1434,73 +1444,25 @@ hls_muxer_simulate_get_segment_sizes( cur_frame_dts); } #endif // VOD_DEBUG + } + done: - if (!selected_stream->is_first_segment_frame && selected_stream->prev_key_frame) - { - // get the frame time - cur_frame_time = rescale_time(selected_stream->prev_frame_pts, HLS_TIMESCALE, 1000); // in millis - if (frame_size != 0) - { - if (cur_frame_time > frame_start_time) - { - vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, - "hls_muxer_simulate_get_segment_sizes: segment %L size %L", segment_index, segment_size); - segment_size = 0; - } - } - else - { - first_frame_time = cur_frame_time; - } - // save the info of the current keyframe - frame_size = selected_stream->mpegts_encoder_state.last_frame_end_pos - - selected_stream->mpegts_encoder_state.last_frame_start_pos; - frame_start_time = cur_frame_time; + for (uint32_t i = 0; i < segment_durations->segment_count; i++) { + if (segment_bandwidth[i] > largest_segment) { + largest_segment = segment_bandwidth[i]; } - - if (last_frame && cur_frame[0].key_frame) - { - // get the frame time - cur_frame_time = rescale_time(cur_frame_time_offset + cur_frame[0].pts_delay, HLS_TIMESCALE, 1000); // in millis - if (frame_size != 0) - { - if (cur_frame_time > frame_start_time) - { - vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, - "hls_muxer_simulate_get_segment_sizes: segment %L size %L", segment_index, segment_size); - segment_size = 0; - } - } - else - { - first_frame_time = cur_frame_time; - } - - // save the info of the current keyframe - frame_size = selected_stream->mpegts_encoder_state.cur_frame_end_pos - - selected_stream->mpegts_encoder_state.cur_frame_start_pos; - frame_start_time = cur_frame_time; - + if (segment_bandwidth[i] > 100) { + sum_segments += segment_bandwidth[i]; + valid_segments++; } - - selected_stream->prev_key_frame = cur_frame->key_frame; - selected_stream->prev_frame_pts = cur_frame_time_offset + cur_frame->pts_delay; - selected_stream->is_first_segment_frame = FALSE; - } - - done: - - // call the callback for the last frame - end_time = first_frame_time + state.video_duration; - if (frame_size != 0 && end_time > frame_start_time) - { vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, - "hls_muxer_simulate_get_segment_sizes: segment %L size %L", segment_index, segment_size); - + "hls_muxer_simulate_get_segment_sizes: segment %L %L kbit/s", i + 1, segment_bandwidth[i] / 1024 * 8); } + *avg_bandwidth = sum_segments / valid_segments * 8 ; + *bandwidth = largest_segment * 8; return VOD_OK; } diff --git a/vod/hls/hls_muxer.h b/vod/hls/hls_muxer.h index f13781ea..9228b784 100644 --- a/vod/hls/hls_muxer.h +++ b/vod/hls/hls_muxer.h @@ -119,7 +119,7 @@ vod_status_t hls_muxer_simulate_get_segment_sizes( hls_mpegts_muxer_conf_t* muxer_conf, hls_encryption_params_t* encryption_params, media_set_t* media_set, - vod_array_t* segments_sizes, - void* context); + uint32_t* bandwidth, + uint32_t* avg_bandwidth); #endif // __HLS_MUXER_H__ diff --git a/vod/hls/m3u8_builder.c b/vod/hls/m3u8_builder.c index be417a35..4d6c1ac5 100644 --- a/vod/hls/m3u8_builder.c +++ b/vod/hls/m3u8_builder.c @@ -1076,8 +1076,6 @@ m3u8_builder_write_variants( media_track_t* tracks[MEDIA_TYPE_COUNT]; media_info_t* video = NULL; media_info_t* audio = NULL; - uint32_t bitrate; - uint32_t avg_bitrate; uint32_t muxed_tracks = adaptation_set->type == ADAPTATION_TYPE_MUXED ? MEDIA_TYPE_COUNT : 1; vod_memzero(tracks, sizeof(tracks)); @@ -1086,8 +1084,11 @@ m3u8_builder_write_variants( cur_track_ptr < adaptation_set->last; cur_track_ptr += muxed_tracks) { - vod_array_t segments_sizes; - hls_muxer_simulate_get_segment_sizes(request_context, segment_durations, muxer_conf, encryption_params, media_set, &segments_sizes, request_context); + uint32_t bandwidth; + uint32_t avg_bandwidth; + hls_muxer_simulate_get_segment_sizes(request_context, segment_durations, muxer_conf, encryption_params, media_set, &bandwidth, &avg_bandwidth); + vod_log_error(VOD_LOG_ERR, request_context->log, 0, + "m3u8_builder_write_variants: %L %L", bandwidth, avg_bandwidth); // get the audio / video tracks if (muxed_tracks == MEDIA_TYPE_COUNT) @@ -1105,8 +1106,6 @@ m3u8_builder_write_variants( if (tracks[MEDIA_TYPE_VIDEO] != NULL) { video = &tracks[MEDIA_TYPE_VIDEO]->media_info; - bitrate = video->bitrate; - avg_bitrate = video->avg_bitrate; if (group_audio_track != NULL) { audio = &group_audio_track->media_info; @@ -1120,17 +1119,9 @@ m3u8_builder_write_variants( audio = NULL; } - if (audio != NULL) - { - bitrate += audio->bitrate; - if (avg_bitrate != 0) - { - avg_bitrate += audio->avg_bitrate; - } - } p = vod_sprintf(p, m3u8_stream_inf_video, - bitrate, + bandwidth, (uint32_t)video->u.video.width, (uint32_t)video->u.video.height, (uint32_t)(video->timescale / video->min_frame_duration), @@ -1153,16 +1144,14 @@ m3u8_builder_write_variants( audio = &tracks[MEDIA_TYPE_AUDIO]->media_info; } - avg_bitrate = audio->avg_bitrate; - p = vod_sprintf(p, m3u8_stream_inf_audio, audio->bitrate, &audio->codec_name); + + p = vod_sprintf(p, m3u8_stream_inf_audio, bandwidth, &audio->codec_name); } *p++ = '\"'; - if (avg_bitrate != 0) - { - p = vod_sprintf(p, m3u8_average_bandwidth, avg_bitrate); - } + p = vod_sprintf(p, m3u8_average_bandwidth, avg_bandwidth); + if (tracks[MEDIA_TYPE_VIDEO] != NULL) { From 2013caee45c6c4a189d4f04c2b2478e77ea5fd3f Mon Sep 17 00:00:00 2001 From: Sebastian Annies Date: Wed, 21 Sep 2022 12:27:36 +0200 Subject: [PATCH 3/7] get the parse flags right --- ngx_http_vod_hls.c | 42 ++++++++++++++++++++------------------ vod/hds/hds_amf0_encoder.c | 3 --- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/ngx_http_vod_hls.c b/ngx_http_vod_hls.c index 488459fb..1384ca7d 100644 --- a/ngx_http_vod_hls.c +++ b/ngx_http_vod_hls.c @@ -97,7 +97,7 @@ ngx_http_vod_hls_init_encryption_iv(u_char* iv, uint32_t segment_index) static ngx_int_t ngx_http_vod_hls_get_iv_seed( - ngx_http_vod_submodule_context_t* submodule_context, + ngx_http_vod_submodule_context_t* submodule_context, media_sequence_t* sequence, ngx_str_t* result) { @@ -318,11 +318,11 @@ ngx_http_vod_hls_handle_master_playlist( content_type->data = m3u8_content_type; content_type->len = sizeof(m3u8_content_type) - 1; - + return NGX_OK; } -static ngx_int_t +static ngx_int_t ngx_http_vod_hls_handle_index_playlist( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, @@ -362,7 +362,7 @@ ngx_http_vod_hls_handle_index_playlist( } container_format = ngx_http_vod_hls_get_container_format( - &conf->hls, + &conf->hls, &submodule_context->media_set); #if (NGX_HAVE_OPENSSL_EVP) @@ -413,7 +413,7 @@ ngx_http_vod_hls_handle_index_playlist( content_type->data = m3u8_content_type; content_type->len = sizeof(m3u8_content_type) - 1; - + return NGX_OK; } @@ -426,7 +426,7 @@ ngx_http_vod_hls_handle_iframe_playlist( ngx_http_vod_loc_conf_t* conf = submodule_context->conf; ngx_str_t base_url = ngx_null_string; vod_status_t rc; - + if (conf->hls.encryption_method != HLS_ENC_NONE) { ngx_log_error(NGX_LOG_ERR, submodule_context->request_context.log, 0, @@ -475,7 +475,7 @@ ngx_http_vod_hls_handle_iframe_playlist( content_type->data = m3u8_content_type; content_type->len = sizeof(m3u8_content_type) - 1; - + return NGX_OK; } @@ -554,7 +554,7 @@ ngx_http_vod_hls_init_ts_frame_processor( segment_writer->write_tail, segment_writer->context, reuse_output_buffers, - response_size, + response_size, output_buffer, &state); if (rc != VOD_OK) @@ -564,7 +564,7 @@ ngx_http_vod_hls_init_ts_frame_processor( return ngx_http_vod_status_to_ngx_error(submodule_context->r, rc); } - if (encryption_params.type == HLS_ENC_AES_128 && + if (encryption_params.type == HLS_ENC_AES_128 && *response_size != 0) { *response_size = aes_round_up_to_block(*response_size); @@ -886,7 +886,7 @@ ngx_http_vod_hls_handle_vtt_segment( ngx_str_t* content_type) { vod_status_t rc; - + rc = webvtt_builder_build( &submodule_context->request_context, &submodule_context->media_set, @@ -905,14 +905,16 @@ ngx_http_vod_hls_handle_vtt_segment( return NGX_OK; } + static const ngx_http_vod_request_t hls_master_request = { - REQUEST_FLAG_SINGLE_TRACK_PER_MEDIA_TYPE | REQUEST_FLAG_PARSE_ALL_CLIPS, - PARSE_FLAG_FRAMES_ALL_EXCEPT_OFFSETS | PARSE_FLAG_PARSED_EXTRA_DATA_SIZE, - REQUEST_CLASS_OTHER, - SUPPORTED_CODECS | VOD_CODEC_FLAG(WEBVTT), - HLS_TIMESCALE, - ngx_http_vod_hls_handle_master_playlist, - NULL, + REQUEST_FLAG_PARSE_ALL_CLIPS, + PARSE_FLAG_DURATION_LIMITS_AND_TOTAL_SIZE | PARSE_FLAG_KEY_FRAME_BITRATE | PARSE_FLAG_CODEC_NAME | + PARSE_FLAG_PARSED_EXTRA_DATA_SIZE | PARSE_FLAG_CODEC_TRANSFER_CHAR | PARSE_FLAG_FRAMES_ALL_EXCEPT_OFFSETS, + REQUEST_CLASS_OTHER, + SUPPORTED_CODECS | VOD_CODEC_FLAG(WEBVTT), + HLS_TIMESCALE, + ngx_http_vod_hls_handle_master_playlist, + NULL, }; static const ngx_http_vod_request_t hls_index_request = { @@ -1055,12 +1057,12 @@ ngx_http_vod_hls_merge_loc_conf( ngx_conf_merge_value(conf->mpegts_muxer_config.align_frames, prev->mpegts_muxer_config.align_frames, 1); ngx_conf_merge_value(conf->mpegts_muxer_config.output_id3_timestamps, prev->mpegts_muxer_config.output_id3_timestamps, 0); ngx_conf_merge_value(conf->mpegts_muxer_config.align_pts, prev->mpegts_muxer_config.align_pts, 0); - + ngx_conf_merge_uint_value(conf->encryption_method, prev->encryption_method, HLS_ENC_NONE); m3u8_builder_init_config( &conf->m3u8_config, - base->segmenter.max_segment_duration, + base->segmenter.max_segment_duration, conf->encryption_method); switch (conf->encryption_method) @@ -1091,7 +1093,7 @@ ngx_http_vod_hls_merge_loc_conf( return NGX_CONF_OK; } -static int +static int ngx_http_vod_hls_get_file_path_components(ngx_str_t* uri) { return 1; diff --git a/vod/hds/hds_amf0_encoder.c b/vod/hds/hds_amf0_encoder.c index d20b79d2..f587b1c4 100644 --- a/vod/hds/hds_amf0_encoder.c +++ b/vod/hds/hds_amf0_encoder.c @@ -127,7 +127,6 @@ hds_amf0_write_metadata(u_char* p, media_set_t* media_set, media_track_t** track uint64_t duration; uint32_t timescale; uint32_t count; - uint32_t bitrate = 0; uint8_t sound_format; count = AMF0_COMMON_FIELDS_COUNT; @@ -155,7 +154,6 @@ hds_amf0_write_metadata(u_char* p, media_set_t* media_set, media_track_t** track if (tracks[MEDIA_TYPE_VIDEO] != NULL) { media_info = &tracks[MEDIA_TYPE_VIDEO]->media_info; - bitrate += media_info->bitrate; p = hds_amf0_append_array_number_value(p, &amf0_width, media_info->u.video.width); p = hds_amf0_append_array_number_value(p, &amf0_height, media_info->u.video.height); p = hds_amf0_append_array_number_value(p, &amf0_videodatarate, (double)media_info->bitrate / 1000.0); @@ -166,7 +164,6 @@ hds_amf0_write_metadata(u_char* p, media_set_t* media_set, media_track_t** track if (tracks[MEDIA_TYPE_AUDIO] != NULL) { media_info = &tracks[MEDIA_TYPE_AUDIO]->media_info; - bitrate += media_info->bitrate; p = hds_amf0_append_array_number_value(p, &amf0_audiodatarate, (double)media_info->bitrate / 1000.0); p = hds_amf0_append_array_number_value(p, &amf0_audiosamplerate, media_info->u.audio.sample_rate); p = hds_amf0_append_array_number_value(p, &amf0_audiosamplesize, media_info->u.audio.bits_per_sample); From e21443ccb25fbe92c0066ffa616e7c6db7d45290 Mon Sep 17 00:00:00 2001 From: Sebastian Annies Date: Wed, 21 Sep 2022 15:40:01 +0200 Subject: [PATCH 4/7] got the actual numbers right --- vod/hls/hls_muxer.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/vod/hls/hls_muxer.c b/vod/hls/hls_muxer.c index 2d67b3db..22fb7e79 100644 --- a/vod/hls/hls_muxer.c +++ b/vod/hls/hls_muxer.c @@ -1366,6 +1366,10 @@ hls_muxer_simulate_get_segment_sizes( return rc; } // update the limit for the next segment + segment_bandwidth[segment_index] = state.queue.cur_offset / ((float)(cur_item->duration) / (float)(segment_durations->timescale)); + vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, + "hls_muxer_simulate_get_segment_sizes: segment %12L %12L %12f %12f %12f", segment_index + 1, state.queue.cur_offset * 8, (float)(cur_item->duration) / (float)(segment_durations->timescale) , (state.queue.cur_offset * 8) / ((float)(cur_item->duration) / (float)(segment_durations->timescale)), segment_bandwidth[segment_index]); + if (repeat_count <= 0) { cur_item++; @@ -1383,8 +1387,6 @@ hls_muxer_simulate_get_segment_sizes( } repeat_count--; - vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, - "hls_muxer_simulate_get_segment_sizes: %L %L", segment_end, cur_item->duration); segment_end += cur_item->duration; @@ -1398,10 +1400,6 @@ hls_muxer_simulate_get_segment_sizes( } - vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, - "hls_muxer_simulate_get_segment_sizes: segment %L %L", segment_index + 1, state.queue.cur_offset ); - segment_bandwidth[segment_index] = cur_item->duration > 0 ? (state.queue.cur_offset / ((float)cur_item->duration/(float)segment_durations->timescale)): -1; - mpegts_encoder_simulated_start_segment(&state.queue); @@ -1458,8 +1456,9 @@ hls_muxer_simulate_get_segment_sizes( sum_segments += segment_bandwidth[i]; valid_segments++; } - vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, - "hls_muxer_simulate_get_segment_sizes: segment %L %L kbit/s", i + 1, segment_bandwidth[i] / 1024 * 8); + +// vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, +// "hls_muxer_simulate_get_segment_sizes: segment %L %L kbit/s", i + 1, segment_bandwidth[i] / 1024 * 8); } *avg_bandwidth = sum_segments / valid_segments * 8 ; From 708010010018b9375ea6951c92453d2e254569ba Mon Sep 17 00:00:00 2001 From: Sebastian Annies Date: Thu, 22 Sep 2022 12:00:52 +0200 Subject: [PATCH 5/7] restrict media_set to current track I don't believe it's right what I'm doing here, but it works. --- vod/hls/m3u8_builder.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/vod/hls/m3u8_builder.c b/vod/hls/m3u8_builder.c index 4d6c1ac5..0ab8c7b4 100644 --- a/vod/hls/m3u8_builder.c +++ b/vod/hls/m3u8_builder.c @@ -1086,9 +1086,7 @@ m3u8_builder_write_variants( { uint32_t bandwidth; uint32_t avg_bandwidth; - hls_muxer_simulate_get_segment_sizes(request_context, segment_durations, muxer_conf, encryption_params, media_set, &bandwidth, &avg_bandwidth); - vod_log_error(VOD_LOG_ERR, request_context->log, 0, - "m3u8_builder_write_variants: %L %L", bandwidth, avg_bandwidth); + // get the audio / video tracks if (muxed_tracks == MEDIA_TYPE_COUNT) @@ -1102,7 +1100,16 @@ m3u8_builder_write_variants( tracks[adaptation_set->type] = cur_track_ptr[0]; } - // output EXT-X-STREAM-INF + + media_set->filtered_tracks = cur_track_ptr[0]; + media_set->filtered_tracks_end = cur_track_ptr[0]; + media_set->total_track_count = 1; + hls_muxer_simulate_get_segment_sizes(request_context, segment_durations, muxer_conf, encryption_params, media_set, &bandwidth, &avg_bandwidth); + vod_log_error(VOD_LOG_ERR, request_context->log, 0, + "m3u8_builder_write_variants (bandwidth avg_bandwidth): %L %L", bandwidth, avg_bandwidth); + + + // output EXT-X-STREAM-INF if (tracks[MEDIA_TYPE_VIDEO] != NULL) { video = &tracks[MEDIA_TYPE_VIDEO]->media_info; From 7a7f8efb8ae12186b33c6165e9548d7a144c5d3c Mon Sep 17 00:00:00 2001 From: Sebastian Annies Date: Thu, 22 Sep 2022 12:02:33 +0200 Subject: [PATCH 6/7] reduce logging --- vod/hls/hls_muxer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vod/hls/hls_muxer.c b/vod/hls/hls_muxer.c index 22fb7e79..db32b692 100644 --- a/vod/hls/hls_muxer.c +++ b/vod/hls/hls_muxer.c @@ -1367,8 +1367,8 @@ hls_muxer_simulate_get_segment_sizes( } // update the limit for the next segment segment_bandwidth[segment_index] = state.queue.cur_offset / ((float)(cur_item->duration) / (float)(segment_durations->timescale)); - vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, - "hls_muxer_simulate_get_segment_sizes: segment %12L %12L %12f %12f %12f", segment_index + 1, state.queue.cur_offset * 8, (float)(cur_item->duration) / (float)(segment_durations->timescale) , (state.queue.cur_offset * 8) / ((float)(cur_item->duration) / (float)(segment_durations->timescale)), segment_bandwidth[segment_index]); +// vod_log_error(VOD_LOG_ERR, state.request_context->log, 0, +// "hls_muxer_simulate_get_segment_sizes: segment %12L %12L %12f %12f %12f", segment_index + 1, state.queue.cur_offset * 8, (float)(cur_item->duration) / (float)(segment_durations->timescale) , (state.queue.cur_offset * 8) / ((float)(cur_item->duration) / (float)(segment_durations->timescale)), segment_bandwidth[segment_index]); if (repeat_count <= 0) { From 2895ef3c420c0d12911304adc403a34f861b1196 Mon Sep 17 00:00:00 2001 From: Sebastian Annies Date: Fri, 30 Sep 2022 13:44:49 +0200 Subject: [PATCH 7/7] fix compile with openssl --- ngx_http_vod_hls.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ngx_http_vod_hls.c b/ngx_http_vod_hls.c index 1384ca7d..db484921 100644 --- a/ngx_http_vod_hls.c +++ b/ngx_http_vod_hls.c @@ -261,7 +261,14 @@ ngx_http_vod_hls_handle_master_playlist( hls_encryption_params_t encryption_params; vod_status_t rc; + ngx_uint_t container_format; + + + #if (NGX_HAVE_OPENSSL_EVP) + container_format = ngx_http_vod_hls_get_container_format( + &conf->hls, + &submodule_context->media_set); rc = ngx_http_vod_hls_init_encryption_params(&encryption_params, submodule_context, container_format); if (rc != NGX_OK) {