static ngx_int_t ngx_http_vod_hls_handle_master_playlist( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { ngx_str_t base_url = ngx_null_string; vod_status_t rc; if (submodule_context->conf->hls.absolute_master_urls) { ngx_http_vod_get_base_url(submodule_context->r, &submodule_context->conf->https_header_name, NULL, 0, &empty_string, &base_url); } rc = m3u8_builder_build_master_playlist( &submodule_context->request_context, &submodule_context->conf->hls.m3u8_config, &base_url, &submodule_context->media_set, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hls_handle_master_playlist: m3u8_builder_build_master_playlist failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } content_type->data = m3u8_content_type; content_type->len = sizeof(m3u8_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_hds_handle_manifest( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { vod_status_t rc; rc = hds_packager_build_manifest( &submodule_context->request_context, &submodule_context->conf->hds.manifest_config, &submodule_context->r->uri, &submodule_context->conf->segmenter, &submodule_context->media_set, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hds_handle_manifest: hds_packager_build_manifest failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } content_type->data = f4m_content_type; content_type->len = sizeof(f4m_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_dash_handle_manifest( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { ngx_str_t base_url = ngx_null_string; vod_status_t rc; ngx_str_t file_uri; if (submodule_context->conf->dash.absolute_manifest_urls) { if (submodule_context->conf->dash.mpd_config.manifest_format == FORMAT_SEGMENT_LIST) { file_uri.data = NULL; file_uri.len = 0; } else { file_uri = submodule_context->r->uri; } ngx_http_vod_get_base_url(submodule_context->r, &submodule_context->conf->https_header_name, NULL, 0, &file_uri, &base_url); } if (submodule_context->conf->drm_enabled) { rc = edash_packager_build_mpd( &submodule_context->request_context, &submodule_context->conf->dash.mpd_config, &base_url, &submodule_context->media_set, response); } else { rc = dash_packager_build_mpd( &submodule_context->request_context, &submodule_context->conf->dash.mpd_config, &base_url, &submodule_context->media_set, 0, NULL, NULL, response); } if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_dash_handle_manifest: (e)dash_packager_build_mpd failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } content_type->data = mpd_content_type; content_type->len = sizeof(mpd_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_hds_init_frame_processor( ngx_http_vod_submodule_context_t* submodule_context, segment_writer_t* segment_writer, ngx_http_vod_frame_processor_t* frame_processor, void** frame_processor_state, ngx_str_t* output_buffer, size_t* response_size, ngx_str_t* content_type) { hds_muxer_state_t* state; hds_encryption_params_t encryption_params; vod_status_t rc; drm_info_t* drm_info; if (submodule_context->conf->drm_enabled) { drm_info = submodule_context->media_set.sequences[0].drm_info; encryption_params.type = HDS_ENC_SELECTIVE; encryption_params.key = drm_info->key; encryption_params.iv = submodule_context->media_set.sequences[0].encryption_key; } else { encryption_params.type = HDS_ENC_NONE; } rc = hds_muxer_init_fragment( &submodule_context->request_context, &submodule_context->conf->hds.fragment_config, &encryption_params, submodule_context->request_params.segment_index, &submodule_context->media_set, segment_writer->write_tail, segment_writer->context, ngx_http_vod_submodule_size_only(submodule_context), output_buffer, response_size, &state); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hds_init_frame_processor: hds_muxer_init_fragment failed %i", rc); return ngx_http_vod_status_to_ngx_error(submodule_context->r, rc); } *frame_processor = (ngx_http_vod_frame_processor_t)hds_muxer_process_frames; *frame_processor_state = state; // set the 'Content-type' header content_type->len = sizeof(f4f_content_type) - 1; content_type->data = (u_char *)f4f_content_type; return NGX_OK; }
static ngx_int_t ngx_http_vod_hls_handle_iframe_playlist( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { 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, "ngx_http_vod_hls_handle_iframe_playlist: iframes playlist not supported with encryption"); return NGX_HTTP_BAD_REQUEST; } if (submodule_context->media_set.audio_filtering_needed) { ngx_log_error(NGX_LOG_ERR, submodule_context->request_context.log, 0, "ngx_http_vod_hls_handle_iframe_playlist: iframes playlist not supported with audio filtering"); return NGX_HTTP_BAD_REQUEST; } if (conf->hls.absolute_iframe_urls) { rc = ngx_http_vod_get_base_url(submodule_context->r, conf->base_url, &submodule_context->r->uri, &base_url); if (rc != NGX_OK) { return rc; } } rc = m3u8_builder_build_iframe_playlist( &submodule_context->request_context, &conf->hls.m3u8_config, &conf->hls.muxer_config, &base_url, &submodule_context->request_params, &submodule_context->media_set, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hls_handle_iframe_playlist: m3u8_builder_build_iframe_playlist failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } content_type->data = m3u8_content_type; content_type->len = sizeof(m3u8_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_hls_init_frame_processor( ngx_http_vod_submodule_context_t* submodule_context, read_cache_state_t* read_cache_state, segment_writer_t* segment_writer, ngx_http_vod_frame_processor_t* frame_processor, void** frame_processor_state, ngx_str_t* output_buffer, size_t* response_size, ngx_str_t* content_type) { hls_muxer_state_t* state; vod_status_t rc; bool_t simulation_supported; state = ngx_pcalloc(submodule_context->request_context.pool, sizeof(hls_muxer_state_t)); if (state == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hls_init_frame_processor: ngx_pcalloc failed"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } rc = hls_muxer_init( state, &submodule_context->request_context, submodule_context->request_params.segment_index, &submodule_context->mpeg_metadata, read_cache_state, segment_writer->write_tail, segment_writer->context, &simulation_supported); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hls_init_frame_processor: hls_muxer_init failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } if (simulation_supported) { *response_size = hls_muxer_simulate_get_segment_size(state); hls_muxer_simulation_reset(state); } *frame_processor = (ngx_http_vod_frame_processor_t)hls_muxer_process; *frame_processor_state = state; content_type->len = sizeof(mpeg_ts_content_type) - 1; content_type->data = (u_char *)mpeg_ts_content_type; return NGX_OK; }
static ngx_int_t ngx_http_vod_hls_handle_index_playlist( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { hls_encryption_params_t encryption_params; ngx_str_t segments_base_url = ngx_null_string; ngx_str_t base_url = ngx_null_string; vod_status_t rc; u_char iv[AES_BLOCK_SIZE]; if (submodule_context->conf->hls.absolute_index_urls) { ngx_http_vod_get_base_url(submodule_context->r, &submodule_context->conf->https_header_name, NULL, 0, &submodule_context->r->uri, &base_url); ngx_http_vod_get_base_url( submodule_context->r, &submodule_context->conf->https_header_name, &submodule_context->conf->segments_base_url, submodule_context->conf->segments_base_url_has_scheme, &submodule_context->r->uri, &segments_base_url); } ngx_http_vod_hls_init_encryption_params(&encryption_params, submodule_context, iv); rc = m3u8_builder_build_index_playlist( &submodule_context->request_context, &submodule_context->conf->hls.m3u8_config, &base_url, &segments_base_url, &submodule_context->request_params, &encryption_params, &submodule_context->conf->segmenter, &submodule_context->media_set, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hls_handle_index_playlist: m3u8_builder_build_index_playlist failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } content_type->data = m3u8_content_type; content_type->len = sizeof(m3u8_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_dash_handle_init_segment( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { vod_status_t rc; if (submodule_context->conf->drm_enabled) { rc = edash_packager_build_init_mp4( &submodule_context->request_context, &submodule_context->media_set, submodule_context->conf->drm_clear_lead_segment_count > 0, ngx_http_vod_submodule_size_only(submodule_context), response); } else { rc = dash_packager_build_init_mp4( &submodule_context->request_context, &submodule_context->media_set, ngx_http_vod_submodule_size_only(submodule_context), NULL, NULL, response); } if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_dash_handle_init_segment: (e)dash_packager_build_init_mp4 failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } if (submodule_context->media_set.track_count[MEDIA_TYPE_VIDEO] != 0) { content_type->data = mp4_video_content_type; content_type->len = sizeof(mp4_video_content_type) - 1; } else { content_type->data = mp4_audio_content_type; content_type->len = sizeof(mp4_audio_content_type) - 1; } return NGX_OK; }
static ngx_int_t ngx_http_vod_hls_handle_iframe_playlist( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { mpeg_stream_metadata_t* cur_stream; ngx_str_t base_url = ngx_null_string; vod_status_t rc; for (cur_stream = submodule_context->mpeg_metadata.first_stream; cur_stream < submodule_context->mpeg_metadata.last_stream; cur_stream++) { if (cur_stream->media_info.media_type == MEDIA_TYPE_AUDIO && cur_stream->media_info.speed_nom != cur_stream->media_info.speed_denom) { ngx_log_error(NGX_LOG_ERR, submodule_context->request_context.log, 0, "ngx_http_vod_hls_handle_iframe_playlist: iframes playlist not supported with audio speed change"); return NGX_HTTP_BAD_REQUEST; } } if (submodule_context->conf->hls.absolute_iframe_urls) { ngx_http_vod_get_base_url(submodule_context->r, &submodule_context->conf->https_header_name, NULL, 0, &submodule_context->r->uri, &base_url); } rc = m3u8_builder_build_iframe_playlist( &submodule_context->request_context, &submodule_context->conf->hls.m3u8_config, &base_url, submodule_context->request_params.uses_multi_uri && submodule_context->request_params.required_files != 0xffffffff, &submodule_context->conf->segmenter, &submodule_context->mpeg_metadata, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hls_handle_iframe_playlist: m3u8_builder_build_iframe_playlist failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } content_type->data = m3u8_content_type; content_type->len = sizeof(m3u8_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_hls_handle_index_playlist( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { ngx_str_t segments_base_url = ngx_null_string; ngx_str_t base_url = ngx_null_string; vod_status_t rc; if (submodule_context->conf->hls.absolute_index_urls) { ngx_http_vod_get_base_url(submodule_context->r, &submodule_context->conf->https_header_name, NULL, 0, &submodule_context->r->uri, &base_url); ngx_http_vod_get_base_url( submodule_context->r, &submodule_context->conf->https_header_name, &submodule_context->conf->segments_base_url, submodule_context->conf->segments_base_url_has_scheme, &submodule_context->r->uri, &segments_base_url); } rc = m3u8_builder_build_index_playlist( &submodule_context->request_context, &submodule_context->conf->hls.m3u8_config, &base_url, &segments_base_url, submodule_context->request_params.uses_multi_uri && submodule_context->request_params.required_files != 0xffffffff, submodule_context->conf->secret_key.len != 0, &submodule_context->conf->segmenter, &submodule_context->mpeg_metadata, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hls_handle_index_playlist: m3u8_builder_build_index_playlist failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } content_type->data = m3u8_content_type; content_type->len = sizeof(m3u8_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_dash_webm_init_frame_processor( ngx_http_vod_submodule_context_t* submodule_context, segment_writer_t* segment_writer, ngx_http_vod_frame_processor_t* frame_processor, void** frame_processor_state, ngx_str_t* output_buffer, size_t* response_size, ngx_str_t* content_type) { vod_status_t rc; rc = mkv_builder_frame_writer_init( &submodule_context->request_context, submodule_context->media_set.sequences, segment_writer->write_tail, segment_writer->context, FALSE, output_buffer, response_size, frame_processor_state); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_dash_webm_init_frame_processor: mkv_builder_frame_writer_init failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } *frame_processor = (ngx_http_vod_frame_processor_t)mkv_builder_frame_writer_process; // set the 'Content-type' header if (submodule_context->media_set.track_count[MEDIA_TYPE_VIDEO] != 0) { content_type->len = sizeof(webm_video_content_type) - 1; content_type->data = (u_char *)webm_video_content_type; } else { content_type->len = sizeof(webm_audio_content_type) - 1; content_type->data = (u_char *)webm_audio_content_type; } return NGX_OK; }
static ngx_int_t ngx_http_vod_dash_webm_handle_init_segment( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { vod_status_t rc; ngx_md5_t md5; ngx_str_t* uri; uint64_t track_uid; u_char uri_key[16]; // calculate a uid for track based on the request uri uri = &submodule_context->r->uri; ngx_md5_init(&md5); ngx_md5_update(&md5, uri->data, uri->len); ngx_md5_final(uri_key, &md5); ngx_memcpy(&track_uid, uri_key, sizeof(track_uid)); rc = mkv_build_init_segment( &submodule_context->request_context, submodule_context->media_set.sequences[0].filtered_clips[0].first_track, track_uid, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_dash_webm_handle_init_segment: mkv_build_init_segment failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } if (submodule_context->media_set.track_count[MEDIA_TYPE_VIDEO] != 0) { content_type->data = webm_video_content_type; content_type->len = sizeof(webm_video_content_type) - 1; } else { content_type->data = webm_audio_content_type; content_type->len = sizeof(webm_audio_content_type) - 1; } return NGX_OK; }
static ngx_int_t ngx_http_vod_hls_init_ts_frame_processor( ngx_http_vod_submodule_context_t* submodule_context, segment_writer_t* segment_writer, ngx_http_vod_frame_processor_t* frame_processor, void** frame_processor_state, ngx_str_t* output_buffer, size_t* response_size, ngx_str_t* content_type) { hls_encryption_params_t encryption_params; hls_muxer_state_t* state; vod_status_t rc; u_char iv[AES_BLOCK_SIZE]; ngx_http_vod_hls_init_encryption_params(&encryption_params, submodule_context, iv); rc = hls_muxer_init_segment( &submodule_context->request_context, &submodule_context->conf->hls.muxer_config, &encryption_params, submodule_context->request_params.segment_index, &submodule_context->media_set, segment_writer->write_tail, segment_writer->context, response_size, output_buffer, &state); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hls_init_ts_frame_processor: hls_muxer_init failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } *frame_processor = (ngx_http_vod_frame_processor_t)hls_muxer_process; *frame_processor_state = state; content_type->len = sizeof(mpeg_ts_content_type) - 1; content_type->data = (u_char *)mpeg_ts_content_type; return NGX_OK; }
static ngx_int_t ngx_http_vod_hds_init_frame_processor( ngx_http_vod_submodule_context_t* submodule_context, segment_writer_t* segment_writer, ngx_http_vod_frame_processor_t* frame_processor, void** frame_processor_state, ngx_str_t* output_buffer, size_t* response_size, ngx_str_t* content_type) { hds_muxer_state_t* state; vod_status_t rc; rc = hds_muxer_init_fragment( &submodule_context->request_context, &submodule_context->conf->hds.fragment_config, submodule_context->request_params.segment_index, submodule_context->media_set.sequences, segment_writer->write_tail, segment_writer->context, ngx_http_vod_submodule_size_only(submodule_context), output_buffer, response_size, &state); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hds_init_frame_processor: hds_muxer_init_fragment failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } *frame_processor = (ngx_http_vod_frame_processor_t)hds_muxer_process_frames; *frame_processor_state = state; // set the 'Content-type' header content_type->len = sizeof(f4f_content_type) - 1; content_type->data = (u_char *)f4f_content_type; return NGX_OK; }
static ngx_int_t ngx_http_vod_mss_handle_manifest( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { vod_status_t rc; if (submodule_context->conf->drm_enabled) { rc = mss_playready_build_manifest( &submodule_context->request_context, &submodule_context->conf->segmenter, &submodule_context->mpeg_metadata, response); } else { rc = mss_packager_build_manifest( &submodule_context->request_context, &submodule_context->conf->segmenter, &submodule_context->mpeg_metadata, 0, NULL, NULL, response); } if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_mss_handle_manifest: mss_packager_build_manifest failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } content_type->data = manifest_content_type; content_type->len = sizeof(manifest_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_hds_handle_manifest( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { ngx_http_vod_loc_conf_t* conf = submodule_context->conf; ngx_str_t base_url = ngx_null_string; vod_status_t rc; if (conf->hds.absolute_manifest_urls) { rc = ngx_http_vod_get_base_url(submodule_context->r, conf->base_url, &submodule_context->r->uri, &base_url); if (rc != NGX_OK) { return rc; } } rc = hds_packager_build_manifest( &submodule_context->request_context, &conf->hds.manifest_config, &base_url, &submodule_context->r->uri, &submodule_context->media_set, conf->drm_enabled, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hds_handle_manifest: hds_packager_build_manifest failed %i", rc); return ngx_http_vod_status_to_ngx_error(submodule_context->r, rc); } content_type->data = f4m_content_type; content_type->len = sizeof(f4m_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_hls_handle_vtt_segment( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { vod_status_t rc; rc = webvtt_builder_build( &submodule_context->request_context, &submodule_context->media_set, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hls_handle_vtt_segment: webvtt_builder_build failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } content_type->len = sizeof(vtt_content_type) - 1; content_type->data = (u_char *)vtt_content_type; return NGX_OK; }
static ngx_int_t ngx_http_vod_hds_handle_bootstrap( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { vod_status_t rc; rc = hds_packager_build_bootstrap( &submodule_context->request_context, &submodule_context->media_set, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hds_handle_bootstrap: hds_packager_build_bootstrap failed %i", rc); return ngx_http_vod_status_to_ngx_error(submodule_context->r, rc); } content_type->data = abst_content_type; content_type->len = sizeof(abst_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_mss_init_frame_processor( ngx_http_vod_submodule_context_t* submodule_context, read_cache_state_t* read_cache_state, segment_writer_t* segment_writer, ngx_http_vod_frame_processor_t* frame_processor, void** frame_processor_state, ngx_str_t* output_buffer, size_t* response_size, ngx_str_t* content_type) { fragment_writer_state_t* state; segment_writer_t drm_writer; vod_status_t rc; bool_t size_only = ngx_http_vod_submodule_size_only(submodule_context); if (submodule_context->conf->drm_enabled) { rc = mss_playready_get_fragment_writer( &drm_writer, &submodule_context->request_context, submodule_context->mpeg_metadata.first_stream, submodule_context->request_params.segment_index, segment_writer, submodule_context->cur_suburi->file_key, // iv size_only, output_buffer, response_size); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_mss_init_frame_processor: mss_playready_get_fragment_writer failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } segment_writer = &drm_writer; } else { rc = mss_packager_build_fragment_header( &submodule_context->request_context, submodule_context->mpeg_metadata.first_stream, submodule_context->request_params.segment_index, 0, NULL, NULL, size_only, output_buffer, response_size); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_mss_init_frame_processor: mss_packager_build_fragment_header failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } } if (!size_only || *response_size == 0) { rc = mp4_builder_frame_writer_init( &submodule_context->request_context, submodule_context->mpeg_metadata.first_stream, read_cache_state, segment_writer->write_tail, segment_writer->context, &state); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_mss_init_frame_processor: mp4_builder_frame_writer_init failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } *frame_processor = (ngx_http_vod_frame_processor_t)mp4_builder_frame_writer_process; *frame_processor_state = state; } // set the 'Content-type' header if (submodule_context->mpeg_metadata.stream_count[MEDIA_TYPE_VIDEO]) { content_type->len = sizeof(mp4_video_content_type) - 1; content_type->data = mp4_video_content_type; } else { content_type->len = sizeof(mp4_audio_content_type) - 1; content_type->data = mp4_audio_content_type; } return NGX_OK; }
static ngx_int_t ngx_http_vod_hls_handle_index_playlist( ngx_http_vod_submodule_context_t* submodule_context, ngx_str_t* response, ngx_str_t* content_type) { ngx_http_vod_loc_conf_t* conf = submodule_context->conf; hls_encryption_params_t encryption_params; ngx_str_t segments_base_url = ngx_null_string; ngx_str_t base_url = ngx_null_string; vod_status_t rc; u_char iv[AES_BLOCK_SIZE]; if (conf->hls.absolute_index_urls) { rc = ngx_http_vod_get_base_url(submodule_context->r, conf->base_url, &submodule_context->r->uri, &base_url); if (rc != NGX_OK) { return rc; } if (conf->segments_base_url != NULL) { rc = ngx_http_vod_get_base_url( submodule_context->r, conf->segments_base_url, &submodule_context->r->uri, &segments_base_url); if (rc != NGX_OK) { return rc; } } else { segments_base_url = base_url; } } ngx_http_vod_hls_init_encryption_params(&encryption_params, submodule_context, iv); 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; } } rc = m3u8_builder_build_index_playlist( &submodule_context->request_context, &conf->hls.m3u8_config, &base_url, &segments_base_url, &submodule_context->request_params, &encryption_params, &submodule_context->media_set, response); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_hls_handle_index_playlist: m3u8_builder_build_index_playlist failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } content_type->data = m3u8_content_type; content_type->len = sizeof(m3u8_content_type) - 1; return NGX_OK; }
static ngx_int_t ngx_http_vod_parse_multi_uri( ngx_http_request_t* r, ngx_str_t* uri, ngx_str_t* multi_uri_suffix, ngx_http_vod_multi_uri_t* result) { u_char* cur_pos; u_char* end_pos; u_char* last_comma_pos; uint32_t part_index; result->prefix.data = uri->data; result->prefix.len = uri->len; result->postfix.data = NULL; result->postfix.len = 0; if (uri->len < multi_uri_suffix->len || ngx_memcmp(multi_uri_suffix->data, uri->data + uri->len - multi_uri_suffix->len, multi_uri_suffix->len) != 0) { // not a multi uri result->middle_parts[0].data = NULL; result->middle_parts[0].len = 0; result->parts_count = 1; return NGX_OK; } uri->len -= multi_uri_suffix->len; end_pos = uri->data + uri->len; last_comma_pos = NULL; part_index = 0; for (cur_pos = uri->data; cur_pos < end_pos; cur_pos++) { if (*cur_pos != ',') { continue; } if (last_comma_pos == NULL) { result->prefix.len = cur_pos - uri->data; } else { if (part_index >= MAX_SUB_URIS) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_multi_uri: number of url parts exceeds the limit"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->middle_parts[part_index].data = last_comma_pos; result->middle_parts[part_index].len = cur_pos - last_comma_pos; part_index++; } last_comma_pos = cur_pos + 1; } if (last_comma_pos == NULL) { // no commas at all result->postfix.data = NULL; result->postfix.len = 0; } else { // 1 comma or more result->postfix.data = last_comma_pos; result->postfix.len = end_pos - last_comma_pos; } if (part_index == 0) { // no commas at all or a single comma result->middle_parts[0].data = NULL; result->middle_parts[0].len = 0; result->parts_count = 1; } else { // 2 commas or more result->parts_count = part_index; } return NGX_OK; }
static ngx_int_t ngx_http_vod_extract_uri_params( ngx_http_request_t* r, ngx_hash_t* params_hash, ngx_str_t* uri, media_sequence_t* sequence, uint32_t* clip_id, media_clip_source_t* source_clip, media_clip_t** result) { ngx_http_vod_uri_param_def_t* param_def = NULL; request_context_t request_context; media_clip_t* rate_filter = NULL; ngx_uint_t cur_key_hash = 0; ngx_str_t cur_param; ngx_int_t rc; uint32_t parsed_params_mask = 0; uint32_t param_index; u_char param_name[MAX_URI_PARAM_NAME_LEN + 1]; u_char* param_name_end = param_name + sizeof(param_name); u_char* param_name_pos = param_name; u_char* copy_start = uri->data; u_char* cur_pos; u_char* end_pos = uri->data + uri->len; u_char* last_slash = NULL; u_char* p; // set the source defaults vod_memzero(source_clip, sizeof(*source_clip)); source_clip->base.type = MEDIA_CLIP_SOURCE; source_clip->base.id = (*clip_id)++; source_clip->clip_to = UINT_MAX; source_clip->tracks_mask[MEDIA_TYPE_AUDIO] = 0xffffffff; source_clip->tracks_mask[MEDIA_TYPE_VIDEO] = 0xffffffff; source_clip->uri = *uri; source_clip->sequence = sequence; *result = &source_clip->base; // allocate the stripped uri p = ngx_palloc(r->pool, uri->len + 1); if (p == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_extract_uri_params: ngx_palloc failed (1)"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } source_clip->stripped_uri.data = p; for (cur_pos = uri->data; cur_pos <= end_pos; cur_pos++) { if (cur_pos < end_pos && *cur_pos != '/') { if (param_name_pos < param_name_end) { *param_name_pos = ngx_tolower(*cur_pos); cur_key_hash = ngx_hash(cur_key_hash, *param_name_pos); param_name_pos++; } continue; } if (last_slash == NULL) { last_slash = cur_pos; cur_key_hash = 0; param_name_pos = param_name; continue; } if (param_def == NULL) { param_def = ngx_hash_find(params_hash, cur_key_hash, param_name, param_name_pos - param_name); if (param_def != NULL) { p = ngx_copy(p, copy_start, last_slash - copy_start); copy_start = last_slash; } } else { param_index = param_def - uri_param_defs; if ((parsed_params_mask & (1 << param_index)) == 0) // first instance of a param takes priority { parsed_params_mask |= (1 << param_index); cur_param.data = last_slash + 1; cur_param.len = cur_pos - (last_slash + 1); if (param_def->name_conf_offset == offsetof(ngx_http_vod_loc_conf_t, speed_param_name)) { request_context.pool = r->pool; request_context.log = r->connection->log; rc = rate_filter_create_from_string( &request_context, &cur_param, &source_clip->base, &rate_filter); if (rc != VOD_OK) { return ngx_http_vod_status_to_ngx_error(rc); } rate_filter->id = (*clip_id)++; *result = rate_filter; } else { rc = param_def->parser(&cur_param, source_clip, param_def->target_offset); if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_extract_uri_params: %s parser failed %i", param_def->name, rc); return rc; } } } copy_start = cur_pos; param_def = NULL; } last_slash = cur_pos; cur_key_hash = 0; param_name_pos = param_name; } if (source_clip->clip_from >= source_clip->clip_to) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_extract_uri_params: clip from %uD is larger than clip to %uD", source_clip->clip_from, source_clip->clip_to); return NGX_HTTP_BAD_REQUEST; } p = ngx_copy(p, copy_start, end_pos - copy_start); *p = '\0'; source_clip->stripped_uri.len = p - source_clip->stripped_uri.data; source_clip->mapped_uri = source_clip->stripped_uri; return NGX_OK; }
ngx_int_t ngx_http_vod_parse_uri_path( ngx_http_request_t* r, ngx_str_t* multi_uri_suffix, ngx_hash_t* params_hash, ngx_str_t* uri, request_params_t* request_params, media_set_t* media_set) { media_sequence_t* cur_sequence; media_clip_source_t* cur_source; media_clip_source_t* sources_head; ngx_http_vod_multi_uri_t multi_uri; media_clip_t** cur_clip_ptr; media_clip_t* cur_clip; ngx_str_t parts[3]; ngx_str_t cur_uri; ngx_int_t rc; uint32_t sequences_mask; uint32_t parts_mask; uint32_t media_type; uint32_t clip_id = 1; uint32_t i; bool_t has_tracks; int uri_count; media_set->uri = *uri; // must save the uri before calling ngx_http_vod_parse_multi_uri as it may change multi_uri.parts_count = 0; rc = ngx_http_vod_parse_multi_uri(r, uri, multi_uri_suffix, &multi_uri); if (rc != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_path: ngx_http_vod_parse_multi_uri failed %i", rc); return rc; } if (multi_uri.parts_count > 1 && request_params->sequence_ids[0].len == 0) { sequences_mask = request_params->sequences_mask; request_params->sequences_mask = 0xffffffff; // reset the sequences mask so that it won't be applied again on the mapping request } else { sequences_mask = 0xffffffff; } parts_mask = (1 << multi_uri.parts_count) - 1; uri_count = vod_get_number_of_set_bits(sequences_mask & parts_mask); if (uri_count == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_path: request has no uris"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } cur_sequence = ngx_palloc(r->pool, (sizeof(*cur_sequence) + sizeof(*cur_source) + sizeof(*cur_clip_ptr)) * uri_count); if (cur_sequence == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_path: ngx_palloc failed"); return ngx_http_vod_status_to_ngx_error(r, VOD_ALLOC_FAILED); } media_set->sequences = cur_sequence; cur_source = (void*)(cur_sequence + uri_count); cur_clip_ptr = (void*)(cur_source + uri_count); sources_head = NULL; parts[0] = multi_uri.prefix; parts[2] = multi_uri.postfix; for (i = 0; i < multi_uri.parts_count; i++) { if ((sequences_mask & (1 << i)) == 0) { continue; } cur_sequence->id.len = 0; cur_sequence->language = 0; cur_sequence->label.len = 0; cur_sequence->first_key_frame_offset = 0; cur_sequence->key_frame_durations = NULL; cur_sequence->drm_info = NULL; vod_memzero(cur_sequence->bitrate, sizeof(cur_sequence->bitrate)); parts[1] = multi_uri.middle_parts[i]; rc = ngx_http_vod_merge_string_parts(r, parts, 3, &cur_uri); if (rc != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_path: ngx_http_vod_merge_string_parts failed %i", rc); return rc; } rc = ngx_http_vod_extract_uri_params(r, params_hash, &cur_uri, cur_sequence, &clip_id, cur_source, &cur_clip); if (rc != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_path: ngx_http_vod_extract_uri_params failed %i", rc); return rc; } has_tracks = FALSE; for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++) { if ((cur_source->tracks_mask[media_type] & request_params->tracks_mask[media_type]) != 0) { has_tracks = TRUE; break; } } if (!has_tracks) { continue; } *cur_clip_ptr = cur_clip; cur_source->next = sources_head; sources_head = cur_source; cur_sequence->clips = cur_clip_ptr; cur_sequence->index = i; cur_sequence->stripped_uri = cur_source->stripped_uri; cur_sequence->mapped_uri = cur_source->stripped_uri; cur_source++; cur_sequence++; cur_clip_ptr++; } // need to test again since we filtered sub uris that didn't have any required tracks media_set->sequence_count = cur_sequence - media_set->sequences; if (media_set->sequence_count <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_path: request has no uris after track filtering"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } media_set->sources_head = sources_head; media_set->sequences_end = cur_sequence; media_set->has_multi_sequences = (multi_uri.parts_count > 1); media_set->timing.total_count = 1; media_set->clip_count = 1; media_set->presentation_end = TRUE; return NGX_OK; }
static ngx_int_t ngx_http_vod_hds_parse_uri_file_name( ngx_http_request_t *r, ngx_http_vod_loc_conf_t *conf, u_char* start_pos, u_char* end_pos, request_params_t* request_params, const ngx_http_vod_request_t** request) { uint32_t flags = 0; ngx_int_t rc; // fragment request if (ngx_http_vod_starts_with(start_pos, end_pos, &conf->hds.manifest_config.fragment_file_name_prefix)) { // sample fragment file name: frag-f3-v1-a1-Seg1-Frag1 start_pos += conf->hds.manifest_config.fragment_file_name_prefix.len; // parse the fragment index end_pos = ngx_http_vod_extract_uint32_token_reverse(start_pos, end_pos, &request_params->segment_index); if (request_params->segment_index == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_hds_parse_uri_file_name: failed to parse fragment index"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } request_params->segment_index--; // convert to 0-based // extract the '-Seg1-Frag' part end_pos -= sizeof("-Seg1-Frag") - 1; if (end_pos < start_pos || ngx_memcmp(end_pos, "-Seg1-Frag", sizeof("-Seg1-Frag") - 1) != 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_hds_parse_uri_file_name: invalid segment / fragment requested"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } *request = &hds_fragment_request; } // bootstrap request else if (ngx_http_vod_match_prefix_postfix(start_pos, end_pos, &conf->hds.manifest_config.bootstrap_file_name_prefix, bootstrap_file_ext)) { start_pos += conf->hds.manifest_config.bootstrap_file_name_prefix.len; end_pos -= (sizeof(bootstrap_file_ext) - 1); *request = &hds_bootstrap_request; } // manifest request else if (ngx_http_vod_match_prefix_postfix(start_pos, end_pos, &conf->hds.manifest_file_name_prefix, manifest_file_ext)) { start_pos += conf->hds.manifest_file_name_prefix.len; end_pos -= (sizeof(manifest_file_ext) - 1); *request = &hds_manifest_request; flags = PARSE_FILE_NAME_MULTI_STREAMS_PER_TYPE; } else { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_hds_parse_uri_file_name: unidentified request"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } // parse the required tracks string rc = ngx_http_vod_parse_uri_file_name(r, start_pos, end_pos, flags, request_params); if (rc != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_hds_parse_uri_file_name: ngx_http_vod_parse_uri_file_name failed %i", rc); return rc; } return NGX_OK; }
static ngx_int_t ngx_http_vod_dash_init_frame_processor( ngx_http_vod_submodule_context_t* submodule_context, read_cache_state_t* read_cache_state, segment_writer_t* segment_writer, ngx_http_vod_frame_processor_t* frame_processor, void** frame_processor_state, ngx_str_t* output_buffer, size_t* response_size, ngx_str_t* content_type) { dash_fragment_header_extensions_t header_extensions; fragment_writer_state_t* state; segment_writer_t edash_writer; vod_status_t rc; bool_t size_only = ngx_http_vod_submodule_size_only(submodule_context); if (submodule_context->conf->drm_enabled && submodule_context->request_params.segment_index >= submodule_context->conf->drm_clear_lead_segment_count) { // encyrpted fragment rc = edash_packager_get_fragment_writer( &edash_writer, &submodule_context->request_context, &submodule_context->media_set, submodule_context->request_params.segment_index, segment_writer, submodule_context->media_set.sequences[0].encryption_key, // iv size_only, output_buffer, response_size); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_dash_init_frame_processor: edash_packager_get_fragment_writer failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } segment_writer = &edash_writer; } else { // unencrypted ngx_memzero(&header_extensions, sizeof(header_extensions)); rc = dash_packager_build_fragment_header( &submodule_context->request_context, &submodule_context->media_set, submodule_context->request_params.segment_index, submodule_context->conf->drm_enabled ? 2 : 0, // sample description index &header_extensions, size_only, output_buffer, response_size); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_dash_init_frame_processor: dash_packager_build_fragment_header failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } } // initialize the frame processor if (!size_only || *response_size == 0) { rc = mp4_builder_frame_writer_init( &submodule_context->request_context, submodule_context->media_set.sequences, read_cache_state, segment_writer->write_tail, segment_writer->context, &state); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_dash_init_frame_processor: mp4_builder_frame_writer_init failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } *frame_processor = (ngx_http_vod_frame_processor_t)mp4_builder_frame_writer_process; *frame_processor_state = state; } // set the 'Content-type' header if (submodule_context->media_set.track_count[MEDIA_TYPE_VIDEO] != 0) { content_type->len = sizeof(mp4_video_content_type) - 1; content_type->data = (u_char *)mp4_video_content_type; } else { content_type->len = sizeof(mp4_audio_content_type) - 1; content_type->data = (u_char *)mp4_audio_content_type; } return NGX_OK; }
static ngx_int_t ngx_http_vod_mss_init_frame_processor( ngx_http_vod_submodule_context_t* submodule_context, segment_writer_t* segment_writer, ngx_http_vod_frame_processor_t* frame_processor, void** frame_processor_state, ngx_str_t* output_buffer, size_t* response_size, ngx_str_t* content_type) { fragment_writer_state_t* state; segment_writer_t drm_writer; vod_status_t rc; bool_t reuse_buffers = FALSE; bool_t size_only = ngx_http_vod_submodule_size_only(submodule_context); if (submodule_context->conf->drm_enabled) { rc = mss_playready_get_fragment_writer( &drm_writer, &submodule_context->request_context, &submodule_context->media_set, submodule_context->request_params.segment_index, segment_writer, submodule_context->media_set.sequences[0].encryption_key, // iv size_only, output_buffer, response_size); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_mss_init_frame_processor: mss_playready_get_fragment_writer failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } if (drm_writer.write_tail != NULL) { segment_writer = &drm_writer; reuse_buffers = TRUE; // mp4_encrypt allocates new buffers } } else { rc = mss_packager_build_fragment_header( &submodule_context->request_context, &submodule_context->media_set, submodule_context->request_params.segment_index, 0, NULL, NULL, size_only, output_buffer, response_size); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_mss_init_frame_processor: mss_packager_build_fragment_header failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } } if (!size_only || *response_size == 0) { rc = mp4_builder_frame_writer_init( &submodule_context->request_context, submodule_context->media_set.sequences, segment_writer->write_tail, segment_writer->context, reuse_buffers, &state); if (rc != VOD_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, submodule_context->request_context.log, 0, "ngx_http_vod_mss_init_frame_processor: mp4_builder_frame_writer_init failed %i", rc); return ngx_http_vod_status_to_ngx_error(rc); } *frame_processor = (ngx_http_vod_frame_processor_t)mp4_builder_frame_writer_process; *frame_processor_state = state; } // set the 'Content-type' header if (submodule_context->media_set.track_count[MEDIA_TYPE_VIDEO]) { content_type->len = sizeof(mp4_video_content_type) - 1; content_type->data = mp4_video_content_type; } else { content_type->len = sizeof(mp4_audio_content_type) - 1; content_type->data = mp4_audio_content_type; } return NGX_OK; }
ngx_int_t ngx_http_vod_parse_uri_file_name( ngx_http_request_t* r, u_char* start_pos, u_char* end_pos, uint32_t flags, request_params_t* result) { sequence_tracks_mask_t* sequence_tracks_mask_end; sequence_tracks_mask_t* sequence_tracks_mask; ngx_str_t* cur_sequence_id; ngx_str_t* last_sequence_id; uint32_t default_tracks_mask; uint32_t* tracks_mask; uint32_t segment_index_shift; uint32_t sequence_index; uint32_t clip_index; uint32_t media_type; uint32_t pts_delay; uint32_t version; bool_t tracks_mask_updated; language_id_t lang_id; default_tracks_mask = (flags & PARSE_FILE_NAME_MULTI_STREAMS_PER_TYPE) ? 0xffffffff : 1; for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++) { result->tracks_mask[media_type] = default_tracks_mask; } result->sequences_mask = 0xffffffff; result->clip_index = INVALID_CLIP_INDEX; // segment index if ((flags & PARSE_FILE_NAME_EXPECT_SEGMENT_INDEX) != 0) { if (start_pos < end_pos && *start_pos == '-') { start_pos++; // skip the - } start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &result->segment_index); if (result->segment_index <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse segment index"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->segment_index--; // convert to 0-based skip_dash(start_pos, end_pos); // index shift if (*start_pos == 'i') { start_pos++; // skip the i start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &segment_index_shift); if (segment_index_shift <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse segment index shift"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->segment_index += segment_index_shift; skip_dash(start_pos, end_pos); } } else { skip_dash(start_pos, end_pos); } // clip index if (*start_pos == 'c' && (flags & PARSE_FILE_NAME_ALLOW_CLIP_INDEX) != 0) { start_pos++; // skip the c start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &clip_index); if (clip_index <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse clip index"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->clip_index = clip_index - 1; skip_dash(start_pos, end_pos); } // sequence (file) index if (*start_pos == 'f' || *start_pos == 's') { result->sequences_mask = 0; cur_sequence_id = result->sequence_ids; last_sequence_id = cur_sequence_id + MAX_SEQUENCE_IDS; sequence_tracks_mask = NULL; sequence_tracks_mask_end = NULL; tracks_mask_updated = FALSE; for (;;) { if (*start_pos == 'f') { // sequence index start_pos++; // skip the f if (start_pos >= end_pos || *start_pos < '1' || *start_pos > '9') { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: missing index following sequence selector"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } sequence_index = *start_pos - '0'; start_pos++; // skip the digit if (start_pos < end_pos && *start_pos >= '0' && *start_pos <= '9') { sequence_index = sequence_index * 10 + *start_pos - '0'; if (sequence_index > MAX_SEQUENCES) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: sequence index too big"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } start_pos++; // skip the digit } sequence_index--; // Note: sequence_index cannot be 0 here result->sequences_mask |= (1 << sequence_index); } else { // sequence id start_pos++; // skip the s if (cur_sequence_id >= last_sequence_id) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: the number of sequence ids exceeds the limit"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } cur_sequence_id->data = start_pos; while (start_pos < end_pos && *start_pos != '-') { start_pos++; } cur_sequence_id->len = start_pos - cur_sequence_id->data; cur_sequence_id++; sequence_index = -(cur_sequence_id - result->sequence_ids); } skip_dash(start_pos, end_pos); // tracks spec if (*start_pos == 'v' || *start_pos == 'a') { if (sequence_tracks_mask != NULL) { if (sequence_tracks_mask >= sequence_tracks_mask_end) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: the number of track specs exceeds the limit"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } sequence_tracks_mask->index = sequence_index; tracks_mask = sequence_tracks_mask->tracks_mask; sequence_tracks_mask++; result->sequence_tracks_mask_end = sequence_tracks_mask; } else { tracks_mask_updated = TRUE; tracks_mask = result->tracks_mask; } start_pos = ngx_http_vod_extract_track_tokens( start_pos, end_pos, tracks_mask); if (start_pos == NULL) { return NGX_OK; } } if (*start_pos != 'f' && *start_pos != 's') { break; } if (sequence_tracks_mask != NULL) { continue; } // more than one sequence, allocate the per sequence tracks mask sequence_tracks_mask = ngx_palloc(r->pool, sizeof(sequence_tracks_mask[0]) * MAX_SEQUENCE_TRACKS_MASKS); if (sequence_tracks_mask == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: ngx_palloc failed"); return ngx_http_vod_status_to_ngx_error(r, VOD_ALLOC_FAILED); } sequence_tracks_mask_end = sequence_tracks_mask + MAX_SEQUENCE_TRACKS_MASKS; result->sequence_tracks_mask = sequence_tracks_mask; result->sequence_tracks_mask_end = sequence_tracks_mask; if (tracks_mask_updated) { // add the currently parsed mask to the array sequence_tracks_mask->index = sequence_index; ngx_memcpy(sequence_tracks_mask->tracks_mask, result->tracks_mask, sizeof(sequence_tracks_mask->tracks_mask)); sequence_tracks_mask++; result->sequence_tracks_mask_end = sequence_tracks_mask; // restore the global mask to the default for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++) { result->tracks_mask[media_type] = default_tracks_mask; } } } } else if (*start_pos == 'v' || *start_pos == 'a') { // tracks start_pos = ngx_http_vod_extract_track_tokens(start_pos, end_pos, result->tracks_mask); if (start_pos == NULL) { return NGX_OK; } } // pts delay if (*start_pos == 'p') { start_pos++; // skip the p start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &pts_delay); if (pts_delay <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse pts delay"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->pts_delay = pts_delay; skip_dash(start_pos, end_pos); } // languages if (*start_pos == 'l') { result->langs_mask = ngx_pnalloc(r->pool, LANG_MASK_SIZE); if (result->langs_mask == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: ngx_pnalloc failed"); return ngx_http_vod_status_to_ngx_error(r, VOD_ALLOC_FAILED); } ngx_memzero(result->langs_mask, LANG_MASK_SIZE); for (;;) { start_pos++; // skip the l if (start_pos + LANG_ISO639_3_LEN > end_pos) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: language specifier length must be 3 characters"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } lang_id = lang_parse_iso639_3_code(iso639_3_str_to_int(start_pos)); if (lang_id == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse language specifier %*s", (size_t)3, start_pos); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } vod_set_bit(result->langs_mask, lang_id); start_pos += LANG_ISO639_3_LEN; skip_dash(start_pos, end_pos); if (*start_pos != 'l') { break; } } } // version if (*start_pos == 'x') { start_pos++; // skip the x start_pos = parse_utils_extract_uint32_token(start_pos, end_pos, &version); if (version <= 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: failed to parse version"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); } result->version = version - 1; skip_dash(start_pos, end_pos); } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_http_vod_parse_uri_file_name: did not consume the whole name"); return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST); }