├── static ├── alive.html ├── favicon.ico ├── robots.txt ├── crossdomain.xml └── clientaccesspolicy.xml ├── test ├── .gitignore ├── validate_iframes_params.py.template ├── parse_http_time.py ├── g2o_params.py.template ├── stream_compare_params.py.template ├── main_params.py.template ├── setup_test_entries_params.py.template ├── bitset │ └── build.sh ├── hls_compare_params.py.template ├── ts_utils.py ├── json_parser │ └── build.sh ├── buffer_cache │ ├── build.sh │ └── ngx_cycle.h ├── get_perf_counters_as_csv.php ├── download_stream.py ├── speed_test.py ├── print_mp4_atoms.py ├── g2o_curl.py ├── decrypt_ts_segment.py ├── verify_test_entries.py ├── compare_utils.py ├── generate_many_tracks.py ├── test_open_file_cache.conf ├── mp4_utils.py ├── test_static.py ├── README.md ├── dash_clear_key.php ├── http_utils.py ├── stream_compare.py └── uri_compare.py ├── vod ├── mkv │ ├── mkv_format.h │ ├── mkv_builder.h │ ├── mkv_defs.c │ ├── ebml.h │ └── mkv_defs.h ├── subtitle │ ├── cap_format.h │ ├── dfxp_format.h │ ├── webvtt_format.h │ ├── webvtt_builder.h │ ├── ttml_builder.h │ ├── subtitle_format.h │ └── webvtt_format_template.h ├── hls │ ├── frame_joiner_filter.h │ ├── eac3_encrypt_filter.h │ ├── frame_encrypt_filter.h │ ├── adts_encoder_filter.h │ ├── hls_encryption.h │ ├── mp4_to_annexb_filter.h │ ├── sample_aes_avc_filter.h │ ├── buffer_filter.h │ ├── aes_cbc_encrypt.h │ ├── bit_fields.def │ ├── id3_encoder_filter.h │ ├── media_filter.h │ ├── m3u8_builder.h │ ├── id3_encoder_filter.c │ ├── mpegts_encoder_filter.h │ └── hls_muxer.h ├── mp4 │ ├── mp4_format.h │ ├── mp4_cbcs_encrypt.h │ ├── mp4_write_stream.h │ ├── mp4_cenc_decrypt.h │ ├── mp4_muxer.h │ ├── mp4_cenc_passthrough.h │ ├── mp4_init_segment.h │ ├── mp4_clipper.h │ ├── mp4_aes_ctr.h │ ├── mp4_parser.h │ ├── mp4_fragment.h │ └── mp4_cenc_encrypt.h ├── buffer_pool.h ├── filters │ ├── mix_filter.h │ ├── gain_filter.h │ ├── concat_clip.h │ ├── filter.h │ ├── rate_filter.h │ ├── volume_map.h │ ├── audio_filter.h │ ├── audio_encoder.h │ ├── audio_decoder.h │ ├── dynamic_clip.h │ └── mix_filter.c ├── input │ ├── frames_source_memory.h │ ├── silence_generator.h │ ├── frames_source.h │ ├── frames_source_cache.h │ ├── frames_source_memory.c │ ├── read_cache.h │ └── frames_source_cache.c ├── hds │ ├── hds_encryption.h │ ├── hds_amf0_fields_x.h │ ├── hds_manifest.h │ ├── hds_amf0_encoder.h │ └── hds_fragment.h ├── cli │ ├── vod_array.h │ ├── vod_array.c │ └── vod_cli_main.c ├── dynamic_buffer.h ├── aes_defs.h ├── thumb │ └── thumb_grabber.h ├── avc_parser.h ├── hevc_parser.h ├── languages_hash_params.h ├── media_set.c ├── parse_utils.h ├── mss │ ├── mss_playready.h │ └── mss_packager.h ├── write_stream.h ├── udrm.h ├── write_buffer.h ├── language_code.h ├── dash │ ├── edash_packager.h │ └── dash_packager.h ├── write_buffer_queue.h ├── common.c ├── avc_defs.h ├── dynamic_buffer.c ├── media_set_parser.h ├── read_stream.h ├── avc_hevc_parser.h ├── manifest_utils.h ├── bit_read_stream.h ├── buffer_pool.c ├── codec_config.h ├── media_clip.h └── json_parser.h ├── ngx_http_vod_hds.h ├── ngx_http_vod_hls.h ├── ngx_http_vod_mss.h ├── conf ├── main.conf.template ├── cors.conf ├── base.conf ├── ssl.conf.template ├── vod-remote-nginx.conf.template ├── vod-local-nginx.conf.template ├── kaltura-nginx.conf.template ├── http.conf.template ├── vod-local.conf.template ├── vod-remote.conf.template └── kaltura.conf.template ├── ngx_http_vod_dash.h ├── ngx_http_vod_status.h ├── ngx_http_vod_volume_map.h ├── ngx_http_vod_thumb_conf.h ├── ngx_http_vod_volume_map_conf.h ├── ngx_http_vod_mss_conf.h ├── ngx_http_vod_thumb.h ├── ngx_http_vod_submodule.c ├── ngx_perf_counters_x.h ├── ngx_http_vod_hds_conf.h ├── ngx_http_vod_dash_conf.h ├── ngx_http_vod_thumb_commands.h ├── ngx_http_vod_volume_map_commands.h ├── ngx_http_vod_module.h ├── ngx_http_vod_mss_commands.h ├── scripts ├── encrypt_url.rb └── languages_hash_size.py ├── ngx_async_open_file_cache.h ├── tools ├── prefetch_proxy │ ├── README.md │ └── prefetcher.lua └── persist_proxy │ ├── README.md │ ├── status.lua │ └── metrics.lua ├── ngx_http_vod_hls_conf.h ├── ngx_http_vod_utils.h ├── ngx_http_vod_hds_commands.h ├── ngx_child_http_request.h ├── ngx_buffer_cache_internal.h ├── ngx_buffer_cache.h ├── ngx_perf_counters.c ├── ngx_file_reader.h ├── .github └── workflows │ └── ci.yml ├── ci_build.sh └── ngx_http_vod_request_parse.h /static/alive.html: -------------------------------------------------------------------------------- 1 | Kaltura -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /test/validate_iframes_params.py.template: -------------------------------------------------------------------------------- 1 | STOP_FILE = '/tmp/validate_iframes_stop' 2 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaltura/nginx-vod-module/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /content/ 3 | Disallow: /p/*/serveFlavor/ 4 | 5 | User-agent: Googlebot 6 | Disallow: /content/ 7 | -------------------------------------------------------------------------------- /test/parse_http_time.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import email.utils as eut 3 | import calendar 4 | import sys 5 | 6 | print(calendar.timegm(eut.parsedate(sys.argv[1]))) 7 | -------------------------------------------------------------------------------- /vod/mkv/mkv_format.h: -------------------------------------------------------------------------------- 1 | #ifndef __MKV_FORMAT_H__ 2 | #define __MKV_FORMAT_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | 7 | // globals 8 | extern media_format_t mkv_format; 9 | 10 | #endif //__MKV_FORMAT_H__ 11 | -------------------------------------------------------------------------------- /vod/subtitle/cap_format.h: -------------------------------------------------------------------------------- 1 | #ifndef __CAP_FORMAT_H__ 2 | #define __CAP_FORMAT_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | 7 | // globals 8 | extern media_format_t cap_format; 9 | 10 | #endif //__CAP_FORMAT_H__ 11 | -------------------------------------------------------------------------------- /ngx_http_vod_hds.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_HDS_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_HDS_H_INCLUDED_ 3 | 4 | // includes 5 | #include "ngx_http_vod_submodule.h" 6 | 7 | // globals 8 | extern const ngx_http_vod_submodule_t hds; 9 | 10 | #endif // _NGX_HTTP_VOD_HDS_H_INCLUDED_ 11 | -------------------------------------------------------------------------------- /ngx_http_vod_hls.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_HLS_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_HLS_H_INCLUDED_ 3 | 4 | // includes 5 | #include "ngx_http_vod_submodule.h" 6 | 7 | // globals 8 | extern const ngx_http_vod_submodule_t hls; 9 | 10 | #endif // _NGX_HTTP_VOD_HLS_H_INCLUDED_ 11 | -------------------------------------------------------------------------------- /ngx_http_vod_mss.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_MSS_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_MSS_H_INCLUDED_ 3 | 4 | // includes 5 | #include "ngx_http_vod_submodule.h" 6 | 7 | // globals 8 | extern const ngx_http_vod_submodule_t mss; 9 | 10 | #endif // _NGX_HTTP_VOD_MSS_H_INCLUDED_ 11 | -------------------------------------------------------------------------------- /conf/main.conf.template: -------------------------------------------------------------------------------- 1 | user kaltura; 2 | worker_processes auto; 3 | 4 | error_log @LOG_DIR@/kaltura_nginx_errors.log; 5 | 6 | pid @PID_FILE_PATH@; 7 | 8 | events { 9 | worker_connections 1024; 10 | worker_aio_requests 512; 11 | multi_accept on; 12 | use epoll; 13 | } 14 | -------------------------------------------------------------------------------- /ngx_http_vod_dash.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_DASH_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_DASH_H_INCLUDED_ 3 | 4 | // includes 5 | #include "ngx_http_vod_submodule.h" 6 | 7 | // globals 8 | extern const ngx_http_vod_submodule_t dash; 9 | 10 | #endif // _NGX_HTTP_VOD_DASH_H_INCLUDED_ 11 | -------------------------------------------------------------------------------- /ngx_http_vod_status.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_STATUS_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_STATUS_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | 7 | // functions 8 | ngx_int_t ngx_http_vod_status_handler(ngx_http_request_t *r); 9 | 10 | #endif // _NGX_HTTP_VOD_STATUS_H_INCLUDED_ 11 | -------------------------------------------------------------------------------- /test/g2o_params.py.template: -------------------------------------------------------------------------------- 1 | 2 | G2O_VERSION = '5' 3 | G2O_GHOST_IP = '1.2.3.4' 4 | G2O_CLIENT_IP = '5.6.7.8' 5 | G2O_UNIQUE_ID = 'xxx' 6 | G2O_NONCE = 'token' 7 | G2O_KEY = '' 8 | G2O_DATA_HEADER_NAME = 'X-Akamai-G2O-Auth-Data' 9 | G2O_SIGN_HEADER_NAME = 'X-Akamai-G2O-Auth-Sign' 10 | G2O_WINDOW = 29 11 | -------------------------------------------------------------------------------- /vod/hls/frame_joiner_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __FRAME_JOINER_H__ 2 | #define __FRAME_JOINER_H__ 3 | 4 | // include 5 | #include "media_filter.h" 6 | 7 | // functions 8 | vod_status_t frame_joiner_init( 9 | media_filter_t* filter, 10 | media_filter_context_t* context); 11 | 12 | #endif // __FRAME_JOINER_H__ 13 | -------------------------------------------------------------------------------- /conf/cors.conf: -------------------------------------------------------------------------------- 1 | add_header Access-Control-Allow-Headers "Origin,Range,Accept-Encoding,Referer,Cache-Control"; 2 | add_header Access-Control-Expose-Headers "Server,Content-Length,Content-Range,Date"; 3 | add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS"; 4 | add_header Access-Control-Allow-Origin "*"; 5 | -------------------------------------------------------------------------------- /ngx_http_vod_volume_map.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_VOLUME_MAP_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_VOLUME_MAP_H_INCLUDED_ 3 | 4 | // includes 5 | #include "ngx_http_vod_submodule.h" 6 | 7 | // globals 8 | extern const ngx_http_vod_submodule_t volume_map; 9 | 10 | #endif // _NGX_HTTP_VOD_VOLUME_MAP_H_INCLUDED_ 11 | -------------------------------------------------------------------------------- /vod/subtitle/dfxp_format.h: -------------------------------------------------------------------------------- 1 | #ifndef __DFXP_FORMAT_H__ 2 | #define __DFXP_FORMAT_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | 7 | // globals 8 | extern media_format_t dfxp_format; 9 | 10 | // functions 11 | void dfxp_init_process(); 12 | void dfxp_exit_process(); 13 | 14 | #endif //__DFXP_FORMAT_H__ 15 | -------------------------------------------------------------------------------- /vod/subtitle/webvtt_format.h: -------------------------------------------------------------------------------- 1 | #ifndef __WEBVTT_FORMAT_H__ 2 | #define __WEBVTT_FORMAT_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | 7 | // globals 8 | extern media_format_t webvtt_format; 9 | 10 | // functions 11 | void webvtt_init_process(vod_log_t* log); 12 | void webvtt_exit_process(); 13 | 14 | #endif //__WEBVTT_FORMAT_H__ 15 | -------------------------------------------------------------------------------- /vod/mp4/mp4_format.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_FORMAT_H__ 2 | #define __MP4_FORMAT_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | 7 | // enums 8 | enum { 9 | MP4_METADATA_PART_FTYP, 10 | MP4_METADATA_PART_MOOV, 11 | MP4_METADATA_PART_COUNT 12 | }; 13 | 14 | // globals 15 | extern media_format_t mp4_format; 16 | 17 | #endif //__MP4_FORMAT_H__ 18 | -------------------------------------------------------------------------------- /ngx_http_vod_thumb_conf.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_THUMB_CONF_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_THUMB_CONF_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | 7 | // typedefs 8 | typedef struct 9 | { 10 | ngx_str_t file_name_prefix; 11 | ngx_flag_t accurate; 12 | } ngx_http_vod_thumb_loc_conf_t; 13 | 14 | #endif // _NGX_HTTP_VOD_THUMB_CONF_H_INCLUDED_ 15 | -------------------------------------------------------------------------------- /vod/hls/eac3_encrypt_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __EAC3_ENCRYPT_FILTER_H__ 2 | #define __EAC3_ENCRYPT_FILTER_H__ 3 | 4 | // include 5 | #include "media_filter.h" 6 | #include "hls_encryption.h" 7 | 8 | // functions 9 | vod_status_t eac3_encrypt_filter_init( 10 | media_filter_t* filter, 11 | media_filter_context_t* context); 12 | 13 | #endif // __EAC3_ENCRYPT_FILTER_H__ 14 | -------------------------------------------------------------------------------- /ngx_http_vod_volume_map_conf.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_VOLUME_MAP_CONF_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_VOLUME_MAP_CONF_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | 7 | // typedefs 8 | typedef struct 9 | { 10 | ngx_str_t file_name_prefix; 11 | uintptr_t interval; 12 | } ngx_http_vod_volume_map_loc_conf_t; 13 | 14 | #endif // _NGX_HTTP_VOD_VOLUME_MAP_CONF_H_INCLUDED_ -------------------------------------------------------------------------------- /vod/buffer_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef __BUFFER_POOL_H__ 2 | #define __BUFFER_POOL_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | // functions 8 | buffer_pool_t* buffer_pool_create(vod_pool_t* pool, vod_log_t* log, size_t buffer_size, size_t count); 9 | void* buffer_pool_alloc(request_context_t* reqeust_context, buffer_pool_t* buffer_pool, size_t* buffer_size); 10 | 11 | #endif // __BUFFER_POOL_H__ 12 | -------------------------------------------------------------------------------- /vod/filters/mix_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __MIX_FILTER_H__ 2 | #define __MIX_FILTER_H__ 3 | 4 | // includes 5 | #include "../json_parser.h" 6 | 7 | // functions 8 | vod_status_t mix_filter_parse( 9 | void* context, 10 | vod_json_object_t* element, 11 | void** result); 12 | 13 | vod_status_t mix_filter_parser_init( 14 | vod_pool_t* pool, 15 | vod_pool_t* temp_pool); 16 | 17 | #endif // __MIX_FILTER_H__ 18 | -------------------------------------------------------------------------------- /vod/input/frames_source_memory.h: -------------------------------------------------------------------------------- 1 | #ifndef __FRAMES_SOURCE_MEMORY_H__ 2 | #define __FRAMES_SOURCE_MEMORY_H__ 3 | 4 | // includes 5 | #include "frames_source.h" 6 | 7 | // globals 8 | extern frames_source_t frames_source_memory; 9 | 10 | // functions 11 | vod_status_t frames_source_memory_init( 12 | request_context_t* request_context, 13 | void** result); 14 | 15 | #endif //__FRAMES_SOURCE_MEMORY_H__ 16 | -------------------------------------------------------------------------------- /test/stream_compare_params.py.template: -------------------------------------------------------------------------------- 1 | 2 | STOP_FILE = '/tmp/stream_compare_stop' 3 | URL1_BASE = ['http://localhost:8001'] 4 | URL2_BASE = ['http://localhost:8001'] 5 | 6 | EXTRA_HEADERS = {} 7 | 8 | LOG_LEVEL = { 9 | "UrlCompareLog": True 10 | } 11 | 12 | CHUNK_LIST_ITEMS_TO_COMPARE = 0 #0 is all 13 | 14 | URL_COMPARE_RETRIES = 3 15 | 16 | URL_COMPARE_RETRIES_SLEEP_INTERVAL = 5 -------------------------------------------------------------------------------- /vod/filters/gain_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __GAIN_FILTER_H__ 2 | #define __GAIN_FILTER_H__ 3 | 4 | // includes 5 | #include "../json_parser.h" 6 | 7 | // functions 8 | vod_status_t gain_filter_parse( 9 | void* context, 10 | vod_json_object_t* element, 11 | void** result); 12 | 13 | vod_status_t gain_filter_parser_init( 14 | vod_pool_t* pool, 15 | vod_pool_t* temp_pool); 16 | 17 | #endif // __GAIN_FILTER_H__ 18 | -------------------------------------------------------------------------------- /conf/base.conf: -------------------------------------------------------------------------------- 1 | 2 | # nginx status page 3 | location = /nginx_status { 4 | stub_status on; 5 | access_log off; 6 | } 7 | 8 | # vod status page 9 | location = /vod_status { 10 | vod_status; 11 | access_log off; 12 | } 13 | 14 | # redirect server error pages to the static page /50x.html 15 | error_page 500 502 503 504 /50x.html; 16 | 17 | location = /50x.html { 18 | root html; 19 | } 20 | -------------------------------------------------------------------------------- /ngx_http_vod_mss_conf.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_MSS_CONF_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_MSS_CONF_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | #include "vod/mss/mss_packager.h" 7 | 8 | // typedefs 9 | typedef struct 10 | { 11 | ngx_str_t manifest_file_name_prefix; 12 | mss_manifest_config_t manifest_conf; 13 | } ngx_http_vod_mss_loc_conf_t; 14 | 15 | #endif // _NGX_HTTP_VOD_MSS_CONF_H_INCLUDED_ 16 | -------------------------------------------------------------------------------- /static/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /vod/input/silence_generator.h: -------------------------------------------------------------------------------- 1 | #ifndef __SILENCE_GENERATOR_H__ 2 | #define __SILENCE_GENERATOR_H__ 3 | 4 | // includes 5 | #include "../media_clip.h" 6 | #include "../json_parser.h" 7 | 8 | // globals 9 | extern media_generator_t silence_generator; 10 | 11 | // functions 12 | vod_status_t silence_generator_parse( 13 | void* ctx, 14 | vod_json_object_t* element, 15 | void** result); 16 | 17 | #endif // __SILENCE_GENERATOR_H__ 18 | -------------------------------------------------------------------------------- /vod/subtitle/webvtt_builder.h: -------------------------------------------------------------------------------- 1 | #ifndef __WEBVTT_BUILDER_H__ 2 | #define __WEBVTT_BUILDER_H__ 3 | 4 | // includes 5 | #include "../media_set.h" 6 | 7 | // constants 8 | #define WEBVTT_TIMESCALE (1000) 9 | 10 | // functions 11 | vod_status_t webvtt_builder_build( 12 | request_context_t* request_context, 13 | media_set_t* media_set, 14 | bool_t clip_relative_timestamps, 15 | vod_str_t* result); 16 | 17 | #endif //__WEBVTT_BUILDER_H__ 18 | -------------------------------------------------------------------------------- /vod/mp4/mp4_cbcs_encrypt.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_CBCS_ENCRYPT_H__ 2 | #define __MP4_CBCS_ENCRYPT_H__ 3 | 4 | // includes 5 | #include "../media_set.h" 6 | 7 | // functions 8 | vod_status_t mp4_cbcs_encrypt_get_writers( 9 | request_context_t* request_context, 10 | media_set_t* media_set, 11 | segment_writer_t* segment_writer, 12 | const u_char* key, 13 | const u_char* iv, 14 | segment_writer_t** result); 15 | 16 | #endif //__MP4_CBCS_ENCRYPT_H__ 17 | -------------------------------------------------------------------------------- /vod/hds/hds_encryption.h: -------------------------------------------------------------------------------- 1 | #ifndef __HDS_ENCRYPTION_H__ 2 | #define __HDS_ENCRYPTION_H__ 3 | 4 | // includes 5 | #include "../common.h" 6 | 7 | // typedefs 8 | typedef enum { 9 | HDS_ENC_NONE, 10 | HDS_ENC_SELECTIVE, // SE = Selective Encryption 11 | } hds_encryption_type_t; 12 | 13 | typedef struct { 14 | hds_encryption_type_t type; 15 | u_char* key; 16 | u_char* iv; 17 | } hds_encryption_params_t; 18 | 19 | #endif // __HDS_ENCRYPTION_H__ 20 | -------------------------------------------------------------------------------- /conf/ssl.conf.template: -------------------------------------------------------------------------------- 1 | # HTTPS server 2 | # 3 | server { 4 | listen @VOD_PACKAGER_SSL_PORT@ ssl; 5 | server_name @VOD_PACKAGER_HOST@; 6 | 7 | ssl_certificate @SSL_CERT@; 8 | ssl_certificate_key @SSL_KEY@; 9 | 10 | ssl_session_cache shared:SSL:1m; 11 | ssl_session_timeout 5m; 12 | 13 | ssl_ciphers HIGH:!aNULL:!MD5; 14 | ssl_prefer_server_ciphers on; 15 | 16 | include @NGINX_CONF_PATH@/server.conf; 17 | } 18 | -------------------------------------------------------------------------------- /ngx_http_vod_thumb.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_THUMB_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_THUMB_H_INCLUDED_ 3 | 4 | // includes 5 | #include "ngx_http_vod_submodule.h" 6 | 7 | // globals 8 | extern const ngx_http_vod_submodule_t thumb; 9 | 10 | // functions 11 | ngx_int_t ngx_http_vod_thumb_get_url( 12 | ngx_http_vod_submodule_context_t* submodule_context, 13 | uint32_t sequences_mask, 14 | ngx_str_t* result); 15 | 16 | #endif // _NGX_HTTP_VOD_THUMB_H_INCLUDED_ 17 | -------------------------------------------------------------------------------- /test/main_params.py.template: -------------------------------------------------------------------------------- 1 | TEST_FLAVOR_URI = '/p/.../sp/.../serveFlavor/entryId/.../v/.../flavorId/...' # a serveFlavor URI of some h264/aac mp4 2 | TEST_FLAVOR_FILE = '/content/.../entry/data/...mp4' # the file path of some h264/aac mp4 3 | TEST_FILES_ROOT = '/web' 4 | TEST_NONEXISTING_FILE = '/content/file/not/found' 5 | TEST_NO_READ_ACCESS_FILE = '/content/shared/no_access' # a file that nginx workers cant read 6 | TEST_FOLDER = '/content/entry' # some folder 7 | -------------------------------------------------------------------------------- /test/setup_test_entries_params.py.template: -------------------------------------------------------------------------------- 1 | PARTNER_ID = 1234 2 | ADMIN_SECRET = "abcd" 3 | SERVICE_URL = "http://www.kaltura.com" 4 | USER_NAME = "admin" 5 | TEST_FLAVOR_URL = 'http://www.kaltura.com/p/99/sp/9900/serveFlavor/entryId/0_ci8ffv43/v/1/flavorId/0_o2yj6rxx' 6 | TEMP_DOWNLOAD_PATH = '/tmp/temp.mp4' 7 | TEMP_CONVERT_PATH = '/tmp/convert.mp4' 8 | FFMPEG_BIN = '/path/to/ffmpeg' 9 | QTFASTSTART_BIN = 'python -m qtfaststart' # install https://github.com/danielgtaylor/qtfaststart 10 | -------------------------------------------------------------------------------- /test/bitset/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$NGX_ROOT" ]; then 4 | echo "NGX_ROOT not set" 5 | exit 1 6 | fi 7 | 8 | if [ -z "$VOD_ROOT" ]; then 9 | echo "VOD_ROOT not set" 10 | exit 1 11 | fi 12 | 13 | if [ -z "$CC" ]; then 14 | CC=cc 15 | fi 16 | 17 | $CC -Wall -g -obitsettest -DNGX_HAVE_LIB_AV_CODEC=0 $VOD_ROOT/vod/common.c $VOD_ROOT/test/bitset/main.c -I $NGX_ROOT/src/core -I $NGX_ROOT/src/event -I $NGX_ROOT/src/event/modules -I $NGX_ROOT/src/os/unix -I $NGX_ROOT/objs -I $VOD_ROOT 18 | -------------------------------------------------------------------------------- /static/clientaccesspolicy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /conf/vod-remote-nginx.conf.template: -------------------------------------------------------------------------------- 1 | include @NGINX_CONF_PATH@/main.conf; 2 | 3 | http { 4 | upstream media { 5 | server @MEDIA_HOST@; 6 | keepalive 32; 7 | } 8 | 9 | include @NGINX_CONF_PATH@/http.conf; 10 | 11 | # vod remote settings 12 | vod_mode remote; 13 | vod_upstream_location /media_proxy; 14 | 15 | server { 16 | listen @VOD_PACKAGER_PORT@; 17 | server_name @VOD_PACKAGER_HOST@; 18 | include @NGINX_CONF_PATH@/server.conf; 19 | } 20 | 21 | include @NGINX_CONF_PATH@/ssl.conf; 22 | } 23 | -------------------------------------------------------------------------------- /vod/cli/vod_array.h: -------------------------------------------------------------------------------- 1 | #ifndef __VOD_ARRAY_H__ 2 | #define __VOD_ARRAY_H__ 3 | 4 | #define vod_array_init(array, pool, n, size) vod_array_init_impl(array, n, size) 5 | 6 | typedef struct { 7 | void *elts; 8 | unsigned nelts; 9 | size_t size; 10 | unsigned nalloc; 11 | } vod_array_t; 12 | 13 | int vod_array_init_impl(vod_array_t *array, unsigned n, size_t size); 14 | void *vod_array_push(vod_array_t *a); 15 | void vod_array_destroy(vod_array_t *a); 16 | 17 | #endif // __VOD_ARRAY_H__ 18 | -------------------------------------------------------------------------------- /conf/vod-local-nginx.conf.template: -------------------------------------------------------------------------------- 1 | include @NGINX_CONF_PATH@/main.conf; 2 | 3 | http { 4 | include @NGINX_CONF_PATH@/http.conf; 5 | 6 | # file handle caching / aio 7 | open_file_cache max=1000 inactive=5m; 8 | open_file_cache_valid 2m; 9 | open_file_cache_min_uses 1; 10 | open_file_cache_errors on; 11 | aio on; 12 | 13 | server { 14 | listen @VOD_PACKAGER_PORT@; 15 | server_name @VOD_PACKAGER_HOST@; 16 | include @NGINX_CONF_PATH@/server.conf; 17 | } 18 | 19 | include @NGINX_CONF_PATH@/ssl.conf; 20 | } 21 | -------------------------------------------------------------------------------- /test/hls_compare_params.py.template: -------------------------------------------------------------------------------- 1 | URL1_PREFIX = 'http://localhost:8001/mapped' 2 | URL1_PREFIXES = ['mapped/hls','remote/hls','mapped/hls/enc','remote/hls/enc'] 3 | URL1_SUFFIX = '/index.m3u8' 4 | URL2_PREFIX = 'http://some-other-server' 5 | URL2_SUFFIX = '/index.m3u8' 6 | FFPROBE_BIN = '/path/to/ffprobe' 7 | TEST_PARTNER_ID = '12345' 8 | 9 | STOP_FILE = '/tmp/compare_stop' 10 | TEMP_TS_FILE1 = '/tmp/1.ts' 11 | TEMP_TS_FILE2 = '/tmp/2.ts' 12 | TEMP_FFPROBE_FILE1 = '/tmp/1.ts.ffp' 13 | TEMP_FFPROBE_FILE2 = '/tmp/2.ts.ffp' 14 | -------------------------------------------------------------------------------- /vod/hls/frame_encrypt_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __FRAME_ENCRYPT_FILTER_H__ 2 | #define __FRAME_ENCRYPT_FILTER_H__ 3 | 4 | // include 5 | #include "media_filter.h" 6 | #include "hls_encryption.h" 7 | 8 | // functions 9 | vod_status_t frame_encrypt_filter_init( 10 | media_filter_t* filter, 11 | media_filter_context_t* context, 12 | hls_encryption_params_t* encryption_params); 13 | 14 | void frame_encrypt_start_sub_frame( 15 | media_filter_context_t* context, 16 | uint32_t size); 17 | 18 | #endif // __FRAME_ENCRYPT_FILTER_H__ 19 | -------------------------------------------------------------------------------- /ngx_http_vod_submodule.c: -------------------------------------------------------------------------------- 1 | #include "ngx_http_vod_dash.h" 2 | #include "ngx_http_vod_hds.h" 3 | #include "ngx_http_vod_hls.h" 4 | #include "ngx_http_vod_mss.h" 5 | 6 | #if (NGX_HAVE_LIB_AV_CODEC) 7 | #include "ngx_http_vod_thumb.h" 8 | #include "ngx_http_vod_volume_map.h" 9 | #endif // NGX_HAVE_LIB_AV_CODEC 10 | 11 | const ngx_http_vod_submodule_t* submodules[] = { 12 | &dash, 13 | &hds, 14 | &hls, 15 | &mss, 16 | #if (NGX_HAVE_LIB_AV_CODEC) 17 | &thumb, 18 | &volume_map, 19 | #endif // NGX_HAVE_LIB_AV_CODEC 20 | NULL, 21 | }; 22 | -------------------------------------------------------------------------------- /ngx_perf_counters_x.h: -------------------------------------------------------------------------------- 1 | PC(FETCH_CACHE, fetch_cache) 2 | PC(STORE_CACHE, store_cache) 3 | PC(MAP_PATH, map_path) 4 | PC(PARSE_MEDIA_SET, parse_media_set) 5 | PC(GET_DRM_INFO, get_drm_info) 6 | PC(OPEN_FILE, open_file) 7 | PC(ASYNC_OPEN_FILE, async_open_file) 8 | PC(READ_FILE, read_file) 9 | PC(ASYNC_READ_FILE, async_read_file) 10 | PC(MEDIA_PARSE, media_parse) 11 | PC(BUILD_MANIFEST, build_manifest) 12 | PC(INIT_FRAME_PROCESS, init_frame_processing) 13 | PC(PROCESS_FRAMES, process_frames) 14 | PC(TOTAL, total) 15 | -------------------------------------------------------------------------------- /ngx_http_vod_hds_conf.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_HDS_CONF_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_HDS_CONF_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | #include "vod/hds/hds_manifest.h" 7 | #include "vod/hds/hds_fragment.h" 8 | 9 | // typedefs 10 | typedef struct 11 | { 12 | ngx_flag_t absolute_manifest_urls; 13 | ngx_str_t manifest_file_name_prefix; 14 | hds_manifest_config_t manifest_config; 15 | hds_fragment_config_t fragment_config; 16 | } ngx_http_vod_hds_loc_conf_t; 17 | 18 | #endif // _NGX_HTTP_VOD_HDS_CONF_H_INCLUDED_ 19 | -------------------------------------------------------------------------------- /vod/filters/concat_clip.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONCAT_CLIP_H__ 2 | #define __CONCAT_CLIP_H__ 3 | 4 | // includes 5 | #include "../json_parser.h" 6 | #include "../media_clip.h" 7 | 8 | // functions 9 | vod_status_t concat_clip_parse( 10 | void* context, 11 | vod_json_object_t* element, 12 | void** result); 13 | 14 | vod_status_t concat_clip_concat( 15 | request_context_t* request_context, 16 | media_clip_t* clip); 17 | 18 | vod_status_t concat_clip_parser_init( 19 | vod_pool_t* pool, 20 | vod_pool_t* temp_pool); 21 | 22 | #endif // __CONCAT_CLIP_H__ 23 | -------------------------------------------------------------------------------- /test/ts_utils.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import AES 2 | 3 | def decryptTsSegment(fileData, aesKey, aesIv): 4 | cipher = AES.new(aesKey, AES.MODE_CBC, aesIv) 5 | try: 6 | decryptedTS = cipher.decrypt(fileData) 7 | except ValueError: 8 | return None, 'Error: cipher.decrypt failed' 9 | padLength = ord(decryptedTS[-1]) 10 | if padLength > 16: 11 | return None, 'Error: invalid pad length %s' % padLength 12 | if decryptedTS[-padLength:] != chr(padLength) * padLength: 13 | return None, 'Error: invalid padding' 14 | return decryptedTS[:-padLength], '' 15 | -------------------------------------------------------------------------------- /vod/dynamic_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef __DYNAMIC_BUFFER_H__ 2 | #define __DYNAMIC_BUFFER_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | // typedefs 8 | typedef struct { 9 | request_context_t* request_context; 10 | u_char* start; 11 | u_char* pos; 12 | u_char* end; 13 | } vod_dynamic_buf_t; 14 | 15 | // functions 16 | vod_status_t vod_dynamic_buf_init(vod_dynamic_buf_t* buffer, request_context_t* request_context, size_t initial_size); 17 | 18 | vod_status_t vod_dynamic_buf_reserve(vod_dynamic_buf_t* buffer, size_t size); 19 | 20 | #endif // __DYNAMIC_BUFFER_H__ 21 | -------------------------------------------------------------------------------- /vod/hls/adts_encoder_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __ADTS_ENCODER_FILTER_H__ 2 | #define __ADTS_ENCODER_FILTER_H__ 3 | 4 | // includes 5 | #include "hls_encryption.h" 6 | #include "bit_fields.h" 7 | #include "media_filter.h" 8 | #include "../media_format.h" 9 | #include "../common.h" 10 | 11 | // functions 12 | vod_status_t adts_encoder_init( 13 | media_filter_t* filter, 14 | media_filter_context_t* context); 15 | 16 | vod_status_t adts_encoder_set_media_info( 17 | media_filter_context_t* context, 18 | media_info_t* media_info); 19 | 20 | #endif // __ADTS_ENCODER_FILTER_H__ 21 | -------------------------------------------------------------------------------- /vod/aes_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef __AES_DEFS_H__ 2 | #define __AES_DEFS_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | #if (VOD_HAVE_OPENSSL_EVP) 8 | #include 9 | #endif // VOD_HAVE_OPENSSL_EVP 10 | 11 | // constants 12 | #ifndef AES_BLOCK_SIZE 13 | #define AES_BLOCK_SIZE (16) 14 | #endif // AES_BLOCK_SIZE 15 | 16 | // macros 17 | #define aes_round_down_to_block(n) ((n) & ~0xf) 18 | #define aes_round_up_to_block(n) aes_round_down_to_block((n) + 0x10) 19 | #define aes_round_up_to_block_exact(n) aes_round_down_to_block((n) + 0xf) 20 | 21 | #endif //__AES_DEFS_H__ 22 | -------------------------------------------------------------------------------- /vod/subtitle/ttml_builder.h: -------------------------------------------------------------------------------- 1 | #ifndef __TTML_BUILDER_H__ 2 | #define __TTML_BUILDER_H__ 3 | 4 | // includes 5 | #include "../media_set.h" 6 | 7 | // constants 8 | #define TTML_TIMESCALE (1000) 9 | 10 | // functions 11 | size_t ttml_builder_get_max_size(media_set_t* media_set); 12 | 13 | u_char* ttml_builder_write(media_set_t* media_set, u_char* p); 14 | 15 | vod_status_t ttml_build_mp4( 16 | request_context_t* request_context, 17 | media_set_t* media_set, 18 | uint32_t segment_index, 19 | uint32_t timescale, 20 | vod_str_t* result); 21 | 22 | #endif //__TTML_BUILDER_H__ 23 | -------------------------------------------------------------------------------- /vod/hls/hls_encryption.h: -------------------------------------------------------------------------------- 1 | #ifndef __HLS_ENCRYPTION_H__ 2 | #define __HLS_ENCRYPTION_H__ 3 | 4 | // includes 5 | #include "../common.h" 6 | #include "../aes_defs.h" 7 | 8 | // typedefs 9 | typedef enum { 10 | HLS_ENC_NONE, 11 | HLS_ENC_AES_128, 12 | HLS_ENC_SAMPLE_AES, 13 | HLS_ENC_SAMPLE_AES_CENC, 14 | } hls_encryption_type_t; 15 | 16 | typedef struct { 17 | hls_encryption_type_t type; 18 | u_char* key; 19 | u_char* iv; 20 | vod_str_t key_uri; 21 | bool_t return_iv; 22 | u_char iv_buf[AES_BLOCK_SIZE]; 23 | } hls_encryption_params_t; 24 | 25 | #endif // __HLS_ENCRYPTION_H__ 26 | -------------------------------------------------------------------------------- /vod/thumb/thumb_grabber.h: -------------------------------------------------------------------------------- 1 | #ifndef __THUMB_GRABBER_H__ 2 | #define __THUMB_GRABBER_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | #include "../media_set.h" 7 | 8 | // functions 9 | void thumb_grabber_process_init(vod_log_t* log); 10 | 11 | vod_status_t thumb_grabber_init_state( 12 | request_context_t* request_context, 13 | media_track_t* track, 14 | request_params_t* request_params, 15 | bool_t accurate, 16 | write_callback_t write_callback, 17 | void* write_context, 18 | void** result); 19 | 20 | vod_status_t thumb_grabber_process(void* context); 21 | 22 | #endif //__THUMB_GRABBER_H__ 23 | -------------------------------------------------------------------------------- /ngx_http_vod_dash_conf.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_DASH_CONF_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_DASH_CONF_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | #include "vod/dash/dash_packager.h" 7 | 8 | // typedefs 9 | typedef struct 10 | { 11 | ngx_str_t manifest_file_name_prefix; 12 | ngx_flag_t absolute_manifest_urls; 13 | ngx_flag_t init_mp4_pssh; 14 | dash_manifest_config_t mpd_config; 15 | } ngx_http_vod_dash_loc_conf_t; 16 | 17 | // globals 18 | extern ngx_conf_enum_t dash_manifest_formats[]; 19 | 20 | extern ngx_conf_enum_t dash_subtitle_formats[]; 21 | 22 | #endif // _NGX_HTTP_VOD_DASH_CONF_H_INCLUDED_ 23 | -------------------------------------------------------------------------------- /test/json_parser/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$NGX_ROOT" ]; then 4 | echo "NGX_ROOT not set" 5 | exit 1 6 | fi 7 | 8 | if [ -z "$VOD_ROOT" ]; then 9 | echo "VOD_ROOT not set" 10 | exit 1 11 | fi 12 | 13 | if [ -z "$CC" ]; then 14 | CC=cc 15 | fi 16 | 17 | $CC -Wall -g -ojsontest $VOD_ROOT/vod/json_parser.c $VOD_ROOT/vod/parse_utils.c $VOD_ROOT/test/json_parser/main.c $NGX_ROOT/src/core/ngx_string.c $NGX_ROOT/src/core/ngx_hash.c $NGX_ROOT/src/core/ngx_palloc.c $NGX_ROOT/src/os/unix/ngx_alloc.c -I $NGX_ROOT/src/core -I $NGX_ROOT/src/event -I $NGX_ROOT/src/event/modules -I $NGX_ROOT/src/os/unix -I $NGX_ROOT/objs -I $VOD_ROOT 18 | -------------------------------------------------------------------------------- /vod/hls/mp4_to_annexb_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_TO_ANNEXB_FILTER_H__ 2 | #define __MP4_TO_ANNEXB_FILTER_H__ 3 | 4 | // includes 5 | #include "hls_encryption.h" 6 | #include "media_filter.h" 7 | #include "../media_format.h" 8 | 9 | // functions 10 | vod_status_t mp4_to_annexb_init( 11 | media_filter_t* filter, 12 | media_filter_context_t* context, 13 | hls_encryption_params_t* encryption_params); 14 | 15 | vod_status_t mp4_to_annexb_set_media_info( 16 | media_filter_context_t* context, 17 | media_info_t* media_info); 18 | 19 | bool_t mp4_to_annexb_simulation_supported( 20 | media_info_t* media_info); 21 | 22 | #endif // __MP4_TO_ANNEXB_FILTER_H__ 23 | -------------------------------------------------------------------------------- /vod/input/frames_source.h: -------------------------------------------------------------------------------- 1 | #ifndef __FRAMES_SOURCE_H__ 2 | #define __FRAMES_SOURCE_H__ 3 | 4 | // includes 5 | #include "read_cache.h" 6 | 7 | // typedefs 8 | struct input_frame_s; 9 | 10 | typedef struct { 11 | void(*set_cache_slot_id)(void* context, int cache_slot_id); 12 | vod_status_t(*start_frame)(void* context, struct input_frame_s* frame, read_cache_hint_t* cache_hint); 13 | vod_status_t(*read)(void* context, u_char** buffer, uint32_t* size, bool_t* frame_done); 14 | void(*disable_buffer_reuse)(void* context); 15 | vod_status_t(*skip_frames)(void* context, uint32_t skip_count); 16 | } frames_source_t; 17 | 18 | #endif // __FRAMES_SOURCE_H__ 19 | -------------------------------------------------------------------------------- /vod/filters/filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __FILTER_H__ 2 | #define __FILTER_H__ 3 | 4 | // includes 5 | #include "../input/read_cache.h" 6 | #include "../media_set.h" 7 | 8 | // functions 9 | vod_status_t filter_init_filtered_clips( 10 | request_context_t* request_context, 11 | media_set_t* media_set, 12 | bool_t parsed_frames); 13 | 14 | vod_status_t filter_init_state( 15 | request_context_t* request_context, 16 | read_cache_state_t* read_cache_state, 17 | media_set_t* media_set, 18 | uint32_t max_frame_count, 19 | uint32_t output_codec_id, 20 | void** context); 21 | 22 | vod_status_t filter_run_state_machine(void* context); 23 | 24 | #endif // __FILTER_H__ 25 | -------------------------------------------------------------------------------- /vod/hds/hds_amf0_fields_x.h: -------------------------------------------------------------------------------- 1 | AMF0_FIELD(AMF0_COMMON,duration, AMF0_NUMBER) 2 | AMF0_FIELD(AMF0_VIDEO, width, AMF0_NUMBER) 3 | AMF0_FIELD(AMF0_VIDEO, height, AMF0_NUMBER) 4 | AMF0_FIELD(AMF0_VIDEO, videodatarate, AMF0_NUMBER) 5 | AMF0_FIELD(AMF0_VIDEO, framerate, AMF0_NUMBER) 6 | AMF0_FIELD(AMF0_VIDEO, videocodecid, AMF0_NUMBER) 7 | AMF0_FIELD(AMF0_AUDIO, audiodatarate, AMF0_NUMBER) 8 | AMF0_FIELD(AMF0_AUDIO, audiosamplerate, AMF0_NUMBER) 9 | AMF0_FIELD(AMF0_AUDIO, audiosamplesize, AMF0_NUMBER) 10 | AMF0_FIELD(AMF0_AUDIO, stereo, AMF0_BOOLEAN) 11 | AMF0_FIELD(AMF0_AUDIO, audiocodecid, AMF0_NUMBER) 12 | AMF0_FIELD(AMF0_COMMON,filesize, AMF0_NUMBER) 13 | -------------------------------------------------------------------------------- /vod/avc_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef __AVC_PARSER_H__ 2 | #define __AVC_PARSER_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | // functions 8 | vod_status_t avc_parser_parse_extra_data( 9 | void* ctx, 10 | vod_str_t* extra_data, 11 | uint32_t* nal_packet_size_length, 12 | uint32_t* min_packet_size); 13 | 14 | vod_status_t avc_parser_get_slice_header_size( 15 | void* ctx, 16 | const u_char* buffer, 17 | uint32_t size, 18 | uint32_t* result); 19 | 20 | vod_status_t avc_parser_is_slice( 21 | void* ctx, 22 | uint8_t nal_type, 23 | bool_t* is_slice); 24 | 25 | uint8_t avc_parser_get_transfer_characteristics( 26 | void* ctx); 27 | 28 | #endif // __AVC_PARSER_H__ 29 | -------------------------------------------------------------------------------- /vod/hls/sample_aes_avc_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __SAMPLE_AES_AVC_FILTER_H__ 2 | #define __SAMPLE_AES_AVC_FILTER_H__ 3 | 4 | // includes 5 | #include "media_filter.h" 6 | 7 | // functions 8 | vod_status_t sample_aes_avc_filter_init( 9 | media_filter_t* filter, 10 | media_filter_context_t* context, 11 | u_char* key, 12 | u_char* iv); 13 | 14 | vod_status_t sample_aes_avc_start_nal_unit( 15 | media_filter_context_t* context, 16 | int unit_type, 17 | uint32_t unit_size); 18 | 19 | vod_status_t sample_aes_avc_filter_write_nal_body( 20 | media_filter_context_t* context, 21 | const u_char* buffer, 22 | uint32_t size); 23 | 24 | #endif //__SAMPLE_AES_AVC_FILTER_H__ 25 | -------------------------------------------------------------------------------- /vod/mp4/mp4_write_stream.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_WRITE_STREAM_H__ 2 | #define __MP4_WRITE_STREAM_H__ 3 | 4 | #include "../write_stream.h" 5 | 6 | // macros 7 | #define write_atom_name(p, c1, c2, c3, c4) \ 8 | { *(p)++ = (c1); *(p)++ = (c2); *(p)++ = (c3); *(p)++ = (c4); } 9 | 10 | #define write_atom_header(p, size, c1, c2, c3, c4) \ 11 | { \ 12 | write_be32(p, size); \ 13 | write_atom_name(p, c1, c2, c3, c4); \ 14 | } 15 | 16 | #define write_atom_header64(p, size, c1, c2, c3, c4) \ 17 | { \ 18 | write_be32(p, 1); \ 19 | write_atom_name(p, c1, c2, c3, c4); \ 20 | write_be64(p, size); \ 21 | } 22 | 23 | #endif //__MP4_WRITE_STREAM_H__ 24 | -------------------------------------------------------------------------------- /test/buffer_cache/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$NGX_ROOT" ]; then 4 | echo "NGX_ROOT not set" 5 | exit 1 6 | fi 7 | 8 | if [ -z "$VOD_ROOT" ]; then 9 | echo "VOD_ROOT not set" 10 | exit 1 11 | fi 12 | 13 | if [ -z "$CC" ]; then 14 | CC=cc 15 | fi 16 | 17 | $CC -Wall $NGX_ROOT/src/core/ngx_palloc.c $NGX_ROOT/src/os/unix/ngx_alloc.c $NGX_ROOT/src/core/ngx_string.c $NGX_ROOT/src/core/ngx_crc32.c $NGX_ROOT/src/core/ngx_rbtree.c $VOD_ROOT/ngx_buffer_cache.c $VOD_ROOT/test/buffer_cache/main.c -o bctest -I $VOD_ROOT/test/buffer_cache -I $NGX_ROOT/src/core -I $NGX_ROOT/src/event -I $NGX_ROOT/src/event/modules -I $NGX_ROOT/src/os/unix -I $NGX_ROOT/objs -I $VOD_ROOT -g 18 | -------------------------------------------------------------------------------- /vod/hevc_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef __HEVC_PARSER_H__ 2 | #define __HEVC_PARSER_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | // functions 8 | vod_status_t hevc_parser_parse_extra_data( 9 | void* ctx, 10 | vod_str_t* extra_data, 11 | uint32_t* nal_packet_size_length, 12 | uint32_t* min_packet_size); 13 | 14 | vod_status_t hevc_parser_get_slice_header_size( 15 | void* ctx, 16 | const u_char* buffer, 17 | uint32_t size, 18 | uint32_t* result); 19 | 20 | vod_status_t hevc_parser_is_slice( 21 | void* ctx, 22 | uint8_t nal_type, 23 | bool_t* is_slice); 24 | 25 | uint8_t hevc_parser_get_transfer_characteristics( 26 | void* ctx); 27 | 28 | #endif // __HEVC_PARSER_H__ 29 | -------------------------------------------------------------------------------- /ngx_http_vod_thumb_commands.h: -------------------------------------------------------------------------------- 1 | #define BASE_OFFSET offsetof(ngx_http_vod_loc_conf_t, thumb) 2 | 3 | { ngx_string("vod_thumb_file_name_prefix"), 4 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 5 | ngx_conf_set_str_slot, 6 | NGX_HTTP_LOC_CONF_OFFSET, 7 | BASE_OFFSET + offsetof(ngx_http_vod_thumb_loc_conf_t, file_name_prefix), 8 | NULL }, 9 | 10 | { ngx_string("vod_thumb_accurate_positioning"), 11 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 12 | ngx_conf_set_flag_slot, 13 | NGX_HTTP_LOC_CONF_OFFSET, 14 | BASE_OFFSET + offsetof(ngx_http_vod_thumb_loc_conf_t, accurate), 15 | NULL }, 16 | 17 | #undef BASE_OFFSET 18 | -------------------------------------------------------------------------------- /ngx_http_vod_volume_map_commands.h: -------------------------------------------------------------------------------- 1 | #define BASE_OFFSET offsetof(ngx_http_vod_loc_conf_t, volume_map) 2 | 3 | { ngx_string("vod_volume_map_file_name_prefix"), 4 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 5 | ngx_conf_set_str_slot, 6 | NGX_HTTP_LOC_CONF_OFFSET, 7 | BASE_OFFSET + offsetof(ngx_http_vod_volume_map_loc_conf_t, file_name_prefix), 8 | NULL }, 9 | 10 | { ngx_string("vod_volume_map_interval"), 11 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 12 | ngx_conf_set_num_slot, 13 | NGX_HTTP_LOC_CONF_OFFSET, 14 | BASE_OFFSET + offsetof(ngx_http_vod_volume_map_loc_conf_t, interval), 15 | NULL }, 16 | 17 | #undef BASE_OFFSET -------------------------------------------------------------------------------- /vod/hls/buffer_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __BUFFER_FILTER_H__ 2 | #define __BUFFER_FILTER_H__ 3 | 4 | // include 5 | #include "media_filter.h" 6 | 7 | // functions 8 | vod_status_t buffer_filter_init( 9 | media_filter_t* filter, 10 | media_filter_context_t* context, 11 | bool_t align_frames, 12 | uint32_t size); 13 | 14 | vod_status_t buffer_filter_force_flush( 15 | media_filter_context_t* context, 16 | bool_t last_stream_frame); 17 | 18 | bool_t buffer_filter_get_dts( 19 | media_filter_context_t* context, 20 | uint64_t* dts); 21 | 22 | 23 | void buffer_filter_simulated_force_flush( 24 | media_filter_context_t* context, 25 | bool_t last_stream_frame); 26 | 27 | #endif // __BUFFER_FILTER_H__ 28 | -------------------------------------------------------------------------------- /vod/languages_hash_params.h: -------------------------------------------------------------------------------- 1 | // generated by languages_hash_size.py 2 | 3 | #define ISO639_3_HASH_TOTAL_SIZE (2547) 4 | 5 | static const language_hash_offsets_t iso639_3_hash_offsets[] = { 6 | { 0, 361 }, 7 | { 361, 148 }, 8 | { 509, 191 }, 9 | { 700, 85 }, 10 | { 785, 19 }, 11 | { 804, 33 }, 12 | { 837, 111 }, 13 | { 948, 146 }, 14 | { 1094, 65 }, 15 | { 1159, 13 }, 16 | { 1172, 212 }, 17 | { 1384, 109 }, 18 | { 1493, 171 }, 19 | { 1664, 108 }, 20 | { 1772, 45 }, 21 | { 1817, 93 }, 22 | { 1910, 8 }, 23 | { 1918, 35 }, 24 | { 1953, 345 }, 25 | { 2298, 178 }, 26 | { 2476, 18 }, 27 | { 2494, 15 }, 28 | { 2509, 9 }, 29 | { 2518, 15 }, 30 | { 2533, 3 }, 31 | { 2536, 11 }, 32 | }; 33 | -------------------------------------------------------------------------------- /ngx_http_vod_module.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_MODULE_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_MODULE_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | #include 7 | 8 | // globals 9 | extern ngx_module_t ngx_http_vod_module; 10 | 11 | // main 12 | ngx_int_t ngx_http_vod_handler(ngx_http_request_t *r); 13 | 14 | // variables 15 | ngx_int_t ngx_http_vod_preconfiguration(ngx_conf_t *cf); 16 | 17 | // handlers 18 | ngx_int_t ngx_http_vod_local_request_handler(ngx_http_request_t *r); 19 | ngx_int_t ngx_http_vod_mapped_request_handler(ngx_http_request_t *r); 20 | ngx_int_t ngx_http_vod_remote_request_handler(ngx_http_request_t *r); 21 | 22 | #endif // _NGX_HTTP_VOD_MODULE_H_INCLUDED_ 23 | -------------------------------------------------------------------------------- /vod/mp4/mp4_cenc_decrypt.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_CENC_DECRYPT_H__ 2 | #define __MP4_CENC_DECRYPT_H__ 3 | 4 | // includes 5 | #include "mp4_parser.h" 6 | 7 | // globals 8 | extern frames_source_t mp4_cenc_decrypt_frames_source; 9 | 10 | // functions 11 | vod_status_t mp4_cenc_decrypt_init( 12 | request_context_t* request_context, 13 | frames_source_t* frames_source, 14 | void* frames_source_context, 15 | u_char* key, 16 | media_encryption_t* encryption, 17 | void** result); 18 | 19 | u_char* mp4_cenc_decrypt_get_key(void* context); 20 | 21 | void mp4_cenc_decrypt_get_original_source( 22 | void* ctx, 23 | frames_source_t** frames_source, 24 | void** frames_source_context); 25 | 26 | #endif //__MP4_CENC_DECRYPT_H__ 27 | -------------------------------------------------------------------------------- /vod/media_set.c: -------------------------------------------------------------------------------- 1 | #include "media_set.h" 2 | 3 | int64_t 4 | media_set_get_segment_time_millis(media_set_t* media_set) 5 | { 6 | media_track_t* cur_track; 7 | 8 | // try to find a track that has frames, if no track is found, fallback to the first track 9 | for (cur_track = media_set->filtered_tracks; ; cur_track += media_set->total_track_count) 10 | { 11 | if (cur_track >= media_set->filtered_tracks_end) 12 | { 13 | cur_track = media_set->filtered_tracks; 14 | break; 15 | } 16 | 17 | if (cur_track->frame_count > 0) 18 | { 19 | break; 20 | } 21 | } 22 | 23 | return cur_track->original_clip_time + 24 | rescale_time(cur_track->first_frame_time_offset, cur_track->media_info.timescale, 1000); 25 | } 26 | -------------------------------------------------------------------------------- /ngx_http_vod_mss_commands.h: -------------------------------------------------------------------------------- 1 | #define BASE_OFFSET offsetof(ngx_http_vod_loc_conf_t, mss) 2 | 3 | { ngx_string("vod_mss_manifest_file_name_prefix"), 4 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 5 | ngx_conf_set_str_slot, 6 | NGX_HTTP_LOC_CONF_OFFSET, 7 | BASE_OFFSET + offsetof(ngx_http_vod_mss_loc_conf_t, manifest_file_name_prefix), 8 | NULL }, 9 | 10 | { ngx_string("vod_mss_duplicate_bitrate_threshold"), 11 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 12 | ngx_conf_set_num_slot, 13 | NGX_HTTP_LOC_CONF_OFFSET, 14 | BASE_OFFSET + offsetof(ngx_http_vod_mss_loc_conf_t, manifest_conf.duplicate_bitrate_threshold), 15 | NULL }, 16 | 17 | #undef BASE_OFFSET 18 | -------------------------------------------------------------------------------- /vod/parse_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __PARSE_UTILS_H__ 2 | #define __PARSE_UTILS_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | #include "media_format.h" 7 | 8 | // functions 9 | vod_status_t parse_utils_parse_guid_string(vod_str_t* str, u_char* output); 10 | 11 | vod_status_t parse_utils_parse_fixed_base64_string(vod_str_t* str, u_char* output, size_t output_size); 12 | 13 | vod_status_t parse_utils_parse_variable_base64_string(vod_pool_t* pool, vod_str_t* str, vod_str_t* result); 14 | 15 | u_char* parse_utils_extract_uint32_token(u_char* start_pos, u_char* end_pos, uint32_t* result); 16 | 17 | u_char* parse_utils_extract_track_tokens(u_char* start_pos, u_char* end_pos, track_mask_t* result); 18 | 19 | #endif // __PARSE_UTILS_H__ 20 | -------------------------------------------------------------------------------- /test/get_perf_counters_as_csv.php: -------------------------------------------------------------------------------- 1 | \nthe url is usually 'http://server-name/vod_status'\n"); 6 | } 7 | 8 | // get the status xml 9 | $status = file_get_contents($argv[1]); 10 | 11 | // convert the xml to array 12 | $xml = @simplexml_load_string($status); 13 | $json = json_encode($xml); 14 | $array = json_decode($json, true); 15 | 16 | // print the result 17 | $pc = $array["performance_counters"]; 18 | 19 | echo "name,sum,count,max,max_time\n"; 20 | foreach ($pc as $key => $row) 21 | { 22 | echo $key; 23 | foreach ($row as $cell) 24 | { 25 | echo ",".$cell; 26 | } 27 | echo "\n"; 28 | } 29 | -------------------------------------------------------------------------------- /conf/kaltura-nginx.conf.template: -------------------------------------------------------------------------------- 1 | include @NGINX_CONF_PATH@/main.conf; 2 | 3 | http { 4 | upstream kalapi { 5 | server @WWW_HOST@; 6 | } 7 | 8 | include @NGINX_CONF_PATH@/http.conf; 9 | 10 | # vod mapped settings 11 | vod_mode mapped; 12 | vod_upstream_location /kalapi_proxy; 13 | vod_upstream_extra_args "pathOnly=1"; 14 | vod_mapping_cache mapping_cache 64m; 15 | 16 | # file handle caching / aio 17 | open_file_cache max=1000 inactive=5m; 18 | open_file_cache_valid 2m; 19 | open_file_cache_min_uses 1; 20 | open_file_cache_errors on; 21 | aio on; 22 | 23 | server { 24 | listen @VOD_PACKAGER_PORT@; 25 | server_name @VOD_PACKAGER_HOST@; 26 | include @NGINX_CONF_PATH@/server.conf; 27 | } 28 | 29 | include @NGINX_CONF_PATH@/ssl.conf; 30 | } 31 | -------------------------------------------------------------------------------- /scripts/encrypt_url.rb: -------------------------------------------------------------------------------- 1 | require 'openssl' 2 | require 'base64' 3 | 4 | if ARGV.length != 4 5 | raise "Usage:\n\truby \n" 6 | end 7 | 8 | base_url = ARGV[0] 9 | encrypted_part = ARGV[1] 10 | encryption_key = [ARGV[2]].pack("H*") 11 | encryption_iv = [ARGV[3]].pack("H*") 12 | 13 | hash_size = 8 14 | 15 | signed_url = Digest::MD5.digest(encrypted_part)[0,hash_size] + encrypted_part 16 | 17 | cipher = OpenSSL::Cipher::AES.new('256-CBC') 18 | cipher.encrypt 19 | cipher.key = encryption_key 20 | cipher.iv = encryption_iv 21 | 22 | encrypted_data = cipher.update(signed_url) 23 | encrypted_data << cipher.final 24 | 25 | puts base_url + Base64.strict_encode64(encrypted_data).tr('+/', '-_').gsub(/(\s|=)*$/,'') 26 | -------------------------------------------------------------------------------- /test/download_stream.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import manifest_utils 3 | import http_utils 4 | import sys 5 | import os 6 | 7 | if len(sys.argv) < 3: 8 | print('Usage:\n\tpython %s ' % os.path.basename(__file__)) 9 | sys.exit(1) 10 | 11 | _, manifestUrl, outputPath = sys.argv 12 | 13 | code, headers, body = http_utils.getUrl(manifestUrl, {}) 14 | mimeType = headers['content-type'][0] 15 | urls = manifest_utils.getManifestUrls(manifestUrl, body, mimeType, {}) 16 | 17 | for curUrl in [manifestUrl] + urls: 18 | fileName = os.path.join(outputPath, os.path.split(curUrl)[1].split('?')[0]) 19 | if os.path.exists(fileName): 20 | print('Error: %s already exists' % fileName) 21 | break 22 | http_utils.downloadUrl(curUrl, fileName) 23 | -------------------------------------------------------------------------------- /vod/mp4/mp4_muxer.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_MUXER_H__ 2 | #define __MP4_MUXER_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | #include "../media_set.h" 7 | #include "../common.h" 8 | 9 | // typedefs 10 | struct mp4_muxer_state_s; 11 | typedef struct mp4_muxer_state_s mp4_muxer_state_t; 12 | 13 | // functions 14 | vod_status_t mp4_muxer_init_fragment( 15 | request_context_t* request_context, 16 | uint32_t segment_index, 17 | media_set_t* media_set, 18 | segment_writer_t* writers, 19 | bool_t per_stream_writer, 20 | bool_t reuse_buffers, 21 | bool_t size_only, 22 | vod_str_t* header, 23 | size_t* total_fragment_size, 24 | mp4_muxer_state_t** processor_state); 25 | 26 | vod_status_t mp4_muxer_process_frames(mp4_muxer_state_t* state); 27 | 28 | #endif // __MP4_MUXER_H__ 29 | -------------------------------------------------------------------------------- /vod/mss/mss_playready.h: -------------------------------------------------------------------------------- 1 | #ifndef __MSS_PLAYREADY_H__ 2 | #define __MSS_PLAYREADY_H__ 3 | 4 | // includes 5 | #include "mss_packager.h" 6 | #include "../media_format.h" 7 | #include "../segmenter.h" 8 | 9 | // functions 10 | vod_status_t mss_playready_build_manifest( 11 | request_context_t* request_context, 12 | mss_manifest_config_t* conf, 13 | media_set_t* media_set, 14 | vod_str_t* result); 15 | 16 | vod_status_t mss_playready_get_fragment_writer( 17 | segment_writer_t* segment_writer, 18 | request_context_t* request_context, 19 | media_set_t* media_set, 20 | uint32_t segment_index, 21 | bool_t single_nalu_per_frame, 22 | const u_char* iv, 23 | bool_t size_only, 24 | vod_str_t* fragment_header, 25 | size_t* total_fragment_size); 26 | 27 | #endif // __MSS_PLAYREADY_H__ 28 | -------------------------------------------------------------------------------- /vod/mp4/mp4_cenc_passthrough.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_CENC_PASSTHROUGH_H__ 2 | #define __MP4_CENC_PASSTHROUGH_H__ 3 | 4 | // includes 5 | #include "../media_set.h" 6 | 7 | // typedefs 8 | typedef struct { 9 | media_sequence_t* sequence; 10 | uint8_t default_auxiliary_sample_size; 11 | bool_t use_subsamples; 12 | size_t saiz_atom_size; 13 | size_t saio_atom_size; 14 | size_t auxiliary_info_size; 15 | size_t total_size; 16 | } mp4_cenc_passthrough_context_t; 17 | 18 | // functions 19 | bool_t mp4_cenc_passthrough_init( 20 | mp4_cenc_passthrough_context_t* context, 21 | media_sequence_t* sequence); 22 | 23 | u_char* mp4_cenc_passthrough_write_saiz_saio( 24 | mp4_cenc_passthrough_context_t* context, 25 | u_char* p, 26 | size_t auxiliary_data_offset); 27 | 28 | #endif //__MP4_CENC_PASSTHROUGH_H__ 29 | -------------------------------------------------------------------------------- /test/speed_test.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import urllib2 3 | import time 4 | import sys 5 | 6 | from speed_test_params import * 7 | 8 | def getUrl(url): 9 | request = urllib2.Request(url) 10 | try: 11 | f = urllib2.urlopen(request) 12 | except urllib2.HTTPError as e: 13 | return e.getcode() 14 | except urllib2.URLError as e: 15 | return 0 16 | return f.getcode() 17 | 18 | limit = int(sys.argv[2]) 19 | count = 0 20 | countByStatus = {} 21 | startTime = time.time() 22 | for curLine in file(sys.argv[1]): 23 | if count >= limit: 24 | break 25 | count += 1 26 | statusCode = getUrl(BASE_URL + curLine.strip()) 27 | countByStatus.setdefault(statusCode, 0) 28 | countByStatus[statusCode] += 1 29 | print('Done, took %s' % (time.time() - startTime)) 30 | print(countByStatus) 31 | -------------------------------------------------------------------------------- /ngx_async_open_file_cache.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | * Copyright (C) Nginx, Inc. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifndef _NGX_ASYNC_OPEN_FILE_CACHE_H_INCLUDED_ 14 | #define _NGX_ASYNC_OPEN_FILE_CACHE_H_INCLUDED_ 15 | 16 | 17 | typedef void(*ngx_async_open_file_callback_t)(void* context, ngx_int_t rc); 18 | 19 | 20 | ngx_int_t ngx_async_open_cached_file( 21 | ngx_open_file_cache_t *cache, 22 | ngx_str_t *name, 23 | ngx_open_file_info_t *of, 24 | ngx_pool_t *pool, 25 | ngx_thread_pool_t *tp, 26 | ngx_thread_task_t **taskp, 27 | ngx_async_open_file_callback_t callback, 28 | void* context); 29 | 30 | 31 | #endif /* _NGX_ASYNC_OPEN_FILE_CACHE_H_INCLUDED_ */ 32 | -------------------------------------------------------------------------------- /test/print_mp4_atoms.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from mp4_utils import * 3 | import sys 4 | 5 | MAX_READ_SIZE = 64 * 1024 * 1024 6 | 7 | inputFileName = sys.argv[1] 8 | inputData = file(inputFileName, 'rb').read(MAX_READ_SIZE) 9 | 10 | def printAtoms(inputData, parsedAtoms, indent): 11 | for atomName, atoms in parsedAtoms.items(): 12 | print(indent + atomName) 13 | for startPos, atomHeaderSize, endPos, childAtoms in atoms: 14 | if childAtoms == {}: 15 | if endPos - startPos < 256: 16 | print(inputData[(startPos + atomHeaderSize):endPos].encode('hex')) 17 | else: 18 | print('atom size %s' % (endPos - startPos)) 19 | printAtoms(inputData, childAtoms, indent + ' ') 20 | 21 | parsedAtoms = parseAtoms(inputData, 0, len(inputData)) 22 | printAtoms(inputData, parsedAtoms, '') 23 | -------------------------------------------------------------------------------- /vod/hds/hds_manifest.h: -------------------------------------------------------------------------------- 1 | #ifndef __HDS_MANIFEST_H__ 2 | #define __HDS_MANIFEST_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | #include "../segmenter.h" 7 | #include "../common.h" 8 | 9 | // typedefs 10 | typedef struct { 11 | vod_str_t fragment_file_name_prefix; 12 | vod_str_t bootstrap_file_name_prefix; 13 | } hds_manifest_config_t; 14 | 15 | // functions 16 | vod_status_t hds_packager_build_bootstrap( 17 | request_context_t* request_context, 18 | media_set_t* media_set, 19 | vod_str_t* result); 20 | 21 | vod_status_t hds_packager_build_manifest( 22 | request_context_t* request_context, 23 | hds_manifest_config_t* conf, 24 | vod_str_t* base_url, 25 | vod_str_t* manifest_id, 26 | media_set_t* media_set, 27 | bool_t drm_enabled, 28 | vod_str_t* result); 29 | 30 | #endif // __HDS_MANIFEST_H__ 31 | -------------------------------------------------------------------------------- /vod/input/frames_source_cache.h: -------------------------------------------------------------------------------- 1 | #ifndef __FRAMES_SOURCE_CACHE_H__ 2 | #define __FRAMES_SOURCE_CACHE_H__ 3 | 4 | // includes 5 | #include "frames_source.h" 6 | #include "read_cache.h" 7 | 8 | // macros 9 | #define get_frame_part_source_clip(part) \ 10 | (part.frames_source == &frames_source_cache ? ((frames_source_cache_state_t*)part.frames_source_context)->req.source : NULL) 11 | 12 | // typedefs 13 | typedef struct { 14 | read_cache_state_t* read_cache_state; 15 | read_cache_request_t req; 16 | } frames_source_cache_state_t; 17 | 18 | // globals 19 | extern frames_source_t frames_source_cache; 20 | 21 | // functions 22 | vod_status_t frames_source_cache_init( 23 | request_context_t* request_context, 24 | read_cache_state_t* read_cache_state, 25 | void* source, 26 | int cache_slot_id, 27 | void** result); 28 | 29 | #endif //__FRAMES_SOURCE_CACHE_H__ 30 | -------------------------------------------------------------------------------- /test/g2o_curl.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import http_utils 3 | import urlparse 4 | import sys 5 | import os 6 | 7 | if len(sys.argv) < 2: 8 | print('Usage:\n\tpython %s [curl options] ' % os.path.basename(__file__)) 9 | sys.exit(1) 10 | 11 | switches = sys.argv[1:-1] 12 | url = sys.argv[-1] 13 | 14 | parsedUrl = urlparse.urlsplit(url) 15 | if parsedUrl.scheme not in ['http', 'https']: 16 | url = 'http://' + url 17 | parsedUrl = urlparse.urlsplit(url) 18 | 19 | uri = urlparse.urlunsplit(urlparse.SplitResult('','',parsedUrl.path,parsedUrl.query,parsedUrl.fragment)) 20 | 21 | headers = http_utils.getG2OHeaders(uri) 22 | headers = map(lambda x: '-H "%s: %s"' % x, headers.items()) 23 | 24 | cmdLine = 'curl %s %s "%s"' % (' '.join(map(lambda x: "'%s'" % x, switches)), ' '.join(headers), url) 25 | print('%s\n' % cmdLine) 26 | os.system(cmdLine) 27 | -------------------------------------------------------------------------------- /vod/filters/rate_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __RATE_FILTER_H__ 2 | #define __RATE_FILTER_H__ 3 | 4 | // includes 5 | #include "../media_set.h" 6 | #include "../json_parser.h" 7 | 8 | // typedefs 9 | typedef struct { 10 | media_clip_t base; 11 | vod_fraction_t rate; 12 | } media_clip_rate_filter_t; 13 | 14 | // functions 15 | void rate_filter_scale_track_timestamps( 16 | media_track_t* track, 17 | uint32_t speed_num, 18 | uint32_t speed_denom); 19 | 20 | vod_status_t rate_filter_parse( 21 | void* context, 22 | vod_json_object_t* element, 23 | void** result); 24 | 25 | vod_status_t rate_filter_create_from_string( 26 | request_context_t* request_context, 27 | vod_str_t* str, 28 | media_clip_t* source, 29 | media_clip_rate_filter_t** result); 30 | 31 | vod_status_t rate_filter_parser_init( 32 | vod_pool_t* pool, 33 | vod_pool_t* temp_pool); 34 | 35 | #endif // __RATE_FILTER_H__ 36 | -------------------------------------------------------------------------------- /test/buffer_cache/ngx_cycle.h: -------------------------------------------------------------------------------- 1 | // Stub implementation of ngx_cycle for testing the buffer cache 2 | 3 | #ifndef _NGX_CYCLE_H_INCLUDED_ 4 | #define _NGX_CYCLE_H_INCLUDED_ 5 | 6 | typedef struct ngx_shm_zone_s ngx_shm_zone_t; 7 | 8 | #include 9 | 10 | typedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data); 11 | 12 | struct ngx_shm_zone_s { 13 | void *data; 14 | ngx_shm_t shm; 15 | ngx_shm_zone_init_pt init; 16 | void *tag; 17 | }; 18 | 19 | extern volatile ngx_cycle_t *ngx_cycle; 20 | 21 | struct ngx_cycle_s { 22 | ngx_pool_t *pool; 23 | 24 | ngx_log_t *log; 25 | ngx_log_t new_log; 26 | }; 27 | 28 | ngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag); 29 | 30 | #endif // _NGX_CYCLE_H_INCLUDED_ 31 | -------------------------------------------------------------------------------- /tools/prefetch_proxy/README.md: -------------------------------------------------------------------------------- 1 | ## Segment prefetcher proxy for nginx-vod-module 2 | 3 | OpenResty®-based proxy for optimizing the delivery of VOD streams to remote regions by prefetching video segments. 4 | When the proxy gets a request for segment N, it starts pulling segments N+1 and N+2 in the background. 5 | The segments that are pulled from upstream are cached using nginx's proxy_cache directive. 6 | The proxy uses two types of caches - 7 | * long term - holds manifests, init segments and segments 1-3. Since this cache holds only a few segments per video, 8 | the assumption is that it can hold the objects for relatively long period. 9 | * short term - holds segment 4 and beyond - the purpose of this cache is to save the prefetched segments, 10 | until they are pulled by the CDN/client. 11 | 12 | ### Setup 13 | 14 | Replace the `{{ origin_domain }}` markers in nginx.conf with the domain of the origin. 15 | -------------------------------------------------------------------------------- /vod/cli/vod_array.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | 4 | int 5 | vod_array_init_impl(vod_array_t *array, unsigned n, size_t size) 6 | { 7 | array->nelts = 0; 8 | array->size = size; 9 | array->nalloc = n; 10 | 11 | array->elts = malloc(n * size); 12 | if (array->elts == NULL) 13 | { 14 | return VOD_ERROR; 15 | } 16 | 17 | return VOD_OK; 18 | } 19 | 20 | void* 21 | vod_array_push(vod_array_t *a) 22 | { 23 | void *elt, *new_elts; 24 | 25 | if (a->nelts >= a->nalloc) 26 | { 27 | new_elts = realloc(a->elts, a->size * a->nalloc * 2); 28 | if (new_elts == NULL) 29 | { 30 | return NULL; 31 | } 32 | a->elts = new_elts; 33 | a->nalloc *= 2; 34 | } 35 | 36 | elt = (u_char *) a->elts + a->size * a->nelts; 37 | a->nelts++; 38 | 39 | return elt; 40 | } 41 | 42 | void 43 | vod_array_destroy(vod_array_t *a) 44 | { 45 | free(a->elts); 46 | a->elts = NULL; 47 | } 48 | -------------------------------------------------------------------------------- /ngx_http_vod_hls_conf.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_HLS_CONF_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_HLS_CONF_H_INCLUDED_ 3 | 4 | // includes 5 | #include "vod/hls/m3u8_builder.h" 6 | #include "vod/hls/hls_muxer.h" 7 | 8 | // typedefs 9 | typedef struct 10 | { 11 | ngx_flag_t absolute_master_urls; 12 | ngx_flag_t absolute_index_urls; 13 | ngx_flag_t absolute_iframe_urls; 14 | ngx_str_t master_file_name_prefix; 15 | bool_t interleave_frames; 16 | bool_t align_frames; 17 | bool_t align_pts; 18 | bool_t output_id3_timestamps; 19 | ngx_http_complex_value_t* id3_data; 20 | vod_uint_t encryption_method; 21 | ngx_http_complex_value_t* encryption_key_uri; 22 | bool_t output_iv; 23 | 24 | // derived fields 25 | m3u8_config_t m3u8_config; 26 | } ngx_http_vod_hls_loc_conf_t; 27 | 28 | // globals 29 | extern ngx_conf_enum_t hls_encryption_methods[]; 30 | extern ngx_conf_enum_t hls_container_formats[]; 31 | 32 | #endif // _NGX_HTTP_VOD_HLS_CONF_H_INCLUDED_ 33 | -------------------------------------------------------------------------------- /vod/mkv/mkv_builder.h: -------------------------------------------------------------------------------- 1 | #ifndef __MKV_BUILDER_H__ 2 | #define __MKV_BUILDER_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | #include "../media_set.h" 7 | 8 | // typedefs 9 | typedef enum { 10 | MKV_CLEAR, 11 | MKV_CLEAR_LEAD, 12 | MKV_ENCRYPTED, 13 | } mkv_encryption_type_t; 14 | 15 | // functions 16 | vod_status_t mkv_build_init_segment( 17 | request_context_t* request_context, 18 | media_track_t* track, 19 | uint64_t track_uid, 20 | vod_str_t* result); 21 | 22 | vod_status_t mkv_builder_frame_writer_init( 23 | request_context_t* request_context, 24 | media_sequence_t* sequence, 25 | write_callback_t write_callback, 26 | void* write_context, 27 | bool_t reuse_buffers, 28 | mkv_encryption_type_t encryption_type, 29 | u_char* iv, 30 | vod_str_t* response_header, 31 | size_t* total_fragment_size, 32 | void** context); 33 | 34 | vod_status_t mkv_builder_frame_writer_process(void* context); 35 | 36 | #endif //__MKV_BUILDER_H__ 37 | -------------------------------------------------------------------------------- /vod/write_stream.h: -------------------------------------------------------------------------------- 1 | #ifndef __WRITE_STREAM_H__ 2 | #define __WRITE_STREAM_H__ 3 | 4 | // macros 5 | #define write_le32(p, dw) \ 6 | { \ 7 | *(p)++ = (dw)& 0xFF; \ 8 | *(p)++ = ((dw) >> 8) & 0xFF; \ 9 | *(p)++ = ((dw) >> 16) & 0xFF; \ 10 | *(p)++ = ((dw) >> 24) & 0xFF; \ 11 | } 12 | 13 | #define write_be16(p, w) \ 14 | { \ 15 | *(p)++ = ((w) >> 8) & 0xFF; \ 16 | *(p)++ = (w)& 0xFF; \ 17 | } 18 | 19 | #define write_be24(p, dw) \ 20 | { \ 21 | *(p)++ = ((dw) >> 16) & 0xFF; \ 22 | *(p)++ = ((dw) >> 8) & 0xFF; \ 23 | *(p)++ = (dw)& 0xFF; \ 24 | } 25 | 26 | #define write_be32(p, dw) \ 27 | { \ 28 | *(p)++ = ((dw) >> 24) & 0xFF; \ 29 | *(p)++ = ((dw) >> 16) & 0xFF; \ 30 | *(p)++ = ((dw) >> 8) & 0xFF; \ 31 | *(p)++ = (dw)& 0xFF; \ 32 | } 33 | 34 | #define write_be64(p, qw) \ 35 | { \ 36 | write_be32(p, (qw) >> 32); \ 37 | write_be32(p, (qw)); \ 38 | } 39 | 40 | #endif //__WRITE_STREAM_H__ 41 | -------------------------------------------------------------------------------- /vod/udrm.h: -------------------------------------------------------------------------------- 1 | #ifndef __UDRM_H__ 2 | #define __UDRM_H__ 3 | 4 | // constants 5 | #define DRM_SYSTEM_ID_SIZE (16) 6 | #define DRM_KEY_SIZE (16) 7 | #define DRM_KID_SIZE (16) 8 | #define DRM_IV_SIZE (16) 9 | 10 | // typedefs 11 | typedef struct { 12 | u_char system_id[DRM_SYSTEM_ID_SIZE]; 13 | vod_str_t data; 14 | } drm_system_info_t; 15 | 16 | typedef struct { 17 | uint32_t count; 18 | drm_system_info_t* first; 19 | drm_system_info_t* last; 20 | } drm_system_info_array_t; 21 | 22 | typedef struct { 23 | u_char key_id[DRM_KID_SIZE]; 24 | u_char key[DRM_KEY_SIZE]; 25 | u_char iv[DRM_IV_SIZE]; 26 | bool_t iv_set; 27 | drm_system_info_array_t pssh_array; 28 | } drm_info_t; 29 | 30 | // functions 31 | vod_status_t udrm_parse_response( 32 | request_context_t* request_context, 33 | vod_str_t* drm_info, 34 | bool_t base64_decode_pssh, 35 | void** output); 36 | 37 | vod_status_t udrm_init_parser( 38 | vod_pool_t* pool, 39 | vod_pool_t* temp_pool); 40 | 41 | #endif // __UDRM_H__ 42 | -------------------------------------------------------------------------------- /vod/subtitle/subtitle_format.h: -------------------------------------------------------------------------------- 1 | #ifndef __SUBTITLE_FORMAT_H__ 2 | #define __SUBTITLE_FORMAT_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | 7 | // constants 8 | #define WEBVTT_HEADER_NEWLINES ("WEBVTT\r\n\r\n") 9 | #define UTF8_BOM ("\xEF\xBB\xBF") 10 | 11 | // typedefs 12 | typedef struct { 13 | media_base_metadata_t base; 14 | vod_str_t source; 15 | void* context; 16 | } subtitle_base_metadata_t; 17 | 18 | // functions 19 | vod_status_t subtitle_reader_init( 20 | request_context_t* request_context, 21 | void** ctx); 22 | 23 | vod_status_t subtitle_reader_read( 24 | void* ctx, 25 | uint64_t offset, 26 | vod_str_t* buffer, 27 | media_format_read_metadata_result_t* result); 28 | 29 | vod_status_t subtitle_parse( 30 | request_context_t* request_context, 31 | media_parse_params_t* parse_params, 32 | vod_str_t* source, 33 | void* context, 34 | uint64_t full_duration, 35 | size_t metadata_part_count, 36 | media_base_metadata_t** result); 37 | 38 | #endif //__SUBTITLE_FORMAT_H__ 39 | -------------------------------------------------------------------------------- /vod/write_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef __WRITE_BUFFER_H__ 2 | #define __WRITE_BUFFER_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | // typedefs 8 | typedef struct { 9 | request_context_t* request_context; 10 | write_callback_t write_callback; 11 | void* write_context; 12 | bool_t reuse_buffers; 13 | 14 | u_char* start_pos; 15 | u_char* cur_pos; 16 | u_char* end_pos; 17 | } write_buffer_state_t; 18 | 19 | // functions 20 | void write_buffer_init( 21 | write_buffer_state_t* state, 22 | request_context_t* request_context, 23 | write_callback_t write_callback, 24 | void* write_context, 25 | bool_t reuse_buffers); 26 | 27 | vod_status_t write_buffer_flush(write_buffer_state_t* state, bool_t reallocate); 28 | 29 | vod_status_t write_buffer_get_bytes( 30 | write_buffer_state_t* state, 31 | size_t min_size, 32 | size_t* size, 33 | u_char** buffer); 34 | 35 | vod_status_t write_buffer_write( 36 | write_buffer_state_t* state, 37 | const u_char* buffer, 38 | size_t size); 39 | 40 | #endif // __WRITE_BUFFER_H__ 41 | -------------------------------------------------------------------------------- /test/decrypt_ts_segment.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from ts_utils import decryptTsSegment 3 | import struct 4 | import sys 5 | import os 6 | 7 | if len(sys.argv) < 5: 8 | print('Usage:\n\tpython %s ' % os.path.basename(__file__)) 9 | sys.exit(1) 10 | 11 | INPUT_FILE = sys.argv[1] 12 | OUTPUT_FILE = sys.argv[2] 13 | ENC_KEY = sys.argv[3] 14 | ENC_IV = sys.argv[4] 15 | 16 | # key 17 | if ENC_KEY.startswith('0x'): 18 | ENC_KEY = ENC_KEY[2:] 19 | ENC_KEY = ENC_KEY.decode('hex') 20 | 21 | # iv 22 | if ENC_IV.startswith('0x'): 23 | ENC_IV = ENC_IV[2:] 24 | ENC_IV = ENC_IV.decode('hex') 25 | else: 26 | segmentIndex = int(ENC_IV) 27 | ENC_IV = '\0' * 12 + struct.pack('>L', segmentIndex) 28 | 29 | encryptedData = open(INPUT_FILE, 'rb').read() 30 | decryptedData, error = decryptTsSegment(encryptedData, ENC_KEY, ENC_IV) 31 | if decryptedData == None: 32 | print(error) 33 | sys.exit(1) 34 | open(OUTPUT_FILE, 'wb').write(decryptedData) 35 | -------------------------------------------------------------------------------- /vod/hls/aes_cbc_encrypt.h: -------------------------------------------------------------------------------- 1 | #ifndef __AES_CBC_ENCRYPT_H__ 2 | #define __AES_CBC_ENCRYPT_H__ 3 | 4 | // includes 5 | #include "../aes_defs.h" 6 | 7 | // typedefs 8 | typedef struct { 9 | request_context_t* request_context; 10 | buffer_pool_t* buffer_pool; 11 | write_callback_t callback; 12 | void* callback_context; 13 | EVP_CIPHER_CTX* cipher; 14 | u_char last_block[AES_BLOCK_SIZE]; 15 | } aes_cbc_encrypt_context_t; 16 | 17 | // functions 18 | vod_status_t aes_cbc_encrypt_init( 19 | aes_cbc_encrypt_context_t** ctx, 20 | request_context_t* request_context, 21 | write_callback_t callback, 22 | void* callback_context, 23 | buffer_pool_t* buffer_pool, 24 | const u_char* key, 25 | const u_char* iv); 26 | 27 | vod_status_t aes_cbc_encrypt( 28 | aes_cbc_encrypt_context_t* state, 29 | vod_str_t* dest, 30 | vod_str_t* src, 31 | bool_t flush); 32 | 33 | vod_status_t aes_cbc_encrypt_write( 34 | aes_cbc_encrypt_context_t* ctx, 35 | u_char* buffer, 36 | uint32_t size); 37 | 38 | #endif // __AES_CBC_ENCRYPT_H__ 39 | -------------------------------------------------------------------------------- /vod/filters/volume_map.h: -------------------------------------------------------------------------------- 1 | #ifndef __VOLUME_MAP_H__ 2 | #define __VOLUME_MAP_H__ 3 | 4 | // includes 5 | #include "../media_set.h" 6 | #include 7 | 8 | // constants 9 | #define VOLUME_MAP_INPUT_SAMPLE_FORMAT (AV_SAMPLE_FMT_FLTP) 10 | 11 | // audio filter encoder functions 12 | vod_status_t volume_map_encoder_init( 13 | request_context_t* request_context, 14 | uint32_t timescale, 15 | vod_array_t* frames_array, 16 | void** result); 17 | 18 | vod_status_t volume_map_encoder_update_media_info( 19 | void* context, 20 | media_info_t* media_info); 21 | 22 | vod_status_t volume_map_encoder_write_frame( 23 | void* context, 24 | AVFrame* frame); 25 | 26 | // writer functions 27 | vod_status_t volume_map_writer_init( 28 | request_context_t* request_context, 29 | media_set_t* media_set, 30 | uint32_t interval, 31 | write_callback_t write_callback, 32 | void* write_context, 33 | void** result); 34 | 35 | vod_status_t volume_map_writer_process( 36 | void* state); 37 | 38 | #endif // __VOLUME_MAP_H__ 39 | -------------------------------------------------------------------------------- /vod/hds/hds_amf0_encoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __HDS_AMF0_ENCODER_H__ 2 | #define __HDS_AMF0_ENCODER_H__ 3 | 4 | // includes 5 | #include "../media_set.h" 6 | 7 | // codecs 8 | #define CODEC_ID_AVC (0x7) 9 | #define SOUND_FORMAT_MP3 (0x2) 10 | #define SOUND_FORMAT_AAC (0xA) 11 | 12 | // buffer size limit 13 | #define AMF0_NUMBER sizeof(u_char) + sizeof(double) 14 | #define AMF0_BOOLEAN sizeof(u_char) + sizeof(u_char) 15 | 16 | #define AMF0_FIELD(group, name, type) sizeof(uint16_t) + sizeof(#name) - 1 + type + 17 | 18 | static const uint32_t amf0_max_total_size = 19 | sizeof(u_char) + sizeof(uint16_t) + sizeof("onMetaData") - 1 + // on metadata string 20 | sizeof(u_char) + sizeof(uint32_t) + // array header 21 | #include "hds_amf0_fields_x.h" 22 | sizeof(uint16_t) + sizeof(u_char); // array footer 23 | 24 | #undef AMF0_FIELD 25 | 26 | // functions 27 | u_char* hds_amf0_write_base64_metadata(u_char* p, u_char* temp_buffer, media_set_t* media_set, media_track_t** tracks); 28 | 29 | #endif //__HDS_AMF0_ENCODER_H__ 30 | -------------------------------------------------------------------------------- /vod/mp4/mp4_init_segment.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_INIT_SEGMENT_H__ 2 | #define __MP4_INIT_SEGMENT_H__ 3 | 4 | // includes 5 | #include "../media_set.h" 6 | 7 | // typedefs 8 | typedef u_char* (*atom_writer_func_t)(void* context, u_char* p); 9 | 10 | typedef struct { 11 | size_t atom_size; 12 | atom_writer_func_t write; 13 | void* context; 14 | } atom_writer_t; 15 | 16 | // functions 17 | vod_status_t mp4_init_segment_build_stsd_atom( 18 | request_context_t* request_context, 19 | media_track_t* track); 20 | 21 | vod_status_t mp4_init_segment_build( 22 | request_context_t* request_context, 23 | media_set_t* media_set, 24 | bool_t size_only, 25 | atom_writer_t* extra_moov_atoms_writer, 26 | atom_writer_t* stsd_atom_writers, 27 | vod_str_t* result); 28 | 29 | vod_status_t mp4_init_segment_get_encrypted_stsd_writers( 30 | request_context_t* request_context, 31 | media_set_t* media_set, 32 | uint32_t scheme_type, 33 | bool_t has_clear_lead, 34 | u_char* default_kid, 35 | u_char* iv, 36 | atom_writer_t** result); 37 | 38 | #endif // __MP4_INIT_SEGMENT_H__ 39 | -------------------------------------------------------------------------------- /vod/filters/audio_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUDIO_FILTER_H__ 2 | #define __AUDIO_FILTER_H__ 3 | 4 | // includes 5 | #include "../media_set.h" 6 | 7 | // typedefs 8 | struct audio_filter_s { 9 | uint32_t(*get_filter_desc_size)(media_clip_t* clip); 10 | u_char* (*append_filter_desc)(u_char* p, media_clip_t* clip); 11 | }; 12 | 13 | typedef struct audio_filter_s audio_filter_t; 14 | 15 | // functions 16 | void audio_filter_process_init(vod_log_t* log); 17 | 18 | vod_status_t audio_filter_alloc_state( 19 | request_context_t* request_context, 20 | media_sequence_t* sequence, 21 | media_clip_t* clip, 22 | media_track_t* output_track, 23 | uint32_t max_frame_count, 24 | uint32_t output_codec_id, 25 | size_t* cache_buffer_count, 26 | void** result); 27 | 28 | void audio_filter_free_state(void* context); 29 | 30 | vod_status_t audio_filter_process(void* context); 31 | 32 | vod_status_t audio_filter_alloc_memory_frame( 33 | request_context_t* request_context, 34 | vod_array_t* frames_array, 35 | size_t size, 36 | input_frame_t** result); 37 | 38 | #endif // __AUDIO_FILTER_H__ 39 | -------------------------------------------------------------------------------- /vod/hls/bit_fields.def: -------------------------------------------------------------------------------- 1 | adts_frame_header 2 | syncword : 12 3 | id : 1 4 | layer : 2 5 | protection_absent : 1 6 | profile_object_type : 2 7 | sample_rate_index: 4 8 | private_bit : 1 9 | channel_configuration : 3 10 | original_copy : 1 11 | home : 1 12 | copyright_identification_bit : 1 13 | copyright_identification_start : 1 14 | aac_frame_length : 13 15 | adts_buffer_fullness : 11 16 | number_of_raw_data_blocks_in_frame : 2 17 | 18 | pmt 19 | pointer_field : 8 20 | table_id : 8 21 | section_syntax_indicator : 1 22 | zero : 1 23 | reserved1 : 2 // 11 24 | reserved2 : 2 // 00 25 | section_length : 10 26 | program_number : 16 27 | reserved3 : 2 // 11 28 | version_number : 5 29 | current_next_indicator : 1 30 | section_number : 8 // 0 31 | last_section_number : 8 // 0 32 | reserved4 : 3 33 | pcr_pid : 13 34 | reserved5 : 4 35 | reserved6 : 2 // 00 36 | program_info_length : 10 37 | 38 | pmt_entry 39 | stream_type : 8 40 | reserved1 : 3 41 | elementary_pid : 13 42 | reserved2 : 4 43 | reserved3 : 2 // 00 44 | es_info_length : 10 45 | -------------------------------------------------------------------------------- /vod/hds/hds_fragment.h: -------------------------------------------------------------------------------- 1 | #ifndef __HDS_FRAGMENT_H__ 2 | #define __HDS_FRAGMENT_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | #include "../media_set.h" 7 | #include "../common.h" 8 | #include "hds_encryption.h" 9 | 10 | // constants 11 | #define HDS_TIMESCALE (1000) 12 | 13 | // macros 14 | #define hds_rescale_millis(millis) (millis) 15 | 16 | // typedefs 17 | struct hds_muxer_state_s; 18 | typedef struct hds_muxer_state_s hds_muxer_state_t; 19 | 20 | typedef struct { 21 | bool_t generate_moof_atom; 22 | } hds_fragment_config_t; 23 | 24 | // functions 25 | vod_status_t hds_muxer_init_fragment( 26 | request_context_t* request_context, 27 | hds_fragment_config_t* conf, 28 | hds_encryption_params_t* encryption_params, 29 | uint32_t segment_index, 30 | media_set_t* media_set, 31 | write_callback_t write_callback, 32 | void* write_context, 33 | bool_t size_only, 34 | vod_str_t* header, 35 | size_t* total_fragment_size, 36 | hds_muxer_state_t** processor_state); 37 | 38 | vod_status_t hds_muxer_process_frames(hds_muxer_state_t* state); 39 | 40 | #endif // __HDS_FRAGMENT_H__ 41 | -------------------------------------------------------------------------------- /test/verify_test_entries.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from test_base import * 3 | import urllib2 4 | import httplib 5 | import sys 6 | import os 7 | 8 | # Note: need to disable the moov atom cache and the response cache when running the tests 9 | 10 | NGINX_LOG_PATH = '/var/log/nginx/error.log' 11 | BASE_URL = 'http://localhost:8001/mapped' 12 | 13 | for curLine in sys.stdin: 14 | curLine = curLine.strip() 15 | if len(curLine) == 0: 16 | break 17 | splittedLine = curLine.split(' ', 3) 18 | if len(splittedLine) < 4: 19 | continue 20 | refId, uri, expectedStatusCode, message = splittedLine 21 | print('testing %s %s' % (refId, uri)) 22 | logTracker = LogTracker() 23 | try: 24 | f = urllib2.urlopen(BASE_URL + uri) 25 | statusCode = f.getcode() 26 | f.read() 27 | except urllib2.HTTPError as e: 28 | statusCode = e.getcode() 29 | except httplib.BadStatusLine: 30 | statusCode = 0 31 | if message != 'None': 32 | logTracker.assertContains(message) 33 | assert(expectedStatusCode == str(statusCode)) 34 | -------------------------------------------------------------------------------- /vod/language_code.h: -------------------------------------------------------------------------------- 1 | #ifndef __LANGUAGE_CODE_H__ 2 | #define __LANGUAGE_CODE_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | // constants 8 | #define LANG_ISO639_3_LEN (3) 9 | #define LANG_MASK_SIZE vod_array_length_for_bits(VOD_LANG_COUNT) 10 | 11 | // macros 12 | #define iso639_3_str_to_int(x) \ 13 | ((((((uint16_t)(x)[0]) - 'a' + 1) & 0x1f) << 10) | \ 14 | (((((uint16_t)(x)[1]) - 'a' + 1) & 0x1f) << 5) | \ 15 | (((((uint16_t)(x)[2]) - 'a' + 1) & 0x1f))) 16 | 17 | // typedefs 18 | enum { 19 | #define LANG(id, iso639_1, iso639_2b, iso639_3, name, native_name) VOD_LANG_##id, 20 | #include "languages_x.h" 21 | #undef LANG 22 | 23 | VOD_LANG_COUNT 24 | }; 25 | 26 | typedef uint16_t language_id_t; 27 | 28 | // functions 29 | vod_status_t language_code_process_init(vod_pool_t* pool, vod_log_t* log); 30 | 31 | language_id_t lang_parse_iso639_3_code(uint16_t code); 32 | 33 | void lang_get_native_name(language_id_t id, vod_str_t* result); 34 | 35 | const char* lang_get_rfc_5646_name(language_id_t id); 36 | 37 | const char* lang_get_iso639_3_name(language_id_t id); 38 | 39 | #endif //__LANGUAGE_CODE_H__ 40 | -------------------------------------------------------------------------------- /vod/mp4/mp4_clipper.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_CLIPPER_H__ 2 | #define __MP4_CLIPPER_H__ 3 | 4 | // includes 5 | #include "mp4_parser_base.h" 6 | 7 | // typedefs 8 | typedef struct { 9 | atom_info_t atom; 10 | uint64_t duration; 11 | u_char version; 12 | } mvhd_clip_result_t; 13 | 14 | typedef struct { 15 | media_clipper_parse_result_t base; 16 | mvhd_clip_result_t mvhd; 17 | vod_array_t parsed_traks; 18 | bool_t copy_data; 19 | size_t alloc_size; 20 | size_t moov_atom_size; 21 | } mp4_clipper_parse_result_t; 22 | 23 | // functions 24 | vod_status_t mp4_clipper_parse_moov( 25 | request_context_t* request_context, 26 | media_parse_params_t* parse_params, 27 | vod_str_t* metadata_parts, 28 | size_t metadata_part_count, 29 | bool_t copy_data, 30 | media_clipper_parse_result_t** result); 31 | 32 | vod_status_t mp4_clipper_build_header( 33 | request_context_t* request_context, 34 | vod_str_t* metadata_parts, 35 | size_t metadata_part_count, 36 | media_clipper_parse_result_t* parse_result, 37 | vod_chain_t** result, 38 | size_t* response_size, 39 | vod_str_t* content_type); 40 | 41 | #endif // __MP4_CLIPPER_H__ 42 | -------------------------------------------------------------------------------- /vod/mkv/mkv_defs.c: -------------------------------------------------------------------------------- 1 | #include "../media_format.h" 2 | #include "../mp4/mp4_defs.h" 3 | #include "mkv_defs.h" 4 | 5 | // constants 6 | mkv_codec_type_t mkv_codec_types[] = { 7 | // video 8 | { vod_string("V_MPEG4/ISO/AVC"), VOD_CODEC_ID_AVC, FORMAT_AVC1, TRUE }, 9 | { vod_string("V_MPEGH/ISO/HEVC"), VOD_CODEC_ID_HEVC, FORMAT_HVC1, TRUE }, 10 | { vod_string("V_VP8"), VOD_CODEC_ID_VP8, 0, FALSE }, 11 | { vod_string("V_VP9"), VOD_CODEC_ID_VP9, 0, FALSE }, 12 | { vod_string("V_AV1"), VOD_CODEC_ID_AV1, 0, FALSE }, 13 | 14 | // audio 15 | { vod_string("A_AAC"), VOD_CODEC_ID_AAC, FORMAT_MP4A, TRUE }, 16 | { vod_string("A_MPEG/L3"), VOD_CODEC_ID_MP3, FORMAT_MP4A, FALSE }, 17 | { vod_string("A_VORBIS"), VOD_CODEC_ID_VORBIS,0, TRUE }, 18 | { vod_string("A_OPUS"), VOD_CODEC_ID_OPUS, FORMAT_OPUS, TRUE }, 19 | { vod_string("A_AC3"), VOD_CODEC_ID_AC3, FORMAT_AC3, FALSE }, 20 | { vod_string("A_EAC3"), VOD_CODEC_ID_EAC3, FORMAT_EAC3, FALSE }, 21 | { vod_string("A_DTS"), VOD_CODEC_ID_DTS, 0, TRUE }, 22 | { vod_string("A_FLAC"), VOD_CODEC_ID_FLAC, FORMAT_FLAC, TRUE }, 23 | 24 | { vod_null_string, 0, 0, FALSE } 25 | }; 26 | -------------------------------------------------------------------------------- /vod/filters/audio_encoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUDIO_ENCODER_H__ 2 | #define __AUDIO_ENCODER_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | #include 7 | 8 | // constants 9 | #define AUDIO_ENCODER_INPUT_SAMPLE_FORMAT (AV_SAMPLE_FMT_S16) 10 | 11 | //typedefs 12 | typedef struct 13 | { 14 | uint64_t channel_layout; 15 | uint16_t channels; 16 | uint32_t sample_rate; 17 | uint32_t timescale; 18 | uint32_t bitrate; 19 | } audio_encoder_params_t; 20 | 21 | // functions 22 | void audio_encoder_process_init( 23 | vod_log_t* log); 24 | 25 | vod_status_t audio_encoder_init( 26 | request_context_t* request_context, 27 | audio_encoder_params_t* params, 28 | vod_array_t* frames_array, 29 | void** result); 30 | 31 | void audio_encoder_free( 32 | void* context); 33 | 34 | size_t audio_encoder_get_frame_size( 35 | void* context); 36 | 37 | vod_status_t audio_encoder_write_frame( 38 | void* context, 39 | AVFrame* frame); 40 | 41 | vod_status_t audio_encoder_flush( 42 | void* context); 43 | 44 | vod_status_t audio_encoder_update_media_info( 45 | void* context, 46 | media_info_t* media_info); 47 | 48 | #endif // __AUDIO_ENCODER_H__ 49 | -------------------------------------------------------------------------------- /vod/filters/audio_decoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __AUDIO_DECODER_H__ 2 | #define __AUDIO_DECODER_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | #include 7 | 8 | // macros 9 | #define audio_decoder_has_frame(decoder) \ 10 | ((decoder)->cur_frame < (decoder)->cur_frame_part.last_frame) 11 | 12 | // typedefs 13 | typedef struct { 14 | request_context_t* request_context; 15 | AVCodecContext* decoder; 16 | AVFrame* decoded_frame; 17 | 18 | frame_list_part_t cur_frame_part; 19 | input_frame_t* cur_frame; 20 | uint64_t dts; 21 | 22 | u_char* frame_buffer; 23 | uint32_t max_frame_size; 24 | uint32_t cur_frame_pos; 25 | bool_t data_handled; 26 | bool_t frame_started; 27 | } audio_decoder_state_t; 28 | 29 | // functions 30 | void audio_decoder_process_init(vod_log_t* log); 31 | 32 | vod_status_t audio_decoder_init( 33 | audio_decoder_state_t* state, 34 | request_context_t* request_context, 35 | media_track_t* track, 36 | int cache_slot_id); 37 | 38 | void audio_decoder_free(audio_decoder_state_t* state); 39 | 40 | vod_status_t audio_decoder_get_frame( 41 | audio_decoder_state_t* state, 42 | AVFrame** result); 43 | 44 | #endif // __AUDIO_DECODER_H__ 45 | -------------------------------------------------------------------------------- /vod/dash/edash_packager.h: -------------------------------------------------------------------------------- 1 | #ifndef __EDASH_PACKAGER_H__ 2 | #define __EDASH_PACKAGER_H__ 3 | 4 | // includes 5 | #include "dash_packager.h" 6 | #include "../udrm.h" 7 | 8 | // constants 9 | #define EDASH_INIT_MP4_HAS_CLEAR_LEAD (0x01) 10 | #define EDASH_INIT_MP4_WRITE_PSSH (0x02) 11 | 12 | // functions 13 | vod_status_t edash_packager_build_mpd( 14 | request_context_t* request_context, 15 | dash_manifest_config_t* conf, 16 | vod_str_t* base_url, 17 | media_set_t* media_set, 18 | bool_t drm_single_key, 19 | vod_str_t* result); 20 | 21 | vod_status_t edash_packager_build_init_mp4( 22 | request_context_t* request_context, 23 | media_set_t* media_set, 24 | uint32_t flags, 25 | bool_t size_only, 26 | vod_str_t* result); 27 | 28 | vod_status_t edash_packager_get_fragment_writer( 29 | segment_writer_t* segment_writer, 30 | request_context_t* request_context, 31 | media_set_t* media_set, 32 | uint32_t segment_index, 33 | bool_t single_nalu_per_frame, 34 | const u_char* iv, 35 | bool_t size_only, 36 | vod_str_t* fragment_header, 37 | size_t* total_fragment_size); 38 | 39 | u_char* edash_packager_write_pssh( 40 | u_char* p, 41 | drm_system_info_t* cur_info); 42 | 43 | #endif //__EDASH_PACKAGER_H__ 44 | -------------------------------------------------------------------------------- /vod/write_buffer_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef __WRITE_BUFFER_QUEUE_H__ 2 | #define __WRITE_BUFFER_QUEUE_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | // typedefs 8 | typedef struct { 9 | vod_queue_t link; 10 | u_char* start_pos; 11 | u_char* cur_pos; 12 | u_char* end_pos; 13 | off_t end_offset; 14 | } buffer_header_t; 15 | 16 | typedef struct { 17 | // input params 18 | request_context_t* request_context; 19 | buffer_pool_t* output_buffer_pool; 20 | write_callback_t write_callback; 21 | void* write_context; 22 | bool_t reuse_buffers; 23 | 24 | vod_queue_t buffers; 25 | buffer_header_t* cur_write_buffer; 26 | 27 | void* last_writer_context; 28 | off_t cur_offset; 29 | } write_buffer_queue_t; 30 | 31 | // functions 32 | void write_buffer_queue_init( 33 | write_buffer_queue_t* queue, 34 | request_context_t* request_context, 35 | write_callback_t write_callback, 36 | void* write_context, 37 | bool_t reuse_buffers); 38 | u_char* write_buffer_queue_get_buffer(write_buffer_queue_t* queue, uint32_t size, void* writer_context); 39 | vod_status_t write_buffer_queue_send(write_buffer_queue_t* queue, off_t max_offset); 40 | vod_status_t write_buffer_queue_flush(write_buffer_queue_t* queue); 41 | 42 | #endif // __WRITE_BUFFER_QUEUE_H__ 43 | -------------------------------------------------------------------------------- /vod/hls/id3_encoder_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __ID3_ENCODER_FILTER_H__ 2 | #define __ID3_ENCODER_FILTER_H__ 3 | 4 | // includes 5 | #include "media_filter.h" 6 | #include "../media_format.h" 7 | #include "../common.h" 8 | 9 | // typedefs 10 | typedef struct { 11 | u_char file_identifier[4]; 12 | u_char version[1]; 13 | u_char flags[1]; 14 | u_char size[4]; 15 | } id3_file_header_t; 16 | 17 | typedef struct { 18 | u_char id[4]; 19 | u_char size[4]; 20 | u_char flags[2]; 21 | } id3_frame_header_t; 22 | 23 | typedef struct { 24 | u_char encoding[1]; 25 | } id3_text_frame_header_t; 26 | 27 | typedef struct { 28 | id3_file_header_t file_header; 29 | id3_frame_header_t frame_header; 30 | id3_text_frame_header_t text_frame_header; 31 | } id3_text_frame_t; 32 | 33 | typedef struct { 34 | // input 35 | media_filter_start_frame_t start_frame; 36 | media_filter_write_t write; 37 | media_filter_simulated_start_frame_t simulated_start_frame; 38 | media_filter_simulated_write_t simulated_write; 39 | 40 | // fixed 41 | id3_text_frame_t header; 42 | } id3_encoder_state_t; 43 | 44 | // functions 45 | void id3_encoder_init( 46 | id3_encoder_state_t* state, 47 | media_filter_t* filter, 48 | media_filter_context_t* context); 49 | 50 | #endif // __ID3_ENCODER_FILTER_H__ 51 | -------------------------------------------------------------------------------- /vod/mp4/mp4_aes_ctr.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_AES_CTR_H__ 2 | #define __MP4_AES_CTR_H__ 3 | 4 | // includes 5 | #include "../write_buffer.h" 6 | #include "../aes_defs.h" 7 | 8 | #define MP4_AES_CTR_KEY_SIZE (16) 9 | #define MP4_AES_CTR_IV_SIZE (8) 10 | #define MP4_AES_CTR_COUNTER_BUFFER_SIZE (AES_BLOCK_SIZE * 64) 11 | 12 | // typedefs 13 | typedef struct { 14 | request_context_t* request_context; 15 | EVP_CIPHER_CTX* cipher; 16 | u_char counter[MP4_AES_CTR_COUNTER_BUFFER_SIZE]; 17 | u_char encrypted_counter[MP4_AES_CTR_COUNTER_BUFFER_SIZE]; 18 | u_char* encrypted_pos; 19 | u_char* encrypted_end; 20 | } mp4_aes_ctr_state_t; 21 | 22 | // functions 23 | vod_status_t mp4_aes_ctr_init( 24 | mp4_aes_ctr_state_t* state, 25 | request_context_t* request_context, 26 | u_char* key); 27 | 28 | void mp4_aes_ctr_set_iv( 29 | mp4_aes_ctr_state_t* state, 30 | u_char* iv); 31 | 32 | vod_status_t mp4_aes_ctr_process( 33 | mp4_aes_ctr_state_t* state, 34 | u_char* dest, 35 | const u_char* src, 36 | uint32_t size); 37 | 38 | void mp4_aes_ctr_increment_be64( 39 | u_char* counter); 40 | 41 | vod_status_t mp4_aes_ctr_write_encrypted( 42 | mp4_aes_ctr_state_t* state, 43 | write_buffer_state_t* write_buffer, 44 | u_char* cur_pos, 45 | uint32_t write_size); 46 | 47 | #endif //__MP4_AES_CTR_H__ 48 | -------------------------------------------------------------------------------- /ngx_http_vod_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_UTILS_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_UTILS_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | #include "ngx_http_vod_request_parse.h" 7 | #include "ngx_http_vod_conf.h" 8 | #include "vod/common.h" 9 | 10 | // functions 11 | void ngx_http_vod_set_status_index(ngx_uint_t index); 12 | 13 | ngx_int_t ngx_http_vod_send_response(ngx_http_request_t *r, ngx_str_t *response, ngx_str_t* content_type); 14 | 15 | ngx_int_t ngx_http_vod_status_to_ngx_error( 16 | ngx_http_request_t* r, 17 | vod_status_t rc); 18 | 19 | ngx_flag_t ngx_http_vod_header_exists(ngx_http_request_t* r, ngx_str_t* searched_header); 20 | 21 | ngx_int_t ngx_http_vod_get_base_url( 22 | ngx_http_request_t* r, 23 | ngx_http_complex_value_t* conf_base_url, 24 | ngx_str_t* file_uri, 25 | ngx_str_t* result); 26 | 27 | ngx_int_t ngx_http_vod_merge_string_parts( 28 | ngx_http_request_t* r, 29 | ngx_str_t* parts, 30 | uint32_t part_count, 31 | ngx_str_t* result); 32 | 33 | ngx_int_t ngx_http_vod_range_parse( 34 | ngx_str_t* range, 35 | off_t content_length, 36 | off_t* out_start, 37 | off_t* out_end); 38 | 39 | ngx_int_t ngx_http_vod_set_expires( 40 | ngx_http_request_t *r, 41 | time_t expires_time); 42 | 43 | #endif // _NGX_HTTP_VOD_UTILS_H_INCLUDED_ 44 | -------------------------------------------------------------------------------- /scripts/languages_hash_size.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import os 3 | 4 | 5 | 6 | # get language codes 7 | codes = set([]) 8 | for curLine in open(os.path.join(os.path.split(__file__)[0], '../vod/languages_x.h')): 9 | if not curLine.startswith('LANG('): 10 | continue 11 | splitted = curLine.split(',') 12 | codes.add(splitted[3].split('"')[1]) 13 | if splitted[2].strip() != 'NULL': 14 | codes.add(splitted[2].split('"')[1]) 15 | 16 | # get hash size per starting letter 17 | sizes = [] 18 | for letter in range(26): 19 | curCodes = filter(lambda x: x.startswith(chr(ord('a') + letter)), codes) 20 | langIntCodes = map(lambda x: (((ord(x[0]) - 96) & 0x1f) << 10) | (((ord(x[1]) - 96) & 0x1f) << 5) | ((ord(x[2]) - 96) & 0x1f), curCodes) 21 | 22 | hashSize = len(langIntCodes) 23 | while True: 24 | if len(langIntCodes) == len(set(map(lambda x: x % hashSize, langIntCodes))): 25 | break 26 | hashSize += 1 27 | 28 | sizes.append(hashSize) 29 | 30 | # print the result 31 | print('// generated by languages_hash_size.py\n') 32 | print('#define ISO639_3_HASH_TOTAL_SIZE (%s)\n' % sum(sizes)) 33 | print('static const language_hash_offsets_t iso639_3_hash_offsets[] = {') 34 | pos = 0 35 | for size in sizes: 36 | print('\t{ %s, %s },' % (pos, size)) 37 | pos += size 38 | print('};') 39 | -------------------------------------------------------------------------------- /ngx_http_vod_hds_commands.h: -------------------------------------------------------------------------------- 1 | #define BASE_OFFSET offsetof(ngx_http_vod_loc_conf_t, hds) 2 | 3 | { ngx_string("vod_hds_absolute_manifest_urls"), 4 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 5 | ngx_conf_set_flag_slot, 6 | NGX_HTTP_LOC_CONF_OFFSET, 7 | BASE_OFFSET + offsetof(ngx_http_vod_hds_loc_conf_t, absolute_manifest_urls), 8 | NULL }, 9 | 10 | { ngx_string("vod_hds_manifest_file_name_prefix"), 11 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 12 | ngx_conf_set_str_slot, 13 | NGX_HTTP_LOC_CONF_OFFSET, 14 | BASE_OFFSET + offsetof(ngx_http_vod_hds_loc_conf_t, manifest_file_name_prefix), 15 | NULL }, 16 | 17 | { ngx_string("vod_hds_fragment_file_name_prefix"), 18 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 19 | ngx_conf_set_str_slot, 20 | NGX_HTTP_LOC_CONF_OFFSET, 21 | BASE_OFFSET + offsetof(ngx_http_vod_hds_loc_conf_t, manifest_config.fragment_file_name_prefix), 22 | NULL }, 23 | 24 | { ngx_string("vod_hds_generate_moof_atom"), 25 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 26 | ngx_conf_set_flag_slot, 27 | NGX_HTTP_LOC_CONF_OFFSET, 28 | BASE_OFFSET + offsetof(ngx_http_vod_hds_loc_conf_t, fragment_config.generate_moof_atom), 29 | NULL }, 30 | 31 | #undef BASE_OFFSET 32 | -------------------------------------------------------------------------------- /vod/filters/dynamic_clip.h: -------------------------------------------------------------------------------- 1 | #ifndef __DYNAMIC_CLIP_H__ 2 | #define __DYNAMIC_CLIP_H__ 3 | 4 | // includes 5 | #include "../json_parser.h" 6 | #include "../media_set.h" 7 | 8 | // typedefs 9 | struct media_clip_dynamic_s; 10 | typedef struct media_clip_dynamic_s media_clip_dynamic_t; 11 | 12 | // typedefs 13 | struct media_clip_dynamic_s { 14 | media_clip_t base; 15 | vod_str_t id; 16 | struct media_sequence_s* sequence; 17 | media_range_t* range; 18 | int64_t clip_time; 19 | uint32_t duration; 20 | uint32_t clip_from; 21 | media_clip_dynamic_t* next; 22 | }; 23 | 24 | // functions 25 | vod_status_t dynamic_clip_parse( 26 | void* context, 27 | vod_json_object_t* element, 28 | void** result); 29 | 30 | vod_status_t dynamic_clip_parser_init( 31 | vod_pool_t* pool, 32 | vod_pool_t* temp_pool); 33 | 34 | vod_status_t dynamic_clip_apply_mapping_json( 35 | media_clip_dynamic_t* clip, 36 | request_context_t* request_context, 37 | u_char* mapping, 38 | media_set_t* media_set); 39 | 40 | vod_status_t dynamic_clip_get_mapping_string( 41 | request_context_t* request_context, 42 | media_clip_dynamic_t* dynamic_clips_head, 43 | vod_str_t* result); 44 | 45 | vod_status_t dynamic_clip_apply_mapping_string( 46 | request_context_t* request_context, 47 | media_set_t* media_set, 48 | vod_str_t* mapping); 49 | 50 | #endif // __DYNAMIC_CLIP_H__ 51 | -------------------------------------------------------------------------------- /vod/common.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | int 4 | vod_get_int_print_len(uint64_t n) 5 | { 6 | int res = 1; 7 | while (n >= 10) 8 | { 9 | res++; 10 | n /= 10; 11 | } 12 | return res; 13 | } 14 | 15 | #ifdef VOD_IMPLEMENT_BIT_COUNT 16 | uint32_t 17 | vod_get_number_of_set_bits32(uint32_t i) 18 | { 19 | // variable-precision SWAR algorithm 20 | i = i - ((i >> 1) & 0x55555555); 21 | i = (i & 0x33333333) + ((i >> 2) & 0x33333333); 22 | return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; 23 | } 24 | 25 | uint32_t 26 | vod_get_number_of_set_bits64(uint64_t i) 27 | { 28 | return vod_get_number_of_set_bits32((uint32_t)(i & NGX_MAX_UINT32_VALUE)) + 29 | vod_get_number_of_set_bits32((uint32_t)(i >> 32)); 30 | } 31 | 32 | uint32_t 33 | vod_get_trailing_zeroes64(uint64_t i) 34 | { 35 | size_t k; 36 | 37 | for (k = 0; k < sizeof(i) * 8; k++) 38 | { 39 | if ((i >> k) & 1) 40 | { 41 | return k; 42 | } 43 | } 44 | return -1; /* undefined */ 45 | } 46 | #endif 47 | 48 | u_char* 49 | vod_append_hex_string(u_char* p, const u_char* buffer, uint32_t buffer_size) 50 | { 51 | const u_char* buffer_end = buffer + buffer_size; 52 | static const u_char hex_chars[] = "0123456789ABCDEF"; 53 | 54 | for (; buffer < buffer_end; buffer++) 55 | { 56 | *p++ = hex_chars[*buffer >> 4]; 57 | *p++ = hex_chars[*buffer & 0x0F]; 58 | } 59 | return p; 60 | } 61 | -------------------------------------------------------------------------------- /vod/mp4/mp4_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_PARSER_H__ 2 | #define __MP4_PARSER_H__ 3 | 4 | // includes 5 | #include "mp4_parser_base.h" 6 | 7 | // functions 8 | vod_status_t mp4_parser_get_ftyp_atom_into( 9 | request_context_t* request_context, 10 | const u_char* buffer, 11 | size_t buffer_size, 12 | const u_char** ptr, 13 | size_t* size); 14 | 15 | vod_status_t mp4_parser_get_moov_atom_info( 16 | request_context_t* request_context, 17 | const u_char* buffer, 18 | size_t buffer_size, 19 | off_t* offset, 20 | size_t* size); 21 | 22 | vod_status_t mp4_parser_uncompress_moov( 23 | request_context_t* request_context, 24 | const u_char* buffer, 25 | size_t size, 26 | size_t max_moov_size, 27 | u_char** out_buffer, 28 | off_t* moov_offset, 29 | size_t* moov_size); 30 | 31 | vod_status_t mp4_parser_parse_basic_metadata( 32 | request_context_t* request_context, 33 | media_parse_params_t* parse_params, 34 | vod_str_t* metadata_parts, 35 | size_t metadata_part_count, 36 | media_base_metadata_t** result); 37 | 38 | vod_status_t mp4_parser_parse_frames( 39 | request_context_t* request_context, 40 | media_base_metadata_t* base, 41 | media_parse_params_t* parse_params, 42 | struct segmenter_conf_s* segmenter, 43 | read_cache_state_t* read_cache_state, 44 | vod_str_t* frame_data, 45 | media_format_read_request_t* read_req, 46 | media_track_array_t* result); 47 | 48 | #endif // __MP4_PARSER_H__ 49 | -------------------------------------------------------------------------------- /tools/persist_proxy/README.md: -------------------------------------------------------------------------------- 1 | ## Persistent proxy 2 | 3 | OpenResty®-based proxy that supports persistence/stickiness, can be used to improve the hit ratio of nginx-vod-module shared memory caches. 4 | The upstream servers are assumed to be k8s pods - the proxy periodically pulls the list of active servers from k8s via API. 5 | The stickiness is maintained in redis and also in an nginx-lua shared dict. 6 | 7 | ### Features 8 | 9 | * Persistence based on a key set in nginx.conf 10 | * Fallback to another upstream server in case of error 11 | * Load balancing based on 'power of two choices', using the number of errors and the % of fast responses for prioritization 12 | * Exposes metrics in Prometheus format 13 | 14 | ### Setup 15 | 16 | The proxy expects the following environment variables - 17 | * `KUBERNETES_SERVICE_HOST` - k8s API endpoint host. 18 | * `KUBERNETES_SERVICE_PORT` - k8s API endpoint port. 19 | * `KUBERNETES_SERVICE_TOKEN` - - k8s API token, usually read from `/var/run/secrets/kubernetes.io/serviceaccount/token`. 20 | * `KUBERNETES_POD_NAMESPACE` - optional, the k8s namespace of the upstream pods, defaults to `vod`. 21 | * `KUBERNETES_POD_APP_LABEL` - optional, the `app` label value of the upstream pods, if not specified, the upstream pods are not filtered by label. 22 | * `REDIS_HOST` - the redis host, defaults to `127.0.0.1`. 23 | * `REDIS_PORT` - optional, the redis port, defaults to `6379`. 24 | -------------------------------------------------------------------------------- /ngx_child_http_request.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_CHILD_HTTP_REQUEST_INCLUDED_ 2 | #define _NGX_CHILD_HTTP_REQUEST_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | 7 | // typedefs 8 | typedef void(*ngx_child_request_callback_t)(void* context, ngx_int_t rc, ngx_buf_t* buf, ssize_t bytes_read); 9 | 10 | typedef struct { 11 | ngx_uint_t method; 12 | ngx_str_t base_uri; 13 | ngx_str_t extra_args; 14 | off_t range_start; 15 | off_t range_end; 16 | ngx_table_elt_t extra_header; 17 | ngx_flag_t proxy_range; 18 | ngx_flag_t proxy_all_headers; 19 | } ngx_child_request_params_t; 20 | 21 | // functions 22 | 23 | // Notes: 24 | // 1. callback is optional, if it is not supplied, the module will finalize the request 25 | // when the upstream request completes. 26 | // 2. response_buffer is optional, if it is not supplied, the upstream response gets written 27 | // to the parent request. when a response buffer is supplied, the response is written to it, 28 | // the buffer should be large enough to contain both the response body and the response headers. 29 | ngx_int_t ngx_child_request_start( 30 | ngx_http_request_t *r, 31 | ngx_child_request_callback_t callback, 32 | void* callback_context, 33 | ngx_str_t* internal_location, 34 | ngx_child_request_params_t* params, 35 | ngx_buf_t* response_buffer); 36 | 37 | ngx_int_t ngx_child_request_init(ngx_conf_t *cf); 38 | 39 | #endif // _NGX_CHILD_HTTP_REQUEST_INCLUDED_ 40 | -------------------------------------------------------------------------------- /vod/avc_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef __AVC_DEFS_H__ 2 | #define __AVC_DEFS_H__ 3 | 4 | // NAL unit types 5 | enum { 6 | AVC_NAL_SLICE = 1, 7 | AVC_NAL_DPA = 2, 8 | AVC_NAL_DPB = 3, 9 | AVC_NAL_DPC = 4, 10 | AVC_NAL_IDR_SLICE = 5, 11 | AVC_NAL_SEI = 6, 12 | AVC_NAL_SPS = 7, 13 | AVC_NAL_PPS = 8, 14 | AVC_NAL_AUD = 9, 15 | AVC_NAL_END_SEQUENCE = 10, 16 | AVC_NAL_END_STREAM = 11, 17 | AVC_NAL_FILLER_DATA = 12, 18 | AVC_NAL_SPS_EXT = 13, 19 | AVC_NAL_AUXILIARY_SLICE = 19, 20 | }; 21 | 22 | enum { 23 | HEVC_NAL_TRAIL_N = 0, 24 | HEVC_NAL_TRAIL_R = 1, 25 | HEVC_NAL_TSA_N = 2, 26 | HEVC_NAL_TSA_R = 3, 27 | HEVC_NAL_STSA_N = 4, 28 | HEVC_NAL_STSA_R = 5, 29 | HEVC_NAL_RADL_N = 6, 30 | HEVC_NAL_RADL_R = 7, 31 | HEVC_NAL_RASL_N = 8, 32 | HEVC_NAL_RASL_R = 9, 33 | HEVC_NAL_BLA_W_LP = 16, 34 | HEVC_NAL_BLA_W_RADL = 17, 35 | HEVC_NAL_BLA_N_LP = 18, 36 | HEVC_NAL_IDR_W_RADL = 19, 37 | HEVC_NAL_IDR_N_LP = 20, 38 | HEVC_NAL_CRA_NUT = 21, 39 | HEVC_NAL_RSV_IRAP_VCL22 = 22, 40 | HEVC_NAL_RSV_IRAP_VCL23 = 23, 41 | HEVC_NAL_VPS_NUT = 32, 42 | HEVC_NAL_SPS_NUT = 33, 43 | HEVC_NAL_PPS_NUT = 34, 44 | HEVC_NAL_AUD_NUT = 35, 45 | HEVC_NAL_EOS_NUT = 36, 46 | HEVC_NAL_EOB_NUT = 37, 47 | HEVC_NAL_FD_NUT = 38, 48 | HEVC_NAL_PREFIX_SEI_NUT = 39, 49 | HEVC_NAL_SUFFIX_SEI_NUT = 40, 50 | }; 51 | 52 | #endif // __AVC_DEFS_H__ 53 | -------------------------------------------------------------------------------- /conf/http.conf.template: -------------------------------------------------------------------------------- 1 | include mime.types; 2 | default_type application/octet-stream; 3 | 4 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 5 | '$status $bytes_sent $request_time "$http_referer" "$http_user_agent" "-" - ' 6 | '"$sent_http_x_kaltura" "$http_host" $pid $sent_http_x_kaltura_session - ' 7 | '$request_length "$sent_http_content_range" "$http_x_forwarded_for" ' 8 | '"$http_x_forwarded_server" "$http_x_forwarded_host" "$sent_http_cache_control" ' 9 | '$connection '; 10 | 11 | access_log @LOG_DIR@/kaltura_nginx_access.log main; 12 | 13 | # general nginx tuning 14 | sendfile on; 15 | tcp_nopush on; 16 | tcp_nodelay on; 17 | 18 | keepalive_timeout 60; 19 | keepalive_requests 1000; 20 | client_header_timeout 20; 21 | client_body_timeout 20; 22 | reset_timedout_connection on; 23 | send_timeout 20; 24 | 25 | # manifest compression 26 | gzip on; 27 | gzip_types application/vnd.apple.mpegurl video/f4m application/dash+xml text/xml text/vtt; 28 | gzip_proxied any; 29 | 30 | # shared memory zones 31 | vod_metadata_cache metadata_cache 512m; 32 | vod_response_cache response_cache 64m; 33 | vod_performance_counters perf_counters; 34 | 35 | # common vod settings 36 | vod_last_modified 'Sun, 19 Nov 2000 08:52:00 GMT'; 37 | vod_last_modified_types *; 38 | vod_expires 100d; 39 | vod_expires_live 30; 40 | vod_expires_live_time_dependent 3; 41 | vod_align_segments_to_key_frames on; 42 | vod_output_buffer_pool 64k 32; 43 | -------------------------------------------------------------------------------- /tools/prefetch_proxy/prefetcher.lua: -------------------------------------------------------------------------------- 1 | local http = require 'resty.http' 2 | 3 | local ngx_ERR = ngx.ERR 4 | local ngx_log = ngx.log 5 | local ngx_req = ngx.req 6 | local ngx_req_get_headers = ngx_req.get_headers 7 | local ngx_timer = ngx.timer 8 | local ngx_var = ngx.var 9 | 10 | local base_url = '127.0.0.1' 11 | 12 | local _M = {} 13 | 14 | local function fetch_url(premature, uri, headers) 15 | local httpc = http.new() 16 | local res, err = httpc:request_uri(uri, { 17 | headers = headers, 18 | ssl_verify = false 19 | }) 20 | end 21 | 22 | function _M.prefetch_segments(prefetch_segment_count, url_prefix, req_segment, url_suffix) 23 | -- prevent request loops 24 | if ngx_var.http_x_kaltura_proxy == 'prefetch' then 25 | return 26 | end 27 | local headers = ngx_req_get_headers() 28 | headers['X-Kaltura-Proxy'] = 'prefetch' 29 | 30 | -- prefetch segments 31 | local full_prefix = ngx_var.scheme .. '://' .. base_url .. ':' .. ngx_var.server_port .. url_prefix 32 | req_segment = tonumber(req_segment) 33 | for segment = req_segment + 1, req_segment + prefetch_segment_count do 34 | local url = full_prefix .. segment .. url_suffix 35 | 36 | -- set a timer of 0 sec to perform the request in the background 37 | local ok, err = ngx_timer.at(0, fetch_url, url, headers) 38 | if not ok then 39 | ngx_log(ngx_ERR, 'failed to create timer: ', err) 40 | break 41 | end 42 | end 43 | end 44 | 45 | return _M 46 | -------------------------------------------------------------------------------- /vod/mkv/ebml.h: -------------------------------------------------------------------------------- 1 | #ifndef __EBML_H__ 2 | #define __EBML_H__ 3 | 4 | #include "../common.h" 5 | 6 | // macros 7 | #define ebml_read_id(context, id) ebml_read_num(context, id, 4, 0) 8 | #define is_unknown_size(num, num_bytes) ((num) + 1 == 1ULL << (7 * (num_bytes))) 9 | 10 | #define EBML_TRUNCATE_SIZE 0x80 11 | 12 | // typedefs 13 | typedef enum { 14 | EBML_NONE, 15 | EBML_UINT, 16 | EBML_FLOAT, 17 | EBML_STRING, 18 | EBML_BINARY, 19 | EBML_MASTER, 20 | EBML_CUSTOM, 21 | } ebml_type_t; 22 | 23 | typedef struct { 24 | request_context_t* request_context; 25 | const u_char* cur_pos; 26 | const u_char* end_pos; 27 | int64_t offset_delta; 28 | } ebml_context_t; 29 | 30 | typedef struct { 31 | uint32_t id; 32 | ebml_type_t type; 33 | off_t offset; 34 | void* child; 35 | } ebml_spec_t; 36 | 37 | typedef struct { 38 | uint64_t version; 39 | uint64_t max_size; 40 | uint64_t id_length; 41 | vod_str_t doctype; 42 | uint64_t doctype_version; 43 | } ebml_header_t; 44 | 45 | typedef vod_status_t (*ebml_parser_t)(ebml_context_t* context, ebml_spec_t* spec, void* dest); 46 | 47 | // functions 48 | vod_status_t ebml_read_num(ebml_context_t* context, uint64_t* result, size_t max_size, int remove_first_bit); 49 | 50 | vod_status_t ebml_parse_header(ebml_context_t* context, ebml_header_t* header); 51 | 52 | vod_status_t ebml_parse_single(ebml_context_t* context, ebml_spec_t* spec, void* dest); 53 | 54 | vod_status_t ebml_parse_master(ebml_context_t* context, ebml_spec_t* spec, void* dest); 55 | 56 | #endif //__EBML_H__ 57 | -------------------------------------------------------------------------------- /vod/dynamic_buffer.c: -------------------------------------------------------------------------------- 1 | #include "dynamic_buffer.h" 2 | 3 | vod_status_t 4 | vod_dynamic_buf_init(vod_dynamic_buf_t* buffer, request_context_t* request_context, size_t initial_size) 5 | { 6 | buffer->request_context = request_context; 7 | buffer->start = vod_alloc(request_context->pool, initial_size); 8 | if (buffer->start == NULL) 9 | { 10 | vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, 11 | "vod_dynamic_buf_init: vod_alloc failed"); 12 | return VOD_ALLOC_FAILED; 13 | } 14 | buffer->end = buffer->start + initial_size; 15 | buffer->pos = buffer->start; 16 | return VOD_OK; 17 | } 18 | 19 | vod_status_t 20 | vod_dynamic_buf_reserve(vod_dynamic_buf_t* buffer, size_t size) 21 | { 22 | u_char* new_buffer; 23 | size_t used_buffer_size; 24 | size_t new_size; 25 | 26 | if (buffer->pos + size <= buffer->end) 27 | { 28 | return VOD_OK; 29 | } 30 | 31 | new_size = 2 * (buffer->end - buffer->start); 32 | new_size = vod_max(new_size, size); 33 | 34 | new_buffer = vod_alloc(buffer->request_context->pool, new_size); 35 | if (new_buffer == NULL) 36 | { 37 | vod_log_debug0(VOD_LOG_DEBUG_LEVEL, buffer->request_context->log, 0, 38 | "vod_dynamic_buf_reserve: vod_alloc failed"); 39 | return VOD_ALLOC_FAILED; 40 | } 41 | 42 | used_buffer_size = buffer->pos - buffer->start; 43 | vod_memcpy(new_buffer, buffer->start, used_buffer_size); 44 | buffer->start = new_buffer; 45 | buffer->end = new_buffer + new_size; 46 | buffer->pos = new_buffer + used_buffer_size; 47 | 48 | return VOD_OK; 49 | } 50 | 51 | -------------------------------------------------------------------------------- /conf/vod-local.conf.template: -------------------------------------------------------------------------------- 1 | 2 | # base locations 3 | include @NGINX_CONF_PATH@/base.conf; 4 | 5 | # serve flavor progressive 6 | location /pd/ { 7 | alias @MEDIA_FILES_PATH@; 8 | vod none; 9 | 10 | directio 512; 11 | output_buffers 1 512k; 12 | 13 | include @NGINX_CONF_PATH@/cors.conf; 14 | } 15 | 16 | # serve flavor HLS 17 | location /hls/ { 18 | alias @MEDIA_FILES_PATH@; 19 | vod hls; 20 | vod_bootstrap_segment_durations 2000; 21 | vod_bootstrap_segment_durations 2000; 22 | vod_bootstrap_segment_durations 2000; 23 | vod_bootstrap_segment_durations 4000; 24 | 25 | include @NGINX_CONF_PATH@/cors.conf; 26 | } 27 | 28 | # serve flavor DASH 29 | location /dash/ { 30 | alias @MEDIA_FILES_PATH@; 31 | vod dash; 32 | vod_segment_duration 4000; 33 | vod_dash_manifest_format segmenttemplate; 34 | vod_manifest_duration_policy min; 35 | 36 | include @NGINX_CONF_PATH@/cors.conf; 37 | } 38 | 39 | # serve flavor HDS 40 | location /hds/ { 41 | alias @MEDIA_FILES_PATH@; 42 | vod hds; 43 | vod_segment_duration 6000; 44 | vod_segment_count_policy last_rounded; 45 | 46 | include @NGINX_CONF_PATH@/cors.conf; 47 | } 48 | 49 | # serve flavor MSS 50 | location /mss/ { 51 | alias @MEDIA_FILES_PATH@; 52 | vod mss; 53 | vod_segment_duration 4000; 54 | vod_manifest_segment_durations_mode accurate; 55 | 56 | include @NGINX_CONF_PATH@/cors.conf; 57 | } 58 | 59 | # static files (crossdomain.xml, robots.txt etc.) + fallback to api 60 | location / { 61 | root @STATIC_FILES_PATH@; 62 | } 63 | -------------------------------------------------------------------------------- /tools/persist_proxy/status.lua: -------------------------------------------------------------------------------- 1 | 2 | --[[ 3 | outputs nginx stub status metrics in prom format 4 | --]] 5 | 6 | local log = ngx.log 7 | local ERR = ngx.ERR 8 | local capture = ngx.location.capture 9 | local gmatch = ngx.re.gmatch 10 | local say = ngx.say 11 | 12 | local _M = { _VERSION = '0.1' } 13 | 14 | -- order matching nginx stub status output 15 | local metrics = { 16 | 'nginx_connections_current{state="active"} ', 17 | 'nginx_connections_processed_total{stage="accepted"} ', 18 | 'nginx_connections_processed_total{stage="handled"} ', 19 | 'nginx_connections_processed_total{stage="any"} ', 20 | 'nginx_connections_current{state="reading"} ', 21 | 'nginx_connections_current{state="writing"} ', 22 | 'nginx_connections_current{state="waiting"} ', 23 | } 24 | 25 | function _M.format_status(uri) 26 | 27 | local res = capture(uri) 28 | if res.status ~= 200 then 29 | log(ERR, 'bad status: ', res.status) 30 | return 31 | end 32 | 33 | local iter, err = gmatch(res.body, '(\\d+)') 34 | if not iter then 35 | log(ERR, 'gmatch failed: ', err) 36 | return 37 | end 38 | 39 | local body = '' 40 | for _, cur in pairs(metrics) do 41 | local m, err = iter() 42 | if err then 43 | log(ERR, 'gmatch iterator failed: ', err) 44 | return 45 | end 46 | 47 | if not m then 48 | log(ERR, 'missing matches') 49 | return 50 | end 51 | 52 | body = body .. cur .. m[1] .. '\n' 53 | end 54 | 55 | return body 56 | end 57 | 58 | return _M 59 | -------------------------------------------------------------------------------- /test/compare_utils.py: -------------------------------------------------------------------------------- 1 | import email.utils as eut 2 | import time 3 | 4 | INGORED_HEADERS = set([ 5 | 'x-vod-me','x-vod-session', 6 | 'x-proxy-me', 'x-proxy-session', 7 | 'x-me', 'x-kaltura-session', 8 | 'x-varnish', 'x-amz-id-2', 'x-amz-request-id', 9 | ]) 10 | 11 | IGNORE_HEADER_VALUES = set([ 12 | 'server', 13 | 'date', 14 | 'content-length', # ignore content length since we compare the buffer, the content length may be different due to different host name lengths 15 | 'etag', # derived from content length 16 | ]) 17 | 18 | def parseHttpTime(timeStr): 19 | return time.mktime(eut.parsedate(timeStr)) 20 | 21 | def compareHeaders(headers1, headers2): 22 | for headerName in INGORED_HEADERS: 23 | headers1.pop(headerName, None) 24 | headers2.pop(headerName, None) 25 | 26 | onlyIn1 = set(headers1.keys()) - set(headers2.keys()) 27 | if len(onlyIn1) != 0: 28 | return 'Error: headers %s found only in headers1' % (','.join(onlyIn1)) 29 | onlyIn2 = set(headers2.keys()) - set(headers1.keys()) 30 | if len(onlyIn2) != 0 and onlyIn2 != set(['access-control-allow-origin']): # allow CORS to be added 31 | return 'Error: headers %s found only in headers2' % (','.join(onlyIn2)) 32 | for curHeader in headers1.keys(): 33 | if curHeader in IGNORE_HEADER_VALUES: 34 | continue 35 | value1 = headers1[curHeader] 36 | value2 = headers2[curHeader] 37 | if value1 == value2: 38 | continue 39 | if curHeader in set(['expires', 'last-modified']): 40 | if abs(parseHttpTime(value1[0]) - parseHttpTime(value2[0])) < 10: 41 | continue 42 | return 'Error: different value for header %s - %s vs %s' % (curHeader, value1, value2) 43 | return None 44 | -------------------------------------------------------------------------------- /test/generate_many_tracks.py: -------------------------------------------------------------------------------- 1 | import re 2 | import subprocess 3 | import sys 4 | 5 | # This generates a test file with a large number of audio tracks 6 | # Used to test builds with a custom NGX_VOD_MAX_TRACK_COUNT value 7 | # Requires ffmpeg with flite support (check with `ffplay -f lavfi flite=text="test"`) 8 | 9 | def parse_langs(): 10 | blacklist = ['UND', 'ZXX'] 11 | dict = {} 12 | with open('../vod/languages_x.h') as f: 13 | for l in f: 14 | if l.startswith("LANG("): 15 | lang = [x.strip() for x in l[5:-2].split(",")] 16 | if lang[0] in blacklist: 17 | continue 18 | 19 | identifier = lang[3][1:-1] 20 | name_english = lang[4][1:-1].replace('"', '') 21 | dict[identifier] = name_english 22 | return dict 23 | 24 | def generate_ffmpeg_command(languages, max_tracks, output_filename): 25 | pattern = re.compile('[\W_]+') 26 | c = ['ffmpeg'] 27 | langs = [] 28 | for ident in languages.keys(): 29 | c.extend(['-f', 'lavfi', '-i', 'flite=text="{}",aloop=20:size=3*8000'.format(pattern.sub('', languages[ident]))]) 30 | langs.append(ident) 31 | if len(langs) == max_tracks: 32 | break 33 | 34 | c.append('-shortest') 35 | 36 | for i in range(len(langs)): 37 | c.extend(['-map', '{}:a'.format(i), '-metadata:s:a:{}'.format(i), 'language={}'.format(langs[i])]) 38 | c.append(output_filename) 39 | return c 40 | 41 | if __name__ == '__main__': 42 | args = sys.argv[1:] 43 | if len(args) > 0: 44 | tracks = int(args[0]) 45 | else: 46 | tracks = 128 47 | 48 | languages = parse_langs() 49 | command = generate_ffmpeg_command(languages, tracks, "many_tracks.mp4") 50 | print('command:') 51 | print(*command) 52 | print() 53 | subprocess.run(command) 54 | -------------------------------------------------------------------------------- /conf/vod-remote.conf.template: -------------------------------------------------------------------------------- 1 | 2 | # internal location for vod subrequests 3 | location ~ /media_proxy/[^/]+/(.*) { 4 | internal; 5 | proxy_pass http://media/$1; 6 | proxy_http_version 1.1; 7 | proxy_set_header Host @MEDIA_HOST@; 8 | proxy_set_header Connection ""; 9 | } 10 | 11 | # base locations 12 | include @NGINX_CONF_PATH@/base.conf; 13 | 14 | # serve flavor progressive 15 | location /pd/ { 16 | vod none; 17 | 18 | directio 512; 19 | output_buffers 1 512k; 20 | 21 | include @NGINX_CONF_PATH@/cors.conf; 22 | } 23 | 24 | # serve flavor HLS 25 | location /hls/ { 26 | vod hls; 27 | vod_bootstrap_segment_durations 2000; 28 | vod_bootstrap_segment_durations 2000; 29 | vod_bootstrap_segment_durations 2000; 30 | vod_bootstrap_segment_durations 4000; 31 | 32 | include @NGINX_CONF_PATH@/cors.conf; 33 | } 34 | 35 | # serve flavor DASH 36 | location /dash/ { 37 | vod dash; 38 | vod_segment_duration 4000; 39 | vod_dash_manifest_format segmenttemplate; 40 | vod_manifest_duration_policy min; 41 | 42 | include @NGINX_CONF_PATH@/cors.conf; 43 | } 44 | 45 | # serve flavor HDS 46 | location /hds/ { 47 | vod hds; 48 | vod_segment_duration 6000; 49 | vod_segment_count_policy last_rounded; 50 | 51 | include @NGINX_CONF_PATH@/cors.conf; 52 | } 53 | 54 | # serve flavor MSS 55 | location /mss/ { 56 | vod mss; 57 | vod_segment_duration 4000; 58 | vod_manifest_segment_durations_mode accurate; 59 | 60 | include @NGINX_CONF_PATH@/cors.conf; 61 | } 62 | 63 | # static files (crossdomain.xml, robots.txt etc.) + fallback to api 64 | location / { 65 | root @STATIC_FILES_PATH@; 66 | } 67 | -------------------------------------------------------------------------------- /ngx_buffer_cache_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_BUFFER_CACHE_INTERNAL_H_INCLUDED_ 2 | #define _NGX_BUFFER_CACHE_INTERNAL_H_INCLUDED_ 3 | 4 | #include "ngx_buffer_cache.h" 5 | #include "ngx_queue.h" 6 | 7 | // macros 8 | #define container_of(ptr, type, member) (type *)((char *)(ptr) - offsetof(type, member)) 9 | 10 | // constants 11 | #define CACHE_LOCK_EXPIRATION (5) 12 | #define ENTRY_LOCK_EXPIRATION (5) 13 | #define ENTRIES_ALLOC_MARGIN (1024) // 1K entries ~= 100KB, we reserve this space to make sure allocating entries does not become the bottleneck 14 | #define BUFFER_ALIGNMENT (16) 15 | #define MAX_EVICTIONS_PER_STORE (128) 16 | 17 | // enums 18 | enum { 19 | CES_FREE, 20 | CES_ALLOCATED, 21 | CES_READY, 22 | }; 23 | 24 | // typedefs 25 | typedef struct { 26 | ngx_rbtree_node_t node; 27 | ngx_queue_t queue_node; 28 | u_char* start_offset; 29 | size_t buffer_size; 30 | ngx_atomic_t state; 31 | ngx_atomic_t ref_count; 32 | time_t access_time; 33 | time_t write_time; 34 | u_char key[BUFFER_CACHE_KEY_SIZE]; 35 | } ngx_buffer_cache_entry_t; 36 | 37 | typedef struct { 38 | ngx_atomic_t reset; 39 | time_t access_time; 40 | ngx_rbtree_t rbtree; 41 | ngx_rbtree_node_t sentinel; 42 | ngx_queue_t used_queue; 43 | ngx_queue_t free_queue; 44 | ngx_buffer_cache_entry_t* entries_start; 45 | ngx_buffer_cache_entry_t* entries_end; 46 | u_char* buffers_start; 47 | u_char* buffers_end; 48 | u_char* buffers_read; 49 | u_char* buffers_write; 50 | ngx_buffer_cache_stats_t stats; 51 | } ngx_buffer_cache_sh_t; 52 | 53 | struct ngx_buffer_cache_s { 54 | ngx_buffer_cache_sh_t *sh; 55 | ngx_slab_pool_t *shpool; 56 | 57 | uint32_t expiration; 58 | 59 | ngx_shm_zone_t *shm_zone; 60 | }; 61 | 62 | #endif // _NGX_BUFFER_CACHE_INTERNAL_H_INCLUDED_ 63 | -------------------------------------------------------------------------------- /ngx_buffer_cache.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_BUFFER_CACHE_H_INCLUDED_ 2 | #define _NGX_BUFFER_CACHE_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | 7 | // constants 8 | #define BUFFER_CACHE_KEY_SIZE (16) 9 | 10 | // typedefs 11 | struct ngx_buffer_cache_s; 12 | typedef struct ngx_buffer_cache_s ngx_buffer_cache_t; 13 | 14 | typedef struct { 15 | ngx_atomic_t store_ok; 16 | ngx_atomic_t store_bytes; 17 | ngx_atomic_t store_err; 18 | ngx_atomic_t store_exists; 19 | ngx_atomic_t fetch_hit; 20 | ngx_atomic_t fetch_bytes; 21 | ngx_atomic_t fetch_miss; 22 | ngx_atomic_t evicted; 23 | ngx_atomic_t evicted_bytes; 24 | ngx_atomic_t reset; 25 | 26 | // updated only when the stats are fetched 27 | ngx_atomic_t entries; 28 | ngx_atomic_t data_size; 29 | } ngx_buffer_cache_stats_t; 30 | 31 | // functions 32 | ngx_flag_t ngx_buffer_cache_fetch( 33 | ngx_buffer_cache_t* cache, 34 | u_char* key, 35 | ngx_str_t* buffer, 36 | uint32_t* token); 37 | 38 | void ngx_buffer_cache_release( 39 | ngx_buffer_cache_t* cache, 40 | u_char* key, 41 | uint32_t token); 42 | 43 | ngx_flag_t ngx_buffer_cache_store( 44 | ngx_buffer_cache_t* cache, 45 | u_char* key, 46 | u_char* source_buffer, 47 | size_t buffer_size); 48 | 49 | ngx_flag_t ngx_buffer_cache_store_gather( 50 | ngx_buffer_cache_t* cache, 51 | u_char* key, 52 | ngx_str_t* buffers, 53 | size_t buffer_count); 54 | 55 | void ngx_buffer_cache_get_stats( 56 | ngx_buffer_cache_t* cache, 57 | ngx_buffer_cache_stats_t* stats); 58 | 59 | void ngx_buffer_cache_reset_stats(ngx_buffer_cache_t* cache); 60 | 61 | ngx_buffer_cache_t* ngx_buffer_cache_create( 62 | ngx_conf_t *cf, 63 | ngx_str_t *name, 64 | size_t size, 65 | time_t expiration, 66 | void *tag); 67 | 68 | #endif // _NGX_BUFFER_CACHE_H_INCLUDED_ 69 | -------------------------------------------------------------------------------- /vod/media_set_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEDIA_SET_PARSER_H__ 2 | #define __MEDIA_SET_PARSER_H__ 3 | 4 | // includes 5 | #include "media_set.h" 6 | 7 | // typedefs 8 | typedef struct { 9 | request_context_t* request_context; 10 | media_sequence_t* sequence; 11 | media_range_t* range; 12 | int64_t clip_time; 13 | uint32_t clip_from; 14 | uint32_t duration; 15 | media_clip_source_t* sources_head; 16 | media_clip_source_t* mapped_sources_head; 17 | media_clip_source_t* generators_head; 18 | struct media_clip_dynamic_s* dynamic_clips_head; 19 | media_notification_t* notifications_head; 20 | } media_filter_parse_context_t; 21 | 22 | // main functions 23 | vod_status_t media_set_parser_init( 24 | vod_pool_t* pool, 25 | vod_pool_t* temp_pool); 26 | 27 | vod_status_t media_set_parse_json( 28 | request_context_t* request_context, 29 | u_char* string, 30 | u_char* override, 31 | request_params_t* request_params, 32 | struct segmenter_conf_s* segmenter, 33 | media_clip_source_t* source, 34 | int request_flags, 35 | media_set_t* result); 36 | 37 | vod_status_t media_set_map_source( 38 | request_context_t* request_context, 39 | u_char* string, 40 | media_clip_source_t* source); 41 | 42 | // filter utility functions 43 | vod_status_t media_set_parse_null_term_string( 44 | void* ctx, 45 | vod_json_value_t* value, 46 | void* dest); 47 | 48 | vod_status_t media_set_parse_filter_sources( 49 | void* ctx, 50 | vod_json_value_t* value, 51 | void* dest); 52 | 53 | vod_status_t media_set_parse_clip( 54 | void* ctx, 55 | vod_json_object_t* element, 56 | media_clip_t* parent, 57 | media_clip_t** result); 58 | 59 | vod_status_t media_set_parse_notifications( 60 | request_context_t* request_context, 61 | vod_json_array_t* array, 62 | int64_t min_offset, 63 | int64_t max_offset, 64 | media_notification_t** result); 65 | 66 | #endif //__MEDIA_SET_PARSER_H__ 67 | -------------------------------------------------------------------------------- /vod/hls/media_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEDIA_FILTER_H__ 2 | #define __MEDIA_FILTER_H__ 3 | 4 | // includes 5 | #include "../common.h" 6 | 7 | // typedefs 8 | enum { 9 | MEDIA_FILTER_MPEGTS, 10 | MEDIA_FILTER_MP4_TO_ANNEXB, 11 | MEDIA_FILTER_JOINER, 12 | MEDIA_FILTER_BUFFER, 13 | MEDIA_FILTER_ADTS, 14 | MEDIA_FILTER_ENCRYPT, 15 | MEDIA_FILTER_EAC3_ENCRYPT, 16 | MEDIA_FILTER_ID3, 17 | 18 | MEDIA_FILTER_COUNT 19 | }; 20 | 21 | typedef struct { 22 | request_context_t* request_context; 23 | void* context[MEDIA_FILTER_COUNT]; 24 | } media_filter_context_t; 25 | 26 | typedef struct { 27 | uint64_t pts; 28 | uint64_t dts; 29 | int key; 30 | uint32_t size; 31 | uint32_t header_size; 32 | } output_frame_t; 33 | 34 | typedef vod_status_t (*media_filter_start_frame_t)( 35 | media_filter_context_t* context, 36 | output_frame_t* frame); 37 | typedef vod_status_t (*media_filter_write_t)( 38 | media_filter_context_t* context, 39 | const u_char* buffer, 40 | uint32_t size); 41 | typedef vod_status_t (*media_filter_flush_frame_t)( 42 | media_filter_context_t* context, 43 | bool_t last_stream_frame); 44 | 45 | typedef void (*media_filter_simulated_start_frame_t)( 46 | media_filter_context_t* context, 47 | output_frame_t* frame); 48 | typedef void (*media_filter_simulated_write_t)( 49 | media_filter_context_t* context, 50 | uint32_t size); 51 | typedef void (*media_filter_simulated_flush_frame_t)( 52 | media_filter_context_t* context, 53 | bool_t last_stream_frame); 54 | 55 | typedef struct { 56 | media_filter_start_frame_t start_frame; 57 | media_filter_write_t write; 58 | media_filter_flush_frame_t flush_frame; 59 | 60 | media_filter_simulated_start_frame_t simulated_start_frame; 61 | media_filter_simulated_write_t simulated_write; 62 | media_filter_simulated_flush_frame_t simulated_flush_frame; 63 | } media_filter_t; 64 | 65 | #endif // __MEDIA_FILTER_H__ 66 | -------------------------------------------------------------------------------- /vod/input/frames_source_memory.c: -------------------------------------------------------------------------------- 1 | #include "frames_source_memory.h" 2 | #include "../media_format.h" 3 | 4 | // typedefs 5 | typedef struct { 6 | u_char* buffer; 7 | size_t size; 8 | } frames_source_memory_state_t; 9 | 10 | vod_status_t 11 | frames_source_memory_init( 12 | request_context_t* request_context, 13 | void** result) 14 | { 15 | frames_source_memory_state_t* state; 16 | 17 | state = vod_alloc(request_context->pool, sizeof(*state)); 18 | if (state == NULL) 19 | { 20 | vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, 21 | "frames_source_memory_init: vod_alloc failed"); 22 | return VOD_ALLOC_FAILED; 23 | } 24 | 25 | *result = state; 26 | 27 | return VOD_OK; 28 | } 29 | 30 | static void 31 | frames_source_memory_set_cache_slot_id(void* ctx, int cache_slot_id) 32 | { 33 | } 34 | 35 | static vod_status_t 36 | frames_source_memory_start_frame(void* ctx, input_frame_t* frame, read_cache_hint_t* cache_hint) 37 | { 38 | frames_source_memory_state_t* state = ctx; 39 | 40 | state->buffer = (u_char*)(uintptr_t)frame->offset; 41 | state->size = frame->size; 42 | 43 | return VOD_OK; 44 | } 45 | 46 | static vod_status_t 47 | frames_source_memory_read(void* ctx, u_char** buffer, uint32_t* size, bool_t* frame_done) 48 | { 49 | frames_source_memory_state_t* state = ctx; 50 | 51 | *buffer = state->buffer; 52 | *size = state->size; 53 | *frame_done = TRUE; 54 | 55 | return VOD_OK; 56 | } 57 | 58 | static void 59 | frames_source_memory_disable_buffer_reuse(void* ctx) 60 | { 61 | } 62 | 63 | static vod_status_t 64 | frames_source_memory_skip_frames(void* ctx, uint32_t skip_count) 65 | { 66 | return VOD_OK; 67 | } 68 | 69 | // globals 70 | frames_source_t frames_source_memory = { 71 | frames_source_memory_set_cache_slot_id, 72 | frames_source_memory_start_frame, 73 | frames_source_memory_read, 74 | frames_source_memory_disable_buffer_reuse, 75 | frames_source_memory_skip_frames, 76 | }; 77 | -------------------------------------------------------------------------------- /vod/cli/vod_cli_main.c: -------------------------------------------------------------------------------- 1 | // gcc -Wall -g -o repack adts_encoder.c buffer_filter.c mp4_parser.c mp4_to_annexb.c mpegts_encoder.c repackTs.c vod_array.c muxer.c read_cache.c 2 | // ./repack /opt/kaltura/app/alpha/web/repack.ts 0 10 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "mp4_parser.h" 9 | #include "read_cache.h" 10 | #include "muxer.h" 11 | 12 | static bool_t 13 | write_file(void* context, const u_char* buffer, uint32_t size) 14 | { 15 | int* output_fd = (int*)context; 16 | int rc; 17 | 18 | rc = write(*output_fd, buffer, size); 19 | if (rc < size) 20 | { 21 | return FALSE; 22 | } 23 | 24 | return TRUE; 25 | } 26 | 27 | static size_t 28 | read_file(void* context, u_char *buf, size_t size, off_t offset) 29 | { 30 | int* input_fd = (int*)context; 31 | 32 | return pread(*input_fd, buf, size, offset); 33 | } 34 | 35 | int 36 | main(int argc, const char *argv[]) 37 | { 38 | read_cache_state_t read_cache_state; 39 | hls_muxer_state_t muxer; 40 | media_track_array_t track_array; 41 | input_params_t input_params; 42 | int output_fd; 43 | int input_fd; 44 | 45 | vod_memzero(&track_array, sizeof(track_array)); 46 | input_params.start_sec = atoi(argv[2]); 47 | input_params.end_sec = atoi(argv[3]); 48 | 49 | input_fd = open("/web/content/r70v1/entry/data/80/655/0_vriq23ct_0_g0vnoj5i_1.mp4", O_RDONLY); 50 | if (input_fd == -1) 51 | { 52 | } 53 | 54 | output_fd = creat(argv[1], S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 55 | if (output_fd == -1) 56 | { 57 | } 58 | 59 | if (!parse_mpeg_file(read_file, &input_fd, &track_array, &input_params)) 60 | { 61 | } 62 | 63 | if (!read_cache_init(&read_cache_state, read_file, &input_fd)) 64 | { 65 | } 66 | 67 | if (!hls_muxer_init(&muxer, &track_array, &read_cache_state, write_file, &output_fd)) 68 | { 69 | } 70 | 71 | hls_muxer_process(&muxer); 72 | 73 | close(output_fd); 74 | close(input_fd); 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /vod/input/read_cache.h: -------------------------------------------------------------------------------- 1 | #ifndef __READ_CACHE_H__ 2 | #define __READ_CACHE_H__ 3 | 4 | // includes 5 | #include "../common.h" 6 | 7 | // typedefs 8 | struct media_clip_source_s; 9 | 10 | typedef struct { 11 | u_char* buffer_start; 12 | u_char* buffer_pos; 13 | uint32_t buffer_size; // size of data read 14 | void* source; // opaque context that indicates from where the buffer should be read 15 | uint64_t start_offset; 16 | uint64_t end_offset; 17 | } cache_buffer_t; 18 | 19 | typedef struct { 20 | request_context_t* request_context; 21 | cache_buffer_t* buffers; 22 | cache_buffer_t* buffers_end; 23 | cache_buffer_t* target_buffer; 24 | size_t buffer_count; 25 | size_t buffer_size; 26 | bool_t reuse_buffers; 27 | } read_cache_state_t; 28 | 29 | typedef struct { 30 | uint64_t min_offset; 31 | int min_offset_slot_id; 32 | } read_cache_hint_t; 33 | 34 | typedef struct { 35 | int cache_slot_id; 36 | struct media_clip_source_s* source; 37 | uint64_t cur_offset; 38 | uint64_t end_offset; 39 | read_cache_hint_t hint; 40 | } read_cache_request_t; 41 | 42 | typedef struct { 43 | struct media_clip_source_s* source; 44 | uint64_t offset; 45 | u_char* buffer; 46 | uint32_t size; 47 | } read_cache_get_read_buffer_t; 48 | 49 | // functions 50 | void read_cache_init( 51 | read_cache_state_t* state, 52 | request_context_t* request_context, 53 | size_t buffer_size); 54 | 55 | vod_status_t read_cache_allocate_buffer_slots( 56 | read_cache_state_t* state, 57 | size_t buffer_count); 58 | 59 | bool_t read_cache_get_from_cache( 60 | read_cache_state_t* state, 61 | read_cache_request_t* request, 62 | u_char** buffer, 63 | uint32_t* size); 64 | 65 | void read_cache_disable_buffer_reuse( 66 | read_cache_state_t* state); 67 | 68 | void read_cache_get_read_buffer( 69 | read_cache_state_t* state, 70 | read_cache_get_read_buffer_t* result); 71 | 72 | void read_cache_read_completed(read_cache_state_t* state, vod_buf_t* buf); 73 | 74 | #endif // __READ_CACHE_H__ 75 | -------------------------------------------------------------------------------- /ngx_perf_counters.c: -------------------------------------------------------------------------------- 1 | #include "ngx_perf_counters.h" 2 | 3 | #define LOG_CONTEXT_FORMAT " in perf counters \"%V\"%Z" 4 | 5 | const ngx_str_t perf_counters_open_tags[] = { 6 | #define PC(id, name) { sizeof(#name) - 1 + 4, (u_char*)("<" #name ">\r\n") }, 7 | #include "ngx_perf_counters_x.h" 8 | #undef PC 9 | }; 10 | 11 | const ngx_str_t perf_counters_close_tags[] = { 12 | #define PC(id, name) { sizeof(#name) - 1 + 5, (u_char*)("\r\n") }, 13 | #include "ngx_perf_counters_x.h" 14 | #undef PC 15 | }; 16 | 17 | static ngx_int_t 18 | ngx_perf_counters_init(ngx_shm_zone_t *shm_zone, void *data) 19 | { 20 | ngx_perf_counters_t *state; 21 | ngx_slab_pool_t *shpool; 22 | u_char* p; 23 | 24 | if (data) 25 | { 26 | shm_zone->data = data; 27 | return NGX_OK; 28 | } 29 | 30 | shpool = (ngx_slab_pool_t *)shm_zone->shm.addr; 31 | 32 | if (shm_zone->shm.exists) 33 | { 34 | shm_zone->data = shpool->data; 35 | return NGX_OK; 36 | } 37 | 38 | // start following the ngx_slab_pool_t that was allocated at the beginning of the chunk 39 | p = shm_zone->shm.addr + sizeof(ngx_slab_pool_t); 40 | 41 | // initialize the log context 42 | shpool->log_ctx = p; 43 | p = ngx_sprintf(shpool->log_ctx, LOG_CONTEXT_FORMAT, &shm_zone->shm.name); 44 | 45 | // allocate the perf couonters state 46 | p = ngx_align_ptr(p, sizeof(ngx_atomic_t)); 47 | state = (ngx_perf_counters_t*)p; 48 | 49 | ngx_memzero(state, sizeof(*state)); 50 | 51 | shpool->data = state; 52 | 53 | return NGX_OK; 54 | } 55 | 56 | ngx_shm_zone_t* 57 | ngx_perf_counters_create_zone(ngx_conf_t *cf, ngx_str_t *name, void *tag) 58 | { 59 | ngx_shm_zone_t* result; 60 | size_t size; 61 | 62 | size = sizeof(ngx_slab_pool_t) + sizeof(LOG_CONTEXT_FORMAT) + name->len + sizeof(ngx_atomic_t) + sizeof(ngx_perf_counters_t); 63 | 64 | result = ngx_shared_memory_add(cf, name, size, tag); 65 | if (result == NULL) 66 | { 67 | return NULL; 68 | } 69 | 70 | result->init = ngx_perf_counters_init; 71 | return result; 72 | } 73 | -------------------------------------------------------------------------------- /conf/kaltura.conf.template: -------------------------------------------------------------------------------- 1 | 2 | # internal location for vod subrequests 3 | location /kalapi_proxy/ { 4 | internal; 5 | proxy_pass @PROTOCOL@://kalapi/; 6 | proxy_set_header Host $http_host; 7 | } 8 | 9 | # base locations 10 | include @NGINX_CONF_PATH@/base.conf; 11 | 12 | # serve flavor progressive 13 | location ~ ^/p/\d+/(sp/\d+/)?serveFlavor/ { 14 | vod none; 15 | 16 | directio 512; 17 | output_buffers 1 512k; 18 | 19 | include @NGINX_CONF_PATH@/cors.conf; 20 | } 21 | 22 | # serve flavor HLS 23 | location ~ ^/hls/p/\d+/(sp/\d+/)?serveFlavor/ { 24 | vod hls; 25 | vod_bootstrap_segment_durations 2000; 26 | vod_bootstrap_segment_durations 2000; 27 | vod_bootstrap_segment_durations 2000; 28 | vod_bootstrap_segment_durations 4000; 29 | 30 | include @NGINX_CONF_PATH@/cors.conf; 31 | } 32 | 33 | # serve flavor DASH 34 | location ~ ^/dash/p/\d+/(sp/\d+/)?serveFlavor/ { 35 | vod dash; 36 | vod_segment_duration 4000; 37 | vod_dash_manifest_format segmenttemplate; 38 | vod_manifest_duration_policy min; 39 | 40 | include @NGINX_CONF_PATH@/cors.conf; 41 | } 42 | 43 | # serve flavor HDS 44 | location ~ ^/hds/p/\d+/(sp/\d+/)?serveFlavor/ { 45 | vod hds; 46 | vod_segment_duration 6000; 47 | vod_segment_count_policy last_rounded; 48 | 49 | include @NGINX_CONF_PATH@/cors.conf; 50 | } 51 | 52 | # serve flavor MSS 53 | location ~ ^/mss/p/\d+/(sp/\d+/)?serveFlavor/ { 54 | vod mss; 55 | vod_segment_duration 4000; 56 | vod_manifest_segment_durations_mode accurate; 57 | 58 | include @NGINX_CONF_PATH@/cors.conf; 59 | } 60 | 61 | # static files (crossdomain.xml, robots.txt etc.) + fallback to api 62 | location / { 63 | root @STATIC_FILES_PATH@; 64 | try_files $uri @api_fallback; 65 | } 66 | 67 | # all unidentified requests fallback to api (inc. playManifest) 68 | location @api_fallback { 69 | proxy_pass @PROTOCOL@://kalapi; 70 | proxy_set_header Host $http_host; 71 | } 72 | -------------------------------------------------------------------------------- /vod/read_stream.h: -------------------------------------------------------------------------------- 1 | #ifndef __READ_STREAM_H__ 2 | #define __READ_STREAM_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | // int parsing macros 8 | #define parse_le32(p) ( ((uint32_t) ((u_char*)(p))[3] << 24) | (((u_char*)(p))[2] << 16) | (((u_char*)(p))[1] << 8) | (((u_char*)(p))[0]) ) 9 | #define parse_be16(p) ( ((uint16_t) ((u_char*)(p))[0] << 8) | (((u_char*)(p))[1]) ) 10 | #define parse_be32(p) ( ((uint32_t) ((u_char*)(p))[0] << 24) | (((u_char*)(p))[1] << 16) | (((u_char*)(p))[2] << 8) | (((u_char*)(p))[3]) ) 11 | #define parse_be64(p) ((((uint64_t)parse_be32(p)) << 32) | parse_be32((p) + 4)) 12 | 13 | // int reading macros 14 | #define read_le32(p, v) { v = parse_le32(p); p += sizeof(uint32_t); } 15 | #define read_be16(p, v) { v = parse_be16(p); p += sizeof(uint16_t); } 16 | #define read_be32(p, v) { v = parse_be32(p); p += sizeof(uint32_t); } 17 | #define read_be64(p, v) { v = parse_be64(p); p += sizeof(uint64_t); } 18 | 19 | // typedefs 20 | typedef struct { 21 | const u_char* cur_pos; 22 | const u_char* end_pos; 23 | bool_t eof_reached; 24 | } simple_read_stream_t; 25 | 26 | // functions 27 | static vod_inline u_char 28 | read_stream_get_byte(simple_read_stream_t* stream) 29 | { 30 | if (stream->cur_pos >= stream->end_pos) 31 | { 32 | stream->eof_reached = TRUE; 33 | return 0; 34 | } 35 | return *stream->cur_pos++; 36 | } 37 | 38 | static vod_inline uint32_t 39 | read_stream_get_be32(simple_read_stream_t* stream) 40 | { 41 | uint32_t result; 42 | 43 | if (stream->cur_pos + sizeof(uint32_t) > stream->end_pos) 44 | { 45 | stream->eof_reached = TRUE; 46 | stream->cur_pos = stream->end_pos; 47 | return 0; 48 | } 49 | 50 | read_be32(stream->cur_pos, result); 51 | return result; 52 | } 53 | 54 | static vod_inline void 55 | read_stream_skip(simple_read_stream_t* stream, int bytes) 56 | { 57 | if (stream->cur_pos + bytes > stream->end_pos) 58 | { 59 | stream->eof_reached = TRUE; 60 | stream->cur_pos = stream->end_pos; 61 | } 62 | else 63 | { 64 | stream->cur_pos += bytes; 65 | } 66 | } 67 | 68 | #endif // __READ_STREAM_H__ 69 | -------------------------------------------------------------------------------- /ngx_file_reader.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_FILE_READER_H_INCLUDED_ 2 | #define _NGX_FILE_READER_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #if (NGX_THREADS) 11 | #include "ngx_async_open_file_cache.h" 12 | #endif // NGX_THREADS 13 | 14 | // constants 15 | #define OPEN_FILE_NO_CACHE (0x1) 16 | 17 | // typedefs 18 | typedef void (*ngx_async_read_callback_t)(void* context, ngx_int_t rc, ngx_buf_t* buf, ssize_t bytes_read); 19 | 20 | typedef struct { 21 | ngx_http_request_t *r; 22 | ngx_file_t file; 23 | off_t directio; 24 | ngx_flag_t log_not_found; 25 | ngx_log_t* log; 26 | off_t file_size; 27 | #if (NGX_HAVE_FILE_AIO) 28 | ngx_flag_t use_aio; 29 | ngx_async_read_callback_t read_callback; 30 | void* callback_context; 31 | ngx_buf_t* buf; 32 | #endif // NGX_HAVE_FILE_AIO 33 | } ngx_file_reader_state_t; 34 | 35 | // functions 36 | ngx_int_t ngx_file_reader_init( 37 | ngx_file_reader_state_t* state, 38 | ngx_async_read_callback_t read_callback, 39 | void* callback_context, 40 | ngx_http_request_t *r, 41 | ngx_http_core_loc_conf_t *clcf, 42 | ngx_str_t* path, 43 | uint32_t flags); 44 | 45 | #if (NGX_THREADS) 46 | ngx_int_t ngx_file_reader_init_async( 47 | ngx_file_reader_state_t* state, 48 | void** context, 49 | ngx_thread_pool_t *thread_pool, 50 | ngx_async_open_file_callback_t open_callback, 51 | ngx_async_read_callback_t read_callback, 52 | void* callback_context, 53 | ngx_http_request_t *r, 54 | ngx_http_core_loc_conf_t *clcf, 55 | ngx_str_t* path, 56 | uint32_t flags); 57 | #endif // NGX_THREADS 58 | 59 | ngx_int_t ngx_file_reader_dump_file_part(void* context, off_t start, off_t end); 60 | 61 | size_t ngx_file_reader_get_size(void* context); 62 | 63 | void ngx_file_reader_get_path(void* context, ngx_str_t* path); 64 | 65 | ngx_int_t ngx_async_file_read(ngx_file_reader_state_t* state, ngx_buf_t *buf, size_t size, off_t offset); 66 | 67 | ngx_int_t ngx_file_reader_enable_directio(ngx_file_reader_state_t* state); 68 | 69 | #endif // _NGX_FILE_READER_H_INCLUDED_ 70 | -------------------------------------------------------------------------------- /vod/mss/mss_packager.h: -------------------------------------------------------------------------------- 1 | #ifndef __MSS_PACKAGER_H__ 2 | #define __MSS_PACKAGER_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | #include "../segmenter.h" 7 | #include "../common.h" 8 | 9 | // constants 10 | #define MSS_STREAM_TYPE_VIDEO "video" 11 | #define MSS_STREAM_TYPE_AUDIO "audio" 12 | #define MSS_STREAM_TYPE_TEXT "text" 13 | #define MSS_TIMESCALE (10000000) 14 | 15 | // macros 16 | // Note: in order to be able to process fragment requests efficiently, we need to know the file index and track index 17 | // of the fragment. since we only have the bitrate on the URL, we encode this parameters on the bitrate. 18 | // since both parameters are limited to 32, this results in a maximum of 1kpbs diviation from the real bitrate. 19 | #define mss_encode_indexes(bitrate, sequence_index, track_index) (((bitrate) & ~0x3FF) | (((sequence_index) & 0x1F) << 5) | ((track_index) & 0x1F)) 20 | #define mss_sequence_index(bitrate) (((bitrate) >> 5) & 0x1F) 21 | #define mss_track_index(bitrate) ((bitrate) & 0x1F) 22 | 23 | //typedefs 24 | typedef u_char* (*mss_write_extra_traf_atoms_callback_t)(void* context, u_char* p, size_t mdat_atom_start); 25 | 26 | typedef u_char* (*mss_write_tags_callback_t)(void* context, u_char* p, media_set_t* media_set); 27 | 28 | typedef struct { 29 | vod_uint_t duplicate_bitrate_threshold; 30 | } mss_manifest_config_t; 31 | 32 | // functions 33 | vod_status_t mss_packager_build_manifest( 34 | request_context_t* request_context, 35 | mss_manifest_config_t* conf, 36 | media_set_t* media_set, 37 | size_t extra_tags_size, 38 | mss_write_tags_callback_t write_extra_tags, 39 | void* extra_tags_writer_context, 40 | vod_str_t* result); 41 | 42 | vod_status_t mss_packager_build_fragment_header( 43 | request_context_t* request_context, 44 | media_set_t* media_set, 45 | uint32_t segment_index, 46 | size_t extra_traf_atoms_size, 47 | mss_write_extra_traf_atoms_callback_t write_extra_traf_atoms_callback, 48 | void* write_extra_traf_atoms_context, 49 | bool_t size_only, 50 | vod_str_t* result, 51 | size_t* total_fragment_size); 52 | 53 | #endif // __MSS_PACKAGER_H__ 54 | -------------------------------------------------------------------------------- /test/test_open_file_cache.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | # for coverage analysis using gcov 4 | user root; 5 | master_process off; 6 | daemon off; 7 | 8 | error_log /var/log/nginx/error.log debug; 9 | 10 | pid /var/run/nginx.pid; 11 | 12 | events { 13 | worker_connections 1024; 14 | multi_accept on; 15 | use epoll; 16 | } 17 | 18 | thread_pool test threads=32; 19 | 20 | http { 21 | include mime.types; 22 | default_type application/octet-stream; 23 | 24 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 25 | '$status $bytes_sent $request_time "$http_referer" ' 26 | '"$http_user_agent" "$http_x_kaltura_f5_https" $http_x_kaltura_f5_remote_addr ' 27 | '"$sent_http_x_kaltura" "$http_host" $pid $sent_http_x_kaltura_session - ' 28 | '$request_length "$sent_http_content_range" "$http_x_forwarded_for" ' 29 | '"$http_x_forwarded_server" "$http_x_forwarded_host" "$sent_http_cache_control" ' 30 | '$connection $request_id '; 31 | 32 | access_log /var/log/nginx/access.log main; 33 | 34 | sendfile on; 35 | tcp_nopush on; 36 | tcp_nodelay on; 37 | 38 | add_header X-Me $hostname; 39 | 40 | # common file caching / aio 41 | open_file_cache max=100 inactive=2; 42 | open_file_cache_valid 1; 43 | open_file_cache_min_uses 1; 44 | open_file_cache_errors on; 45 | 46 | vod_open_file_thread_pool test; 47 | 48 | aio on; 49 | 50 | server { 51 | 52 | listen 8001 backlog=1024; 53 | server_name localhost; 54 | vod_segment_duration 10000; 55 | 56 | requestid on; 57 | 58 | # vod status page 59 | location /vod_status { 60 | vod_status; 61 | access_log off; 62 | } 63 | 64 | # local 65 | location /local/content/ { 66 | vod none; 67 | vod_mode local; 68 | 69 | add_header X-Me $hostname; 70 | add_header Last-Modified "Sun, 19 Nov 2000 08:52:00 GMT"; 71 | expires 100d; 72 | } 73 | 74 | # redirect server error pages to the static page /50x.html 75 | # 76 | error_page 500 502 503 504 /50x.html; 77 | location = /50x.html { 78 | root html; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /vod/hls/m3u8_builder.h: -------------------------------------------------------------------------------- 1 | #ifndef __M3U8_BUILDER_H__ 2 | #define __M3U8_BUILDER_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | #include "../segmenter.h" 7 | #include "hls_muxer.h" 8 | 9 | // constants 10 | #define MAX_IFRAMES_M3U8_HEADER_SIZE (sizeof(iframes_m3u8_header_format) + VOD_INT64_LEN) 11 | 12 | static const char iframes_m3u8_header_format[] = "#EXTM3U\n#EXT-X-TARGETDURATION:%d\n#EXT-X-VERSION:4\n#EXT-X-MEDIA-SEQUENCE:1\n#EXT-X-PLAYLIST-TYPE:VOD\n#EXT-X-I-FRAMES-ONLY\n"; 13 | 14 | // typedefs 15 | enum { 16 | HLS_CONTAINER_AUTO, 17 | HLS_CONTAINER_MPEGTS, 18 | HLS_CONTAINER_FMP4, 19 | }; 20 | 21 | typedef struct { 22 | int m3u8_version; 23 | vod_uint_t container_format; 24 | u_char iframes_m3u8_header[MAX_IFRAMES_M3U8_HEADER_SIZE]; 25 | size_t iframes_m3u8_header_len; 26 | bool_t force_unmuxed_segments; 27 | bool_t output_iframes_playlist; 28 | vod_str_t index_file_name_prefix; 29 | vod_str_t iframes_file_name_prefix; 30 | vod_str_t segment_file_name_prefix; 31 | vod_str_t init_file_name_prefix; 32 | vod_str_t encryption_key_file_name; 33 | vod_str_t encryption_key_format; 34 | vod_str_t encryption_key_format_versions; 35 | } m3u8_config_t; 36 | 37 | // functions 38 | vod_status_t m3u8_builder_build_master_playlist( 39 | request_context_t* request_context, 40 | m3u8_config_t* conf, 41 | vod_uint_t encryption_method, 42 | vod_str_t* base_url, 43 | media_set_t* media_set, 44 | vod_str_t* result); 45 | 46 | vod_status_t m3u8_builder_build_index_playlist( 47 | request_context_t* request_context, 48 | m3u8_config_t* conf, 49 | vod_str_t* base_url, 50 | vod_str_t* segments_base_url, 51 | hls_encryption_params_t* encryption_params, 52 | vod_uint_t container_format, 53 | media_set_t* media_set, 54 | vod_str_t* result); 55 | 56 | vod_status_t m3u8_builder_build_iframe_playlist( 57 | request_context_t* request_context, 58 | m3u8_config_t* conf, 59 | hls_mpegts_muxer_conf_t* muxer_conf, 60 | vod_str_t* base_url, 61 | media_set_t* media_set, 62 | vod_str_t* result); 63 | 64 | void m3u8_builder_init_config( 65 | m3u8_config_t* conf, 66 | uint32_t max_segment_duration, 67 | hls_encryption_type_t encryption_method); 68 | 69 | #endif // __M3U8_BUILDER_H__ 70 | -------------------------------------------------------------------------------- /vod/avc_hevc_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef __AVC_HEVC_PARSER_H__ 2 | #define __AVC_HEVC_PARSER_H__ 3 | 4 | #include "bit_read_stream.h" 5 | 6 | // constants 7 | #define MAX_SPS_COUNT (32) 8 | #define MAX_PPS_COUNT (256) 9 | #define EXTENDED_SAR (255) 10 | 11 | // macros 12 | #define bit_read_stream_skip_signed_exp bit_read_stream_skip_unsigned_exp 13 | 14 | // typedefs 15 | typedef struct { 16 | request_context_t* request_context; 17 | vod_array_t sps; 18 | vod_array_t pps; 19 | } avc_hevc_parse_ctx_t; 20 | 21 | // bit stream inlines 22 | static vod_inline void 23 | bit_read_stream_skip_unsigned_exp(bit_reader_state_t* reader) 24 | { 25 | int zero_count; 26 | 27 | for (zero_count = 0; bit_read_stream_get_one(reader) == 0 && !reader->stream.eof_reached; zero_count++); 28 | 29 | bit_read_stream_skip(reader, zero_count); 30 | } 31 | 32 | static vod_inline uint32_t 33 | bit_read_stream_get_unsigned_exp(bit_reader_state_t* reader) 34 | { 35 | int zero_count; 36 | 37 | for (zero_count = 0; bit_read_stream_get_one(reader) == 0 && !reader->stream.eof_reached; zero_count++); 38 | 39 | return (1 << zero_count) - 1 + bit_read_stream_get(reader, zero_count); 40 | } 41 | 42 | static vod_inline int32_t 43 | bit_read_stream_get_signed_exp(bit_reader_state_t* reader) 44 | { 45 | int32_t value = bit_read_stream_get_unsigned_exp(reader); 46 | if (value > 0) 47 | { 48 | if (value & 1) // positive 49 | { 50 | value = (value + 1) / 2; 51 | } 52 | else 53 | { 54 | value = -(value / 2); 55 | } 56 | } 57 | return value; 58 | } 59 | 60 | // functions 61 | bool_t avc_hevc_parser_rbsp_trailing_bits(bit_reader_state_t* reader); 62 | 63 | void* avc_hevc_parser_get_ptr_array_item(vod_array_t* arr, size_t index, size_t size); 64 | 65 | uint32_t avc_hevc_parser_emulation_prevention_encode_bytes( 66 | const u_char* cur_pos, 67 | const u_char* end_pos); 68 | 69 | vod_status_t avc_hevc_parser_emulation_prevention_decode( 70 | request_context_t* request_context, 71 | bit_reader_state_t* reader, 72 | const u_char* buffer, 73 | uint32_t size); 74 | 75 | unsigned avc_hevc_parser_ceil_log2(unsigned val); 76 | 77 | vod_status_t avc_hevc_parser_init_ctx( 78 | request_context_t* request_context, 79 | void** result); 80 | 81 | #endif //__AVC_HEVC_PARSER_H__ 82 | -------------------------------------------------------------------------------- /test/mp4_utils.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import re 3 | 4 | def isContainerAtom(inputData, startOffset, endOffset): 5 | if endOffset - startOffset < 8: 6 | return False 7 | if not re.match('^[0-9a-zA-Z]+$', inputData[(startOffset + 4):(startOffset + 8)]): 8 | return False 9 | atomSize = struct.unpack('>L', inputData[startOffset:(startOffset + 4)])[0] 10 | if atomSize > endOffset - startOffset: 11 | return False 12 | return True 13 | 14 | def parseAtoms(inputData, startOffset, endOffset): 15 | result = {} 16 | curPos = startOffset 17 | while curPos + 8 <= endOffset: 18 | # get atom size and atom name 19 | atomSize = struct.unpack('>L', inputData[curPos:(curPos + 4)])[0] 20 | atomName = inputData[(curPos + 4):(curPos + 8)] 21 | if atomSize == 1: 22 | atomSize = struct.unpack('>Q', inputData[(curPos + 8):(curPos + 16)])[0] 23 | atomHeaderSize = 16 24 | else: 25 | atomHeaderSize = 8 26 | if atomSize == 0: 27 | atomSize = endOffset - curPos 28 | 29 | # get child atoms 30 | childAtoms = {} 31 | if isContainerAtom(inputData, curPos + atomHeaderSize, curPos + atomSize): 32 | childAtoms = parseAtoms(inputData, curPos + atomHeaderSize, min(curPos + atomSize, endOffset)) 33 | 34 | # add the result 35 | result.setdefault(atomName, []) 36 | result[atomName].append((curPos, atomHeaderSize, curPos + atomSize, childAtoms)) 37 | curPos += atomSize 38 | return result 39 | 40 | def getAtomInternal(splittedPath, baseAtom): 41 | if not baseAtom.has_key(splittedPath[0]): 42 | return None 43 | for curAtom in baseAtom[splittedPath[0]][::-1]: 44 | if len(splittedPath) == 1: 45 | return curAtom 46 | result = getAtomInternal(splittedPath[1:], curAtom[3]) 47 | if result != None: 48 | return result 49 | return None 50 | 51 | def getAtom(inputAtoms, path): 52 | return getAtomInternal(path.split('.'), inputAtoms) 53 | 54 | def getAtomData(inputData, inputAtoms, path): 55 | atom = getAtom(inputAtoms, path) 56 | if atom == None: 57 | return False 58 | return inputData[(atom[0] + atom[1]):atom[2]] 59 | -------------------------------------------------------------------------------- /vod/manifest_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __MANIFEST_UTILS_H__ 2 | #define __MANIFEST_UTILS_H__ 3 | 4 | // includes 5 | #include "media_set.h" 6 | 7 | // constants 8 | #define ADAPTATION_SETS_FLAG_MUXED (0x1) 9 | #define ADAPTATION_SETS_FLAG_EXCLUDE_MUXED_AUDIO (0x2) 10 | #define ADAPTATION_SETS_FLAG_SINGLE_LANG_TRACK (0x4) 11 | #define ADAPTATION_SETS_FLAG_AVOID_AUDIO_ONLY (0x8) 12 | #define ADAPTATION_SETS_FLAG_DEFAULT_LANG_LAST (0x10) 13 | #define ADAPTATION_SETS_FLAG_MULTI_AUDIO_CODEC (0x20) 14 | #define ADAPTATION_SETS_FLAG_MULTI_VIDEO_CODEC (0x40) 15 | 16 | #define ADAPTATION_SETS_FLAG_MULTI_CODEC (ADAPTATION_SETS_FLAG_MULTI_AUDIO_CODEC | ADAPTATION_SETS_FLAG_MULTI_VIDEO_CODEC) 17 | 18 | #define MANIFEST_UTILS_TRACKS_SPEC_MAX_SIZE (sizeof("-f-v-f-a") - 1 + VOD_INT32_LEN * 4) 19 | 20 | // enums 21 | enum { // Note: must match media type in order 22 | ADAPTATION_TYPE_VIDEO, 23 | ADAPTATION_TYPE_AUDIO, 24 | ADAPTATION_TYPE_SUBTITLE, 25 | ADAPTATION_TYPE_MUXED, 26 | ADAPTATION_TYPE_COUNT, 27 | }; 28 | 29 | // typedefs 30 | typedef struct { 31 | media_track_t** first; // [count * (type == muxed ? MEDIA_TYPE_COUNT : 1)] 32 | media_track_t** last; 33 | uint32_t type; 34 | uint32_t count; 35 | } adaptation_set_t; 36 | 37 | typedef struct { 38 | adaptation_set_t* first; // [total_count] 39 | adaptation_set_t* last; 40 | adaptation_set_t* first_by_type[ADAPTATION_TYPE_COUNT]; 41 | uint32_t count[ADAPTATION_TYPE_COUNT]; 42 | uint32_t total_count; 43 | bool_t multi_audio; 44 | } adaptation_sets_t; 45 | 46 | // functions 47 | vod_status_t manifest_utils_build_request_params_string( 48 | request_context_t* request_context, 49 | track_mask_t* has_tracks, 50 | uint32_t segment_index, 51 | uint32_t sequences_mask, 52 | sequence_tracks_mask_t* sequence_tracks_mask, 53 | sequence_tracks_mask_t* sequence_tracks_mask_end, 54 | track_mask_t* tracks_mask, 55 | vod_str_t* result); 56 | 57 | u_char* manifest_utils_append_tracks_spec( 58 | u_char* p, 59 | media_track_t** tracks, 60 | uint32_t track_count, 61 | bool_t write_sequence_index); 62 | 63 | vod_status_t manifest_utils_get_adaptation_sets( 64 | request_context_t* request_context, 65 | media_set_t* media_set, 66 | uint32_t flags, 67 | adaptation_sets_t* output); 68 | 69 | #endif //__MANIFEST_UTILS_H__ 70 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: GitHub CI 2 | 3 | on: [push, pull_request] 4 | 5 | defaults: 6 | run: 7 | shell: 'bash -Eeuo pipefail -x {0}' 8 | 9 | jobs: 10 | build-and-test: 11 | name: Build and Test 12 | runs-on: ubuntu-20.04 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | compiler: [clang-12, gcc-10] 17 | confargs: ['--add-module=./nginx-vod-module', '--add-dynamic-module=./nginx-vod-module'] 18 | env: 19 | CC: ${{ matrix.compiler }} 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Install Dependencies 23 | run: | 24 | sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 25 | sudo apt-get update -qq 26 | sudo apt-get install -y \ 27 | build-essential \ 28 | clang-12 \ 29 | gcc-10 \ 30 | python3 \ 31 | libssl-dev \ 32 | libpcre3-dev \ 33 | libxml2-dev \ 34 | curl \ 35 | unzip \ 36 | libopencore-amrwb0 \ 37 | libopencore-amrnb0 \ 38 | libass9 \ 39 | libgsm1 \ 40 | libmp3lame0 \ 41 | libjpeg9 \ 42 | libspeex1 \ 43 | libtheora0 \ 44 | libva2 \ 45 | libvpx6 \ 46 | libxvidcore4 \ 47 | libnuma1 \ 48 | libvdpau1 \ 49 | libfdk-aac1 \ 50 | libva-drm2 \ 51 | libva-x11-2 \ 52 | libvorbisfile3 \ 53 | libopenjp2-7 \ 54 | libzvbi0 \ 55 | libx264-155 \ 56 | libx265-179 \ 57 | libxcb1-dev \ 58 | libxcb-shm0-dev \ 59 | libvorbisenc2 60 | wget http://installrepo.origin.kaltura.org/repo/aptn/test_debs/kaltura-ffmpeg_4.2.2-1_amd64.deb 61 | sudo dpkg -i kaltura-ffmpeg_4.2.2-1_amd64.deb 62 | - name : Build 63 | run: ./ci_build.sh ${{ matrix.confargs }} 64 | - name : Install 65 | run : | 66 | cd /tmp/builddir/nginx 67 | sudo make install 68 | - name : Test smoke 69 | run : /sbin/nginx -V 70 | - name : Test bitset 71 | run : | 72 | cd test/bitset 73 | NGX_ROOT=/tmp/builddir/nginx VOD_ROOT=../../ bash build.sh 74 | ./bitsettest 75 | -------------------------------------------------------------------------------- /vod/bit_read_stream.h: -------------------------------------------------------------------------------- 1 | #ifndef __BIT_READ_STREAM_H__ 2 | #define __BIT_READ_STREAM_H__ 3 | 4 | // includes 5 | #include "read_stream.h" 6 | 7 | // typedefs 8 | typedef struct { 9 | simple_read_stream_t stream; 10 | u_char cur_byte; 11 | signed char cur_bit; 12 | } bit_reader_state_t; 13 | 14 | // functions 15 | static vod_inline void 16 | bit_read_stream_init(bit_reader_state_t* state, const u_char* buffer, int size) 17 | { 18 | state->stream.cur_pos = buffer; 19 | state->stream.end_pos = buffer + size; 20 | state->stream.eof_reached = FALSE; 21 | state->cur_byte = 0; 22 | state->cur_bit = -1; 23 | } 24 | 25 | static vod_inline int 26 | bit_read_stream_get_one(bit_reader_state_t* state) 27 | { 28 | int result; 29 | 30 | if (state->cur_bit < 0) 31 | { 32 | state->cur_byte = read_stream_get_byte(&state->stream); 33 | state->cur_bit = 7; 34 | } 35 | 36 | result = ((state->cur_byte >> state->cur_bit) & 1); 37 | state->cur_bit--; 38 | 39 | return result; 40 | } 41 | 42 | static vod_inline void 43 | bit_read_stream_skip(bit_reader_state_t* state, int count) 44 | { 45 | int skip_bytes; 46 | 47 | state->cur_bit -= count - 1; 48 | if (state->cur_bit < 0) 49 | { 50 | skip_bytes = ((-state->cur_bit + 7) >> 3); 51 | read_stream_skip(&state->stream, skip_bytes - 1); 52 | state->cur_byte = read_stream_get_byte(&state->stream); 53 | state->cur_bit += (skip_bytes << 3); 54 | } 55 | state->cur_bit--; 56 | } 57 | 58 | static vod_inline int 59 | bit_read_stream_get(bit_reader_state_t* state, int count) 60 | { 61 | int result = 0; 62 | 63 | for (; count; count--) 64 | { 65 | if (state->cur_bit < 0) 66 | { 67 | state->cur_byte = read_stream_get_byte(&state->stream); 68 | state->cur_bit = 7; 69 | } 70 | 71 | result = (result << 1) | ((state->cur_byte >> state->cur_bit) & 1); 72 | state->cur_bit--; 73 | } 74 | return result; 75 | } 76 | 77 | static vod_inline int64_t 78 | bit_read_stream_get_long(bit_reader_state_t* state, int count) 79 | { 80 | int64_t result = 0; 81 | 82 | for (; count; count--) 83 | { 84 | if (state->cur_bit < 0) 85 | { 86 | state->cur_byte = read_stream_get_byte(&state->stream); 87 | state->cur_bit = 7; 88 | } 89 | 90 | result = (result << 1) | ((state->cur_byte >> state->cur_bit) & 1); 91 | state->cur_bit--; 92 | } 93 | return result; 94 | } 95 | 96 | #endif // __BIT_READ_STREAM_H__ 97 | -------------------------------------------------------------------------------- /vod/dash/dash_packager.h: -------------------------------------------------------------------------------- 1 | #ifndef __DASH_PACKAGER_H__ 2 | #define __DASH_PACKAGER_H__ 3 | 4 | // includes 5 | #include "../media_format.h" 6 | #include "../segmenter.h" 7 | #include "../common.h" 8 | 9 | // constants 10 | #define DASH_TIMESCALE (90000) 11 | 12 | // typedefs 13 | enum { 14 | FORMAT_SEGMENT_LIST, 15 | FORMAT_SEGMENT_TIMELINE, 16 | FORMAT_SEGMENT_TEMPLATE, 17 | }; 18 | 19 | enum { 20 | SUBTITLE_FORMAT_WEBVTT, 21 | SUBTITLE_FORMAT_SMPTE_TT, 22 | }; 23 | 24 | typedef u_char* (*dash_write_extra_traf_atoms_callback_t)(void* context, u_char* p, size_t mdat_atom_start); 25 | 26 | typedef u_char* (*dash_write_mdat_atom_callback_t)(void* context, u_char* p); 27 | 28 | typedef u_char* (*write_tags_callback_t)(void* context, u_char* p, media_track_t* track); 29 | 30 | typedef struct { 31 | size_t size; 32 | write_tags_callback_t write; 33 | void* context; 34 | } tags_writer_t; 35 | 36 | typedef struct { 37 | vod_str_t profiles; 38 | vod_str_t init_file_name_prefix; 39 | vod_str_t fragment_file_name_prefix; 40 | vod_str_t subtitle_file_name_prefix; 41 | vod_uint_t manifest_format; 42 | vod_uint_t subtitle_format; 43 | vod_uint_t duplicate_bitrate_threshold; 44 | bool_t write_playready_kid; // TODO: remove 45 | bool_t use_base_url_tag; // TODO: remove - if supported by all devices, always use BaseURL 46 | } dash_manifest_config_t; 47 | 48 | typedef struct { 49 | tags_writer_t representation; 50 | tags_writer_t adaptation_set; 51 | } dash_manifest_extensions_t; 52 | 53 | typedef struct { 54 | size_t extra_traf_atoms_size; 55 | dash_write_extra_traf_atoms_callback_t write_extra_traf_atoms_callback; 56 | void* write_extra_traf_atoms_context; 57 | 58 | size_t mdat_atom_max_size; 59 | dash_write_mdat_atom_callback_t write_mdat_atom_callback; 60 | void* write_mdat_atom_context; 61 | } dash_fragment_header_extensions_t; 62 | 63 | // functions 64 | vod_status_t dash_packager_build_mpd( 65 | request_context_t* request_context, 66 | dash_manifest_config_t* conf, 67 | vod_str_t* base_url, 68 | media_set_t* media_set, 69 | dash_manifest_extensions_t* extensions, 70 | vod_str_t* result); 71 | 72 | vod_status_t dash_packager_build_fragment_header( 73 | request_context_t* request_context, 74 | media_set_t* media_set, 75 | uint32_t segment_index, 76 | uint32_t sample_description_index, 77 | dash_fragment_header_extensions_t* extensions, 78 | bool_t size_only, 79 | vod_str_t* result, 80 | size_t* total_fragment_size); 81 | 82 | #endif // __DASH_PACKAGER_H__ 83 | -------------------------------------------------------------------------------- /ci_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eo nounset # Treat unset variables as an error 3 | 4 | BASE_DOWNLOAD_URI=http://nginx.org/download 5 | NGINX_VERSION=`curl -L "$BASE_DOWNLOAD_URI" | 6 | grep -oP 'href="nginx-\K[0-9]+\.[0-9]+\.[0-9]+' | 7 | sort -t. -rn -k1,1 -k2,2 -k3,3 | head -1` 8 | NGINX_URI="$BASE_DOWNLOAD_URI/nginx-$NGINX_VERSION.tar.gz" 9 | 10 | if [ ! -x "`which curl 2>/dev/null`" ];then 11 | echo "Need to install curl." 12 | exit 2 13 | fi 14 | 15 | mkdir -p /tmp/builddir/nginx-$NGINX_VERSION 16 | cp -r . /tmp/builddir/nginx-$NGINX_VERSION/nginx-vod-module 17 | cd /tmp/builddir 18 | curl $NGINX_URI > kaltura-nginx-$NGINX_VERSION.tar.gz 19 | tar zxf kaltura-nginx-$NGINX_VERSION.tar.gz 20 | mv nginx-$NGINX_VERSION nginx 21 | cd nginx 22 | 23 | FFMPEG_VERSION=4.2.2 24 | LD_LIBRARY_PATH=/opt/kaltura/ffmpeg-$FFMPEG_VERSION/lib 25 | LIBRARY_PATH=/opt/kaltura/ffmpeg-$FFMPEG_VERSION/lib 26 | C_INCLUDE_PATH=/opt/kaltura/ffmpeg-$FFMPEG_VERSION/include 27 | export LD_LIBRARY_PATH LIBRARY_PATH C_INCLUDE_PATH 28 | 29 | ./configure \ 30 | --prefix=/etc/nginx \ 31 | --sbin-path=/sbin/nginx \ 32 | --conf-path=/etc/nginx/nginx.conf \ 33 | --error-log-path=/var/log/log/nginx/error.log \ 34 | --http-log-path=/var/log/log/nginx/access.log \ 35 | --pid-path=/var/log/run/nginx.pid \ 36 | --lock-path=/var/log/run/nginx.lock \ 37 | --http-client-body-temp-path=/var/log/cache/nginx/client_temp \ 38 | --http-proxy-temp-path=/var/log/cache/nginx/proxy_temp \ 39 | --http-fastcgi-temp-path=/var/log/cache/nginx/fastcgi_temp \ 40 | --http-uwsgi-temp-path=/var/log/cache/nginx/uwsgi_temp \ 41 | --http-scgi-temp-path=/var/log/cache/nginx/scgi_temp \ 42 | --with-http_ssl_module \ 43 | --with-http_realip_module \ 44 | --with-http_addition_module \ 45 | --with-http_sub_module \ 46 | --with-http_dav_module \ 47 | --with-http_flv_module \ 48 | --with-http_mp4_module \ 49 | --with-http_gunzip_module \ 50 | --with-http_gzip_static_module \ 51 | --with-http_random_index_module \ 52 | --with-http_secure_link_module \ 53 | --with-http_stub_status_module \ 54 | --with-http_auth_request_module \ 55 | --with-mail \ 56 | --with-mail_ssl_module \ 57 | --with-file-aio \ 58 | --with-ipv6 \ 59 | --with-debug \ 60 | --with-threads \ 61 | --with-cc-opt="-O3 -mpopcnt" \ 62 | $* 63 | make -j $(nproc) 64 | -------------------------------------------------------------------------------- /test/test_static.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import commands 3 | import sys 4 | import re 5 | import os 6 | 7 | IGNORE_LIST = set([ 8 | 'vod_array_init_impl', 9 | 'vod_array_destroy', 10 | 'hls_encryption_methods', 11 | 'dash_manifest_formats', 12 | ]) 13 | 14 | if len(sys.argv) < 2: 15 | print('Usage:\n\tpython %s [ []]' % os.path.basename(__file__)) 16 | sys.exit(1) 17 | 18 | sourceRoot = sys.argv[1] 19 | if len(sys.argv) > 2: 20 | objectRoot = sys.argv[2] 21 | else: 22 | objectRoot = '' 23 | 24 | if len(sys.argv) > 3: 25 | useOnlyLastFolder = sys.argv[3].lower() == 'yes' 26 | else: 27 | useOnlyLastFolder = True 28 | 29 | funcNames = [] 30 | wordsByFile = {} 31 | wordsByHeaderFiles = set([]) 32 | for root, dirs, files in os.walk(sourceRoot): 33 | for name in files: 34 | fileExt = os.path.splitext(name)[1] 35 | fullPath = os.path.join(root, name) 36 | if fileExt == '.h': 37 | fileData = open(fullPath, 'rb').read() 38 | wordsByHeaderFiles.update(set(re.findall(r'\b(\w+)\b', fileData))) 39 | continue 40 | if fileExt != '.c': 41 | continue 42 | fileData = open(fullPath, 'rb').read() 43 | wordsByFile[fullPath] = set(re.findall(r'\b(\w+)\b', fileData)) 44 | if objectRoot == '': 45 | # detect exports from code 46 | for curLine in fileData.split('\n'): 47 | if re.match('^\w+\(', curLine) and not 'static' in prevLine: 48 | funcName = curLine.split('(')[0] 49 | funcNames.append(funcName) 50 | prevLine = curLine 51 | else: 52 | # get exports from object file 53 | if useOnlyLastFolder: 54 | objectFile = os.path.join(os.path.split(os.path.split(fullPath)[0])[1], os.path.split(fullPath)[1]) 55 | else: 56 | objectFile = fullPath[len(sourceRoot):] 57 | if objectFile.startswith('/'): 58 | objectFile = objectFile[1:] 59 | objectFile = os.path.join(objectRoot, os.path.splitext(objectFile)[0] + '.o') 60 | if not os.path.exists(objectFile): 61 | continue 62 | for curLine in commands.getoutput("readelf -Ws %s | grep -vw UND | grep -w GLOBAL | awk '{print $NF}'" % objectFile).split('\n'): 63 | funcName = curLine.strip() 64 | if len(funcName) > 0: 65 | funcNames.append(funcName) 66 | 67 | print('Found %s exports' % len(funcNames)) 68 | 69 | for funcName in funcNames: 70 | if funcName in IGNORE_LIST: 71 | continue 72 | fileCount = 0 73 | for words in wordsByFile.values(): 74 | if funcName in words: 75 | fileCount += 1 76 | if fileCount < 2: 77 | print(funcName, funcName in wordsByHeaderFiles) 78 | -------------------------------------------------------------------------------- /tools/persist_proxy/metrics.lua: -------------------------------------------------------------------------------- 1 | 2 | --[[ 3 | - exposes metrics on server performance in prom format 4 | --]] 5 | 6 | local log = ngx.log 7 | local ERR = ngx.ERR 8 | local ngx_var = ngx.var 9 | local ngx_now = ngx.now 10 | local start_time = ngx.req.start_time 11 | 12 | local pairs = pairs 13 | local concat = table.concat 14 | 15 | local HTTP_STATUS_KEY_PREFIX = 'http_status_' 16 | local HTTP_TIME_KEY_PREFIX = 'http_time_' 17 | 18 | local _M = { _VERSION = '0.1' } 19 | 20 | local mt = { __index = _M } 21 | 22 | function _M.get_status(group_name) 23 | local self = _M.self 24 | local dict = self._dict 25 | 26 | local res = {} 27 | local count = 0 28 | 29 | for group, _ in pairs(self._groups) do 30 | local key = HTTP_TIME_KEY_PREFIX .. group 31 | local cur = dict:get(key) or 0 32 | count = count + 1 33 | res[count] = 'upstream_http_time{' .. group_name .. '="' .. group .. '"} ' .. cur .. '\n' 34 | 35 | for status, _ in pairs(self._statuses) do 36 | local key = HTTP_STATUS_KEY_PREFIX .. status .. '_' .. group 37 | local cur = dict:get(key) or 0 38 | 39 | count = count + 1 40 | res[count] = 'upstream_http_status{status="' .. status .. '",' .. group_name .. '="' .. group .. '"} ' .. cur .. '\n' 41 | end 42 | end 43 | 44 | return concat(res) 45 | end 46 | 47 | function _M.log(group) 48 | local self = _M.self 49 | local dict = self._dict 50 | local status = ngx_var.status 51 | 52 | if not group then 53 | group = '' 54 | end 55 | 56 | local key = HTTP_STATUS_KEY_PREFIX .. status .. '_' .. group 57 | local res, err = dict:incr(key, 1, 0) 58 | if not res then 59 | log(ERR, 'dict:incr failed: ', err, ', key: ', key) 60 | end 61 | 62 | local request_time = ngx_now() - start_time() 63 | local key = HTTP_TIME_KEY_PREFIX .. group 64 | local res, err = dict:incr(key, request_time, 0) 65 | if not res then 66 | log(ERR, 'dict:incr failed: ', err, ', key: ', key) 67 | end 68 | 69 | self._statuses[status] = 1 70 | self._groups[group] = 1 71 | end 72 | 73 | function _M.new(_, options) 74 | if _M.self then 75 | return _M.self 76 | end 77 | 78 | local dict = options.dict 79 | if not dict then 80 | log(ERR, 'missing required params for metrics') 81 | return 82 | end 83 | 84 | local self = setmetatable({ 85 | _dict = dict, 86 | _statuses = {}, 87 | _groups = {}, 88 | }, mt) 89 | 90 | _M.self = self -- singleton 91 | return self 92 | end 93 | 94 | return _M 95 | -------------------------------------------------------------------------------- /vod/hls/id3_encoder_filter.c: -------------------------------------------------------------------------------- 1 | #include "id3_encoder_filter.h" 2 | 3 | // macros 4 | #define THIS_FILTER (MEDIA_FILTER_ID3) 5 | #define get_context(ctx) ((id3_encoder_state_t*)ctx->context[THIS_FILTER]) 6 | 7 | // macros 8 | #define write_be32_synchsafe(p, dw) \ 9 | { \ 10 | *(p)++ = ((dw) >> 21) & 0x7F; \ 11 | *(p)++ = ((dw) >> 14) & 0x7F; \ 12 | *(p)++ = ((dw) >> 7) & 0x7F; \ 13 | *(p)++ = (dw) & 0x7F; \ 14 | } 15 | 16 | // constants 17 | static u_char header_template[] = { 18 | // id3 header 19 | 0x49, 0x44, 0x33, 0x04, // file identifier 20 | 0x00, // version 21 | 0x00, // flags 22 | 0x00, 0x00, 0x00, 0x00, // size 23 | 24 | // frame header 25 | 0x54, 0x45, 0x58, 0x54, // frame id 26 | 0x00, 0x00, 0x00, 0x00, // size 27 | 0x00, 0x00, // flags 28 | 29 | // text frame 30 | 0x03, // encoding (=utf8, null term) 31 | }; 32 | 33 | static vod_status_t 34 | id3_encoder_start_frame(media_filter_context_t* context, output_frame_t* frame) 35 | { 36 | id3_encoder_state_t* state = get_context(context); 37 | vod_status_t rc; 38 | uint32_t size = frame->size; 39 | u_char* p; 40 | 41 | // start the frame 42 | frame->size = size + sizeof(state->header); 43 | 44 | rc = state->start_frame(context, frame); 45 | if (rc != VOD_OK) 46 | { 47 | return rc; 48 | } 49 | 50 | // write the header 51 | p = state->header.frame_header.size; 52 | size += sizeof(id3_text_frame_header_t); 53 | write_be32_synchsafe(p, size); 54 | 55 | p = state->header.file_header.size; 56 | size += sizeof(id3_frame_header_t); 57 | write_be32_synchsafe(p, size); 58 | 59 | return state->write(context, (u_char*)&state->header, sizeof(state->header)); 60 | } 61 | 62 | 63 | static void 64 | id3_encoder_simulated_start_frame(media_filter_context_t* context, output_frame_t* frame) 65 | { 66 | id3_encoder_state_t* state = get_context(context); 67 | 68 | state->simulated_start_frame(context, frame); 69 | state->simulated_write(context, sizeof(state->header)); 70 | } 71 | 72 | 73 | void 74 | id3_encoder_init( 75 | id3_encoder_state_t* state, 76 | media_filter_t* filter, 77 | media_filter_context_t* context) 78 | { 79 | vod_memcpy(&state->header, header_template, sizeof(state->header)); 80 | 81 | // save required functions 82 | state->start_frame = filter->start_frame; 83 | state->write = filter->write; 84 | state->simulated_start_frame = filter->simulated_start_frame; 85 | state->simulated_write = filter->simulated_write; 86 | 87 | // override functions 88 | filter->start_frame = id3_encoder_start_frame; 89 | filter->simulated_start_frame = id3_encoder_simulated_start_frame; 90 | 91 | // save the context 92 | context->context[THIS_FILTER] = state; 93 | } 94 | -------------------------------------------------------------------------------- /vod/mp4/mp4_fragment.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_FRAGMENT_H__ 2 | #define __MP4_FRAGMENT_H__ 3 | 4 | // includes 5 | #include "mp4_write_stream.h" 6 | #include "mp4_defs.h" 7 | #include "../media_set.h" 8 | 9 | // constants 10 | #define TRUN_VIDEO_FLAGS (0xF01) // = data offset, duration, size, key, delay 11 | #define TRUN_AUDIO_FLAGS (0x301) // = data offset, duration, size 12 | 13 | // typedefs 14 | typedef struct { 15 | u_char version[1]; 16 | u_char flags[3]; 17 | u_char sample_count[4]; 18 | u_char data_offset[4]; 19 | } trun_atom_t; 20 | 21 | typedef struct { 22 | u_char duration[4]; 23 | u_char size[4]; 24 | u_char flags[4]; 25 | u_char pts_delay[4]; 26 | } trun_video_frame_t; 27 | 28 | typedef struct { 29 | u_char duration[4]; 30 | u_char size[4]; 31 | } trun_audio_frame_t; 32 | 33 | typedef struct { 34 | u_char version[1]; 35 | u_char flags[3]; 36 | u_char sequence_number[4]; 37 | } mfhd_atom_t; 38 | 39 | typedef struct { 40 | request_context_t* request_context; 41 | write_callback_t write_callback; 42 | void* write_context; 43 | bool_t reuse_buffers; 44 | 45 | media_sequence_t* sequence; 46 | media_clip_filtered_t* cur_clip; 47 | frame_list_part_t* first_frame_part; 48 | frame_list_part_t cur_frame_part; 49 | input_frame_t* cur_frame; 50 | bool_t first_time; 51 | bool_t frame_started; 52 | } fragment_writer_state_t; 53 | 54 | // functions 55 | u_char* mp4_fragment_write_mfhd_atom(u_char* p, uint32_t segment_index); 56 | 57 | u_char* mp4_fragment_write_tfhd_atom(u_char* p, uint32_t track_id, uint32_t sample_description_index); 58 | 59 | u_char* mp4_fragment_write_tfdt_atom(u_char* p, uint32_t earliest_pres_time); 60 | 61 | u_char* mp4_fragment_write_tfdt64_atom(u_char* p, uint64_t earliest_pres_time); 62 | 63 | size_t mp4_fragment_get_trun_atom_size(uint32_t media_type, uint32_t frame_count); 64 | 65 | u_char* mp4_fragment_write_video_trun_atom( 66 | u_char* p, 67 | media_sequence_t* sequence, 68 | uint32_t first_frame_offset, 69 | uint32_t version); 70 | 71 | u_char* mp4_fragment_write_audio_trun_atom( 72 | u_char* p, 73 | media_sequence_t* sequence, 74 | uint32_t first_frame_offset); 75 | 76 | u_char* mp4_fragment_write_subtitle_trun_atom( 77 | u_char* p, 78 | uint32_t first_frame_offset, 79 | uint32_t duration, 80 | u_char** sample_size); 81 | 82 | 83 | vod_status_t mp4_fragment_frame_writer_init( 84 | request_context_t* request_context, 85 | media_sequence_t* sequence, 86 | write_callback_t write_callback, 87 | void* write_context, 88 | bool_t reuse_buffers, 89 | fragment_writer_state_t** result); 90 | 91 | vod_status_t mp4_fragment_frame_writer_process(fragment_writer_state_t* state); 92 | 93 | void mp4_fragment_get_content_type( 94 | bool_t video, 95 | vod_str_t* content_type); 96 | 97 | #endif // __MP4_FRAGMENT_H__ 98 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ## nginx-vod-module tests 2 | 3 | the accompanied python scripts test the nginx-vod-module, each test script has an associated template file 4 | that contains environment specific parameters. the template file should be copied and edited to match the 5 | specific environment on which the tests are run. 6 | the tests assume that nginx is running with the accompanied nginx.conf file. 7 | 8 | ### memory pollution 9 | 10 | for additional coverage it is recommended to patch ngx_alloc & ngx_memalign (in src/os/unix/ngx_alloc.c) 11 | so that they memset the allocated pointer with some value. the purpose of this change is to ensure the 12 | code does not make any assumptions on allocated pointers being zeroed. ngx_calloc must not be patched. 13 | to apply the patch - edit ngx_alloc and the two implementations of ngx_memalign, and add the following 14 | block after the if: 15 | else { 16 | memset(p, 0xBD, size); 17 | } 18 | 19 | ### main.py 20 | 21 | sanity + coverage tests, e.g.: 22 | 1. bad request parameters 23 | 2. bad upstream responses 24 | 3. file not found 25 | 26 | in order to run the tests nginx should be compiled with the --with-debug switch 27 | 28 | ### hls_compare.py 29 | 30 | compares the nginx-vod hls implementation to some reference implementation 31 | 32 | ### clip_compare.py 33 | 34 | compares the nginx-vod mp4 clipping implementation to nginx-mp4-module 35 | 36 | ### validate_iframes.py 37 | 38 | verifies using ffprobe that the byte ranges returned in an EXT-X-I-FRAMES-ONLY m3u8 indeed represent iframes 39 | 40 | ### setup_test_entries.py / verify_test_entries.py 41 | 42 | setup_test_entries.py - crafts different types of corrupted mp4 files and uploads them to a Kaltura account 43 | verify_test_entries.py - feeds the entries created by setup_test_entries.py to nginx-vod-module and validates the result. 44 | gets the output of setup_test_entries.py as input (contains the test uris and their expected results). 45 | 46 | ### test_coverage.py 47 | 48 | prints a list of nginx-vod-module log lines that do not appear in the provided log file. 49 | can be executed after running main.py and verify_test_entries.py to get a sense of missing test cases. 50 | 51 | ### buffer_cache 52 | 53 | this folder contains a stress test for the buffer cache module. in order to execute the test, run: 54 | * NGX_ROOT=/path/to/nginx/sources VOD_ROOT=/path/to/nginx/vod bash build.sh 55 | * ./bctest 56 | 57 | ### json_parser 58 | 59 | this folder contains tests for the json parser module. in order to execute the test, run: 60 | * NGX_ROOT=/path/to/nginx/sources VOD_ROOT=/path/to/nginx/vod bash build.sh 61 | * ./jsontest 62 | 63 | ### bitset 64 | 65 | this folder contains tests for the light bitset implementation. in order to execute the test, run: 66 | * NGX_ROOT=/path/to/nginx/sources VOD_ROOT=/path/to/nginx/vod bash build.sh 67 | * ./bitsettest 68 | -------------------------------------------------------------------------------- /vod/buffer_pool.c: -------------------------------------------------------------------------------- 1 | #include "buffer_pool.h" 2 | 3 | // macros 4 | #define next_buffer(buf) (*(void**)buf) 5 | 6 | // typedefs 7 | struct buffer_pool_s { 8 | size_t size; 9 | void* head; 10 | }; 11 | 12 | typedef struct { 13 | buffer_pool_t* buffer_pool; 14 | void* buffer; 15 | } buffer_pool_cleanup_t; 16 | 17 | buffer_pool_t* 18 | buffer_pool_create(vod_pool_t* pool, vod_log_t* log, size_t buffer_size, size_t count) 19 | { 20 | buffer_pool_t* buffer_pool; 21 | u_char* cur_buffer; 22 | void* head; 23 | 24 | if ((buffer_size & 0x0F) != 0) 25 | { 26 | vod_log_error(VOD_LOG_ERR, log, 0, 27 | "buffer_pool_create: invalid size %uz must be a multiple of 16", buffer_size); 28 | return NULL; 29 | } 30 | 31 | buffer_pool = vod_alloc(pool, sizeof(*buffer_pool)); 32 | if (buffer_pool == NULL) 33 | { 34 | vod_log_debug0(VOD_LOG_DEBUG_LEVEL, log, 0, 35 | "buffer_pool_create: vod_alloc failed (1)"); 36 | return NULL; 37 | } 38 | 39 | cur_buffer = vod_alloc(pool, buffer_size * count); 40 | if (cur_buffer == NULL) 41 | { 42 | vod_log_debug0(VOD_LOG_DEBUG_LEVEL, log, 0, 43 | "buffer_pool_create: vod_alloc failed (2)"); 44 | return NULL; 45 | } 46 | 47 | head = NULL; 48 | for (; count > 0; count--, cur_buffer += buffer_size) 49 | { 50 | next_buffer(cur_buffer) = head; 51 | head = cur_buffer; 52 | } 53 | 54 | buffer_pool->size = buffer_size; 55 | buffer_pool->head = head; 56 | 57 | return buffer_pool; 58 | } 59 | 60 | static void 61 | buffer_pool_buffer_cleanup(void *data) 62 | { 63 | buffer_pool_cleanup_t* c = data; 64 | buffer_pool_t* buffer_pool = c->buffer_pool; 65 | void* buffer = c->buffer; 66 | 67 | next_buffer(buffer) = buffer_pool->head; 68 | buffer_pool->head = buffer; 69 | } 70 | 71 | void* 72 | buffer_pool_alloc(request_context_t* request_context, buffer_pool_t* buffer_pool, size_t* buffer_size) 73 | { 74 | buffer_pool_cleanup_t* buf_cln; 75 | vod_pool_cleanup_t* cln; 76 | void* result; 77 | 78 | if (buffer_pool == NULL) 79 | { 80 | return vod_alloc(request_context->pool, *buffer_size); 81 | } 82 | 83 | if (buffer_pool->head == NULL) 84 | { 85 | *buffer_size = buffer_pool->size; 86 | return vod_alloc(request_context->pool, *buffer_size); 87 | } 88 | 89 | cln = vod_pool_cleanup_add(request_context->pool, sizeof(buffer_pool_cleanup_t)); 90 | if (cln == NULL) 91 | { 92 | vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, 93 | "buffer_pool_alloc: vod_pool_cleanup_add failed"); 94 | return NULL; 95 | } 96 | 97 | result = buffer_pool->head; 98 | buffer_pool->head = next_buffer(result); 99 | 100 | cln->handler = buffer_pool_buffer_cleanup; 101 | 102 | buf_cln = cln->data; 103 | buf_cln->buffer = result; 104 | buf_cln->buffer_pool = buffer_pool; 105 | 106 | *buffer_size = buffer_pool->size; 107 | 108 | return result; 109 | } 110 | -------------------------------------------------------------------------------- /vod/hls/mpegts_encoder_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __MPEGTS_ENCODER_FILTER_H__ 2 | #define __MPEGTS_ENCODER_FILTER_H__ 3 | 4 | // includes 5 | #include "hls_encryption.h" 6 | #include "../media_format.h" 7 | #include "../write_buffer_queue.h" 8 | #include "media_filter.h" 9 | 10 | // constants 11 | #define MPEGTS_PACKET_SIZE (188) 12 | #define HLS_DELAY (63000) // 700 ms PCR delay 13 | #define INITIAL_DTS (9090) 14 | #define INITIAL_PCR (4590) 15 | 16 | // typedefs 17 | typedef struct { 18 | int media_type; 19 | unsigned pid; 20 | unsigned sid; 21 | } mpegts_stream_info_t; 22 | 23 | typedef struct { 24 | request_context_t* request_context; 25 | 26 | // stream info 27 | mpegts_stream_info_t stream_info; 28 | 29 | // options 30 | bool_t interleave_frames; 31 | bool_t align_frames; 32 | 33 | // buffer queue 34 | write_buffer_queue_t* queue; 35 | off_t send_queue_offset; 36 | off_t last_queue_offset; 37 | 38 | // packet state 39 | u_char* cur_packet_start; 40 | u_char* cur_packet_end; 41 | u_char* cur_pos; 42 | u_char* temp_packet; 43 | 44 | // frame state 45 | unsigned cc; 46 | unsigned initial_cc; 47 | u_char* cur_pes_size_ptr; 48 | uint32_t pes_bytes_written; 49 | uint32_t flushed_frame_bytes; 50 | uint32_t packet_bytes_left; 51 | uint32_t header_size; 52 | 53 | uint64_t last_frame_pts; 54 | 55 | // simulation only 56 | uint32_t temp_packet_size; 57 | off_t cur_frame_start_pos; 58 | off_t cur_frame_end_pos; 59 | off_t last_frame_start_pos; 60 | off_t last_frame_end_pos; 61 | } mpegts_encoder_state_t; 62 | 63 | typedef struct { 64 | request_context_t* request_context; 65 | hls_encryption_params_t* encryption_params; 66 | uint32_t segment_index; 67 | 68 | u_char* pat_packet_start; 69 | u_char* pmt_packet_start; 70 | u_char* pmt_packet_end; 71 | u_char* pmt_packet_pos; 72 | int cur_pid; 73 | unsigned cur_video_sid; 74 | unsigned cur_audio_sid; 75 | } mpegts_encoder_init_streams_state_t; 76 | 77 | // functions 78 | vod_status_t mpegts_encoder_init_streams( 79 | request_context_t* request_context, 80 | hls_encryption_params_t* encryption_params, 81 | mpegts_encoder_init_streams_state_t* stream_state, 82 | uint32_t segment_index); 83 | 84 | void mpegts_encoder_finalize_streams( 85 | mpegts_encoder_init_streams_state_t* stream_state, 86 | vod_str_t* ts_header); 87 | 88 | vod_status_t mpegts_encoder_init( 89 | media_filter_t* filter, 90 | mpegts_encoder_state_t* state, 91 | mpegts_encoder_init_streams_state_t* stream_state, 92 | media_track_t* track, 93 | write_buffer_queue_t* queue, 94 | bool_t interleave_frames, 95 | bool_t align_frames); 96 | 97 | vod_status_t mpegts_encoder_start_sub_frame( 98 | media_filter_context_t* context, 99 | output_frame_t* frame); 100 | 101 | void mpegts_encoder_simulated_start_segment(write_buffer_queue_t* queue); 102 | 103 | #endif // __MPEGTS_ENCODER_FILTER_H__ 104 | -------------------------------------------------------------------------------- /vod/input/frames_source_cache.c: -------------------------------------------------------------------------------- 1 | #include "frames_source_cache.h" 2 | #include "../media_format.h" 3 | 4 | vod_status_t 5 | frames_source_cache_init( 6 | request_context_t* request_context, 7 | read_cache_state_t* read_cache_state, 8 | void* source, 9 | int cache_slot_id, 10 | void** result) 11 | { 12 | frames_source_cache_state_t* state; 13 | 14 | state = vod_alloc(request_context->pool, sizeof(*state)); 15 | if (state == NULL) 16 | { 17 | vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, 18 | "frames_source_cache_init: vod_alloc failed"); 19 | return VOD_ALLOC_FAILED; 20 | } 21 | 22 | state->read_cache_state = read_cache_state; 23 | state->req.source = source; 24 | state->req.cache_slot_id = cache_slot_id; 25 | 26 | *result = state; 27 | 28 | return VOD_OK; 29 | } 30 | 31 | static void 32 | frames_source_cache_set_cache_slot_id(void* ctx, int cache_slot_id) 33 | { 34 | frames_source_cache_state_t* state = ctx; 35 | 36 | state->req.cache_slot_id = cache_slot_id; 37 | } 38 | 39 | static vod_status_t 40 | frames_source_cache_start_frame(void* ctx, input_frame_t* frame, read_cache_hint_t* cache_hint) 41 | { 42 | frames_source_cache_state_t* state = ctx; 43 | 44 | state->req.cur_offset = frame->offset; 45 | state->req.end_offset = frame->offset + frame->size; 46 | if (cache_hint != NULL) 47 | { 48 | state->req.hint = *cache_hint; 49 | } 50 | else 51 | { 52 | state->req.hint.min_offset = ULLONG_MAX; 53 | } 54 | 55 | return VOD_OK; 56 | } 57 | 58 | static vod_status_t 59 | frames_source_cache_read(void* ctx, u_char** buffer, uint32_t* size, bool_t* frame_done) 60 | { 61 | frames_source_cache_state_t* state = ctx; 62 | uint64_t cur_end_offset; 63 | 64 | if (!read_cache_get_from_cache( 65 | state->read_cache_state, 66 | &state->req, 67 | buffer, 68 | size)) 69 | { 70 | return VOD_AGAIN; 71 | } 72 | 73 | cur_end_offset = state->req.cur_offset + *size; 74 | if (cur_end_offset >= state->req.end_offset) 75 | { 76 | *size = state->req.end_offset - state->req.cur_offset; 77 | *frame_done = TRUE; 78 | state->req.cur_offset = state->req.end_offset; 79 | } 80 | else 81 | { 82 | *frame_done = FALSE; 83 | state->req.cur_offset = cur_end_offset; 84 | } 85 | return VOD_OK; 86 | } 87 | 88 | static void 89 | frames_source_cache_disable_buffer_reuse(void* ctx) 90 | { 91 | frames_source_cache_state_t* state = ctx; 92 | 93 | read_cache_disable_buffer_reuse(state->read_cache_state); 94 | } 95 | 96 | static vod_status_t 97 | frames_source_cache_skip_frames(void* ctx, uint32_t skip_count) 98 | { 99 | return VOD_OK; 100 | } 101 | 102 | // globals 103 | frames_source_t frames_source_cache = { 104 | frames_source_cache_set_cache_slot_id, 105 | frames_source_cache_start_frame, 106 | frames_source_cache_read, 107 | frames_source_cache_disable_buffer_reuse, 108 | frames_source_cache_skip_frames, 109 | }; 110 | -------------------------------------------------------------------------------- /vod/codec_config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CODEC_CONFIG_H__ 2 | #define __CODEC_CONFIG_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | // typedefs 8 | struct media_info_s; 9 | 10 | typedef vod_status_t (*codec_config_get_nal_units_t)( 11 | request_context_t* request_context, 12 | vod_str_t* extra_data, 13 | bool_t size_only, 14 | uint32_t* nal_packet_size_length, 15 | vod_str_t* result); 16 | 17 | typedef struct 18 | { 19 | u_char version; 20 | u_char profile; 21 | u_char compatibility; 22 | u_char level; 23 | u_char nula_length_size; 24 | } avcc_config_t; 25 | 26 | typedef struct 27 | { 28 | uint8_t configurationVersion; 29 | uint8_t profile_space; 30 | uint8_t tier_flag; 31 | uint8_t profile_idc; 32 | uint32_t general_profile_compatibility_flags; 33 | uint8_t progressive_source_flag; 34 | uint8_t interlaced_source_flag; 35 | uint8_t non_packed_constraint_flag; 36 | uint8_t frame_only_constraint_flag; 37 | /*only lowest 44 bits used*/ 38 | uint64_t constraint_indicator_flags; 39 | uint8_t level_idc; 40 | uint16_t min_spatial_segmentation_idc; 41 | 42 | uint8_t parallelismType; 43 | uint8_t chromaFormat; 44 | uint8_t luma_bit_depth; 45 | uint8_t chroma_bit_depth; 46 | uint16_t avgFrameRate; 47 | uint8_t constantFrameRate; 48 | uint8_t numTemporalLayers; 49 | uint8_t temporalIdNested; 50 | 51 | uint8_t nal_unit_size; 52 | 53 | //set by libisomedia at import/export time 54 | bool_t is_shvc; 55 | 56 | //used in SHVC config 57 | bool_t complete_representation; 58 | bool_t non_hevc_base_layer; 59 | uint8_t num_layers; 60 | uint16_t scalability_mask; 61 | } hevc_config_t; 62 | 63 | typedef struct { 64 | uint8_t object_type; 65 | uint8_t sample_rate_index; 66 | uint8_t channel_config; 67 | } mp4a_config_t; 68 | 69 | // functions 70 | vod_status_t codec_config_hevc_get_nal_units( 71 | request_context_t* request_context, 72 | vod_str_t* extra_data, 73 | bool_t size_only, 74 | uint32_t* nal_packet_size_length, 75 | vod_str_t* result); 76 | 77 | vod_status_t codec_config_avcc_get_nal_units( 78 | request_context_t* request_context, 79 | vod_str_t* extra_data, 80 | bool_t size_only, 81 | uint32_t* nal_packet_size_length, 82 | vod_str_t* result); 83 | 84 | // get codec name according to http://tools.ietf.org/html/rfc6381 85 | vod_status_t codec_config_get_video_codec_name(request_context_t* request_context, struct media_info_s* media_info); 86 | vod_status_t codec_config_get_audio_codec_name(request_context_t* request_context, struct media_info_s* media_info); 87 | 88 | vod_status_t codec_config_mp4a_config_parse( 89 | request_context_t* request_context, 90 | vod_str_t* extra_data, 91 | struct media_info_s* media_info); 92 | 93 | vod_status_t codec_config_hevc_config_parse( 94 | request_context_t* request_context, 95 | vod_str_t* extra_data, 96 | hevc_config_t* cfg, 97 | const u_char** end_pos); 98 | 99 | #endif // __CODEC_CONFIG_H__ 100 | -------------------------------------------------------------------------------- /ngx_http_vod_request_parse.h: -------------------------------------------------------------------------------- 1 | #ifndef _NGX_HTTP_VOD_REQUEST_PARSE_H_INCLUDED_ 2 | #define _NGX_HTTP_VOD_REQUEST_PARSE_H_INCLUDED_ 3 | 4 | // includes 5 | #include 6 | #include "ngx_buffer_cache.h" 7 | #include "vod/mp4/mp4_parser.h" 8 | #include "vod/media_set.h" 9 | 10 | // constants 11 | #define MAX_SUB_URIS (32) 12 | #define MAX_URI_PARAM_NAME_LEN (32) // clipTo, clipFrom etc. 13 | 14 | #define PARSE_FILE_NAME_EXPECT_SEGMENT_INDEX (0x1) 15 | #define PARSE_FILE_NAME_MULTI_STREAMS_PER_TYPE (0x2) 16 | #define PARSE_FILE_NAME_ALLOW_CLIP_INDEX (0x4) 17 | 18 | // macros 19 | #define ngx_http_vod_starts_with(start_pos, end_pos, prefix) \ 20 | ((end_pos) - (start_pos) >= (int)(prefix)->len && ngx_memcmp((start_pos), (prefix)->data, (prefix)->len) == 0) 21 | 22 | #define ngx_http_vod_ends_with(start_pos, end_pos, postfix) \ 23 | ((end_pos) - (start_pos) >= (int)(postfix)->len && ngx_memcmp((end_pos) - (postfix)->len, (postfix)->data, (postfix)->len) == 0) 24 | 25 | #define ngx_http_vod_ends_with_static(start_pos, end_pos, postfix) \ 26 | ((end_pos) - (start_pos) >= (int)sizeof(postfix) - 1 && ngx_memcmp((end_pos) - (sizeof(postfix) - 1), (postfix), sizeof(postfix) - 1) == 0) 27 | 28 | #define ngx_http_vod_match_prefix_postfix(start_pos, end_pos, prefix, postfix) \ 29 | ((end_pos) - (start_pos) >= (int)(prefix)->len + (int)sizeof(postfix) - 1 && \ 30 | ngx_memcmp((end_pos) - (sizeof(postfix) - 1), (postfix), sizeof(postfix) - 1) == 0 && \ 31 | ngx_memcmp((start_pos), (prefix)->data, (prefix)->len) == 0) 32 | 33 | // typedefs 34 | struct ngx_http_vod_request_s; 35 | struct ngx_http_vod_loc_conf_s; 36 | 37 | enum { 38 | MATCH_END, 39 | MATCH_FIXED_STRING, 40 | MATCH_DELIM_STRING, 41 | MATCH_NUMBER, 42 | }; 43 | 44 | typedef struct { 45 | int match_type; 46 | int target_offset; 47 | int delim; 48 | ngx_str_t string; 49 | } ngx_http_vod_match_definition_t; 50 | 51 | // functions 52 | bool_t ngx_http_vod_split_uri_file_name( 53 | ngx_str_t* uri, 54 | int components, 55 | ngx_str_t* path, 56 | ngx_str_t* file_name); 57 | 58 | ngx_int_t ngx_http_vod_parse_uri_path( 59 | ngx_http_request_t* r, 60 | ngx_str_t* multi_uri_suffix, 61 | ngx_hash_t* params_hash, 62 | ngx_str_t* uri, 63 | request_params_t* request_params, 64 | media_set_t* media_set); 65 | 66 | ngx_int_t ngx_http_vod_init_uri_params_hash( 67 | ngx_conf_t *cf, 68 | struct ngx_http_vod_loc_conf_s* conf); 69 | 70 | // utility functions for submodules 71 | u_char* ngx_http_vod_extract_uint32_token_reverse( 72 | u_char* start_pos, 73 | u_char* end_pos, 74 | uint32_t* result); 75 | 76 | bool_t ngx_http_vod_parse_string( 77 | const ngx_http_vod_match_definition_t* match_def, 78 | u_char* start_pos, 79 | u_char* end_pos, 80 | void* output); 81 | 82 | ngx_int_t ngx_http_vod_parse_uri_file_name( 83 | ngx_http_request_t* r, 84 | u_char* start_pos, 85 | u_char* end_pos, 86 | uint32_t flags, 87 | request_params_t* result); 88 | 89 | #endif // _NGX_HTTP_VOD_REQUEST_PARSE_H_INCLUDED_ 90 | -------------------------------------------------------------------------------- /test/dash_clear_key.php: -------------------------------------------------------------------------------- 1 | type; 78 | $keys = array(); 79 | foreach ($req->kids as $kid) 80 | { 81 | $key = b64url_enc(getKey(b64url_dec($kid))); 82 | $keys[] = array('k' => $key, 'kty' => 'oct', 'kid' => $kid); 83 | } 84 | 85 | $res = array('keys' => $keys, 'type' => $type); 86 | header('access-control-allow-origin: *'); 87 | } 88 | else 89 | { 90 | // GET - assume encryption request 91 | $kid = hex2bin('0123456789abcdef0123456789abcdef'); // TODO: allocate according to media id 92 | $res = array(array( 93 | 'key' => base64_encode(getKey($kid)), 94 | 'key_id' => base64_encode($kid), 95 | 'pssh' => array(array( 96 | 'uuid' => '1077efec-c0b2-4d02-ace3-3c1e52e2fb4b', 97 | 'data' => base64_encode(hex2bin('00000001') . $kid . hex2bin('00000000')) 98 | )) 99 | )); 100 | } 101 | 102 | echo str_replace('\\/', '/', json_encode($res)); 103 | -------------------------------------------------------------------------------- /vod/mkv/mkv_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef __MKV_DEFS_H__ 2 | #define __MKV_DEFS_H__ 3 | 4 | #include "../common.h" 5 | 6 | // constants 7 | #define NANOS_PER_SEC (1000000000) 8 | 9 | // Matroska elements 10 | 11 | // seek 12 | #define MKV_ID_SEEKID (0x53AB) 13 | #define MKV_ID_SEEKPOSITION (0x53AC) 14 | #define MKV_ID_SEEKENTRY (0x4DBB) 15 | #define MKV_ID_SEEKHEAD (0x114D9B74) 16 | 17 | // info 18 | #define MKV_ID_TIMECODESCALE (0x2AD7B1) 19 | #define MKV_ID_DURATION (0x4489) 20 | #define MKV_ID_WRITINGAPP (0x5741) 21 | #define MKV_ID_MUXINGAPP (0x4D80) 22 | 23 | // track 24 | #define MKV_ID_VIDEOPIXELWIDTH (0xB0) 25 | #define MKV_ID_VIDEOPIXELHEIGHT (0xBA) 26 | 27 | #define MKV_ID_AUDIOSAMPLINGFREQ (0xB5) 28 | #define MKV_ID_AUDIOBITDEPTH (0x6264) 29 | #define MKV_ID_AUDIOCHANNELS (0x9F) 30 | 31 | #define MKV_ID_TRACKNUMBER (0xD7) 32 | #define MKV_ID_TRACKUID (0x73C5) 33 | #define MKV_ID_TRACKTYPE (0x83) 34 | #define MKV_ID_TRACKVIDEO (0xE0) 35 | #define MKV_ID_TRACKAUDIO (0xE1) 36 | #define MKV_ID_TRACKCODECID (0x86) 37 | #define MKV_ID_TRACKCODECPRIVATE (0x63A2) 38 | #define MKV_ID_TRACKDEFAULTDURATION (0x23E383) 39 | #define MKV_ID_TRACKENTRY (0xAE) 40 | #define MKV_ID_TRACKCODECDELAY (0x56AA) 41 | #define MKV_ID_TRACKLANGUAGE (0x22B59C) 42 | #define MKV_ID_TRACKNAME (0x536E) 43 | 44 | // content encodings 45 | #define MKV_ID_CONTENTENCODINGS (0x6D80) 46 | #define MKV_ID_CONTENTENCODING (0x6240) 47 | #define MKV_ID_CONTENTENCODINGORDER (0x5031) 48 | #define MKV_ID_CONTENTENCODINGSCOPE (0x5032) 49 | #define MKV_ID_CONTENTENCODINGTYPE (0x5033) 50 | #define MKV_ID_CONTENTENCRYPTION (0x5035) 51 | #define MKV_ID_CONTENTENCALGO (0x47E1) 52 | #define MKV_ID_CONTENTENCKEYID (0x47E2) 53 | #define MKV_ID_CONTENTENCAESSETTINGS (0x47E7) 54 | #define MKV_ID_AESSETTINGSCIPHERMODE (0x47E8) 55 | 56 | // index 57 | #define MKV_ID_CUETIME (0xB3) 58 | #define MKV_ID_CUETRACKPOSITION (0xB7) 59 | #define MKV_ID_CUETRACK (0xF7) 60 | #define MKV_ID_CUECLUSTERPOSITION (0xF1) 61 | #define MKV_ID_CUERELATIVEPOSITION (0xF0) 62 | #define MKV_ID_POINTENTRY (0xBB) 63 | 64 | // cluster 65 | #define MKV_ID_CLUSTERTIMECODE (0xE7) 66 | #define MKV_ID_SIMPLEBLOCK (0xA3) 67 | #define MKV_ID_BLOCKGROUP (0xA0) 68 | #define MKV_ID_BLOCK (0xA1) 69 | #define MKV_ID_REFERENCEBLOCK (0xFB) 70 | #define MKV_ID_CLUSTER (0x1F43B675) 71 | 72 | // sections 73 | #define MKV_ID_SEGMENT (0x18538067) 74 | #define MKV_ID_INFO (0x1549A966) 75 | #define MKV_ID_TRACKS (0x1654AE6B) 76 | #define MKV_ID_CUES (0x1C53BB6B) 77 | 78 | #define MKV_MAX_CODEC_SIZE (50) // must be greater than all mkv_codec_types values 79 | 80 | // enums 81 | enum { 82 | MKV_TRACK_TYPE_VIDEO = 0x1, 83 | MKV_TRACK_TYPE_AUDIO = 0x2, 84 | }; 85 | 86 | // typedefs 87 | typedef struct { 88 | vod_str_t mkv_codec_id; 89 | uint32_t codec_id; 90 | uint32_t format; 91 | bool_t extra_data_required; 92 | } mkv_codec_type_t; 93 | 94 | // constants 95 | extern mkv_codec_type_t mkv_codec_types[]; 96 | 97 | #endif // __MKV_DEFS_H__ 98 | -------------------------------------------------------------------------------- /vod/media_clip.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEDIA_CLIP_H__ 2 | #define __MEDIA_CLIP_H__ 3 | 4 | // includes 5 | #include "media_format.h" 6 | 7 | // macros 8 | #define media_clip_is_source(type) ((type) < MEDIA_CLIP_SOURCE_LIMIT) 9 | 10 | // constants 11 | #define MEDIA_CLIP_KEY_SIZE (16) 12 | 13 | // typedefs 14 | struct segmenter_conf_s; 15 | struct audio_filter_s; 16 | struct media_sequence_s; 17 | 18 | typedef enum { 19 | // sources / generators 20 | MEDIA_CLIP_SOURCE, 21 | MEDIA_CLIP_SILENCE_GENERATOR, 22 | MEDIA_CLIP_SOURCE_LIMIT, 23 | 24 | // filters 25 | MEDIA_CLIP_RATE_FILTER, 26 | MEDIA_CLIP_MIX_FILTER, 27 | MEDIA_CLIP_GAIN_FILTER, 28 | MEDIA_CLIP_CONCAT, 29 | MEDIA_CLIP_DYNAMIC, 30 | } media_clip_type_t; 31 | 32 | typedef enum { 33 | MEDIA_CLIP_SOURCE_DEFAULT, 34 | MEDIA_CLIP_SOURCE_FILE, 35 | MEDIA_CLIP_SOURCE_HTTP, 36 | } media_clip_source_type_t; 37 | 38 | typedef struct media_clip_s { 39 | media_clip_type_t type; 40 | uint32_t id; 41 | struct media_clip_s* parent; 42 | 43 | // TODO: the fields below are not required for sources, consider adding another struct 44 | struct audio_filter_s* audio_filter; 45 | struct media_clip_s** sources; 46 | uint32_t source_count; 47 | } media_clip_t; 48 | 49 | 50 | typedef enum { 51 | MCS_ENC_CENC, 52 | MCS_ENC_AES_CBC, 53 | } media_clip_source_enc_scheme_t; 54 | 55 | typedef struct { 56 | media_clip_source_enc_scheme_t scheme; 57 | ngx_str_t key; 58 | ngx_str_t iv; 59 | } media_clip_source_enc_t; 60 | 61 | struct media_clip_source_s; 62 | typedef struct media_clip_source_s media_clip_source_t; 63 | typedef struct ngx_http_vod_reader_s ngx_http_vod_reader_t; 64 | 65 | struct media_clip_source_s { 66 | // base 67 | media_clip_t base; 68 | int64_t clip_time; 69 | media_range_t* range; 70 | media_track_array_t track_array; 71 | struct media_sequence_s* sequence; 72 | uint64_t clip_to; 73 | 74 | // TODO: the fields below are not required for generators, consider adding another struct 75 | 76 | // input params 77 | vod_str_t id; 78 | media_clip_source_type_t source_type; 79 | vod_str_t uri; // original uri 80 | uint64_t clip_from; 81 | track_mask_t tracks_mask[MEDIA_TYPE_COUNT]; 82 | uint32_t time_shift[MEDIA_TYPE_COUNT]; 83 | media_clip_source_enc_t encryption; 84 | 85 | // derived params 86 | vod_str_t stripped_uri; // without any params like clipTo 87 | vod_str_t mapped_uri; // in case of mapped mode holds the file path 88 | u_char file_key[MEDIA_CLIP_KEY_SIZE]; 89 | 90 | // runtime members 91 | ngx_http_vod_reader_t* reader; 92 | void* reader_context; 93 | off_t alignment; 94 | size_t alloc_extra_size; 95 | 96 | media_clip_source_t* next; 97 | uint64_t last_offset; 98 | }; 99 | 100 | typedef struct { 101 | int codec_mask; 102 | track_mask_t tracks_mask[MEDIA_TYPE_COUNT]; 103 | vod_status_t(*generate)( 104 | request_context_t* request_context, 105 | media_parse_params_t* parse_params, 106 | media_track_array_t* result); 107 | } media_generator_t; 108 | 109 | #endif //__MEDIA_CLIP_H__ 110 | -------------------------------------------------------------------------------- /test/http_utils.py: -------------------------------------------------------------------------------- 1 | from httplib import BadStatusLine, IncompleteRead, InvalidURL 2 | from StringIO import StringIO 3 | from g2o_params import * 4 | import urlparse 5 | import hashlib 6 | import urllib2 7 | import base64 8 | import socket 9 | import shutil 10 | import hmac 11 | import time 12 | import gzip 13 | 14 | MAX_BODY_SIZE = 20 * 1024 * 1024 15 | GET_TIMEOUT = 60 16 | 17 | def parseHttpHeaders(headers): 18 | result = {} 19 | for header in headers: 20 | header = map(lambda y: y.strip(), header.split(':', 1)) 21 | headerName = header[0].lower() 22 | headerValue = header[1] if len(header) > 1 else '' 23 | result.setdefault(headerName, []) 24 | result[headerName].append(headerValue) 25 | return result 26 | 27 | def parseResponse(url, f): 28 | body = f.read(MAX_BODY_SIZE) 29 | 30 | contentLength = f.info().getheader('content-length') 31 | if contentLength is not None and contentLength.isdigit() and len(body) != min(int(contentLength), MAX_BODY_SIZE): 32 | return 0, {}, 'Error: %s content-length %s is different than the resulting file size %s' % (url, contentLength, len(body)) 33 | 34 | if f.info().get('Content-Encoding') == 'gzip': 35 | gzipFile = gzip.GzipFile(fileobj=StringIO(body)) 36 | try: 37 | body = gzipFile.read() 38 | except IOError as e: 39 | return 0, {}, 'Error: failed to decode gzip' 40 | 41 | return f.getcode(), parseHttpHeaders(f.info().headers), body 42 | 43 | def getUrl(url, extraHeaders={}): 44 | headers = getG2OHeaderFullUrl(url) 45 | headers.update(extraHeaders) 46 | request = urllib2.Request(url, headers=headers) 47 | try: 48 | f = urllib2.urlopen(request, timeout=GET_TIMEOUT) 49 | return parseResponse(url, f) 50 | except urllib2.HTTPError as e: 51 | return parseResponse(url, e) 52 | except urllib2.URLError as e: 53 | return 0, {}, 'Error: request failed %s %s' % (url, e) 54 | except BadStatusLine as e: 55 | return 0, {}, 'Error: request failed %s %s' % (url, e) 56 | except socket.error as e: 57 | return 0, {}, 'Error: got socket error %s %s' % (url, e) 58 | except IncompleteRead as e: 59 | return 0, {}, 'Error: got incomplete read error %s %s' % (url, e) 60 | except InvalidURL as e: 61 | return 0, {}, 'Error: got invalid url error %s %s' % (url, e) 62 | 63 | def downloadUrl(url, fileName): 64 | r = urllib2.urlopen(urllib2.Request(url)) 65 | with file(fileName, 'wb') as w: 66 | shutil.copyfileobj(r,w) 67 | r.close() 68 | 69 | def getG2OHeaders(uri): 70 | if len(G2O_KEY) == 0: 71 | return {} 72 | 73 | expiry = '%s' % (int(time.time()) + G2O_WINDOW) 74 | dataFields = [G2O_VERSION, G2O_GHOST_IP, G2O_CLIENT_IP, expiry, G2O_UNIQUE_ID, G2O_NONCE] 75 | data = ', '.join(dataFields) 76 | dig = hmac.new(G2O_KEY, msg=data + uri, digestmod=hashlib.sha256).digest() 77 | sign = base64.b64encode(dig) 78 | return { 79 | G2O_DATA_HEADER_NAME: data, 80 | G2O_SIGN_HEADER_NAME: sign, 81 | } 82 | 83 | def getG2OHeaderFullUrl(url): 84 | parsedUrl = urlparse.urlsplit(url) 85 | uri = urlparse.urlunsplit(urlparse.SplitResult('', '', parsedUrl.path, parsedUrl.query, parsedUrl.fragment)) 86 | return getG2OHeaders(uri) 87 | -------------------------------------------------------------------------------- /test/stream_compare.py: -------------------------------------------------------------------------------- 1 | from urlparse import urlparse 2 | import manifest_utils 3 | import compare_utils 4 | import stress_base 5 | import http_utils 6 | import random 7 | import time 8 | import re 9 | 10 | from stream_compare_params import * 11 | 12 | manifest_utils.CHUNK_LIST_ITEMS_TO_COMPARE = CHUNK_LIST_ITEMS_TO_COMPARE 13 | def convertBody(body): 14 | try: 15 | return body.decode('ascii') 16 | except UnicodeDecodeError: 17 | return body[:100].encode('hex') 18 | 19 | 20 | class TestThread(stress_base.TestThreadBase): 21 | 22 | def getURL(self, hostHeader, url): 23 | headers = {} 24 | headers.update(EXTRA_HEADERS) 25 | headers['Host'] = hostHeader 26 | code, headers, body = http_utils.getUrl(url, headers) 27 | if code == 0: 28 | self.writeOutput(body) 29 | return code, headers, body 30 | 31 | 32 | def compareUrls(self, hostHeader, url1, url2): 33 | 34 | for retry in xrange(URL_COMPARE_RETRIES): 35 | if retry != 0: 36 | time.sleep(URL_COMPARE_RETRIES_SLEEP_INTERVAL) 37 | 38 | if LOG_LEVEL['UrlCompareLog']: 39 | self.writeOutput('Compare %s with %s (retry %d)' % (url1, url2, retry)) 40 | 41 | code1, headers1, body1 = self.getURL(hostHeader, url1) 42 | code2, headers2, body2 = self.getURL(hostHeader, url2) 43 | if code1 != code2: 44 | self.writeOutput('Error: got different status codes %s vs %s, url1=%s, url2=%s' % (code1, code2, url1, url2)) 45 | continue 46 | 47 | headerCompare = compare_utils.compareHeaders(headers1, headers2) 48 | if headerCompare != None: 49 | self.writeOutput(headerCompare) 50 | continue 51 | 52 | if str(code1) != '200': 53 | self.writeOutput('Notice: got status code %s, url1=%s, url2=%s' % (code1, url1, url2)) 54 | 55 | if body1 != body2: 56 | if retry >= URL_COMPARE_RETRIES-1: 57 | severity = "Error" 58 | else: 59 | severity = "Notice" 60 | self.writeOutput('%s: comparison failed, url1=%s, url2=%s\n%s\n%s' % (severity, url1, url2, convertBody(body1), convertBody(body2))) 61 | continue 62 | 63 | return code1, headers1, body1 64 | 65 | return False 66 | 67 | def runTest(self, uri): 68 | hostHeader, uri = uri.split(' ') 69 | 70 | uri = uri.replace('@time@', str(int(time.time()))) 71 | urlBase1 = random.choice(URL1_BASE) 72 | urlBase2 = random.choice(URL2_BASE) 73 | url1 = urlBase1 + uri 74 | url2 = urlBase2 + uri 75 | 76 | self.writeOutput('Info: testing %s %s' % (url1, url2)) 77 | compareResult = self.compareUrls(hostHeader, url1, url2) 78 | if compareResult == False: 79 | return False 80 | code, headers, body = compareResult 81 | if str(code) != '200': 82 | return True 83 | 84 | mimeType = headers['content-type'][0] 85 | urls = manifest_utils.getManifestUrls(url1.rsplit('/', 1)[0], body, mimeType, {'Host':hostHeader}) 86 | urls = map(lambda x: urlBase1 + urlparse(x).path, urls) # the urls may contain the host header 87 | 88 | result = True 89 | for url in urls: 90 | if not self.compareUrls(hostHeader, url, url.replace(urlBase1, urlBase2)): 91 | result = False 92 | return result 93 | 94 | if __name__ == '__main__': 95 | stress_base.main(TestThread, STOP_FILE) 96 | -------------------------------------------------------------------------------- /vod/hls/hls_muxer.h: -------------------------------------------------------------------------------- 1 | #ifndef __HLS_MUXER_H__ 2 | #define __HLS_MUXER_H__ 3 | 4 | // includes 5 | #include "mp4_to_annexb_filter.h" 6 | #include "adts_encoder_filter.h" 7 | #include "mpegts_encoder_filter.h" 8 | #include "buffer_filter.h" 9 | #include "../media_format.h" 10 | #include "../segmenter.h" 11 | 12 | // constants 13 | #define HLS_TIMESCALE (90000) 14 | 15 | // typedefs 16 | struct id3_context_s; 17 | typedef struct id3_context_s id3_context_t; 18 | 19 | typedef void(*hls_get_iframe_positions_callback_t)( 20 | void* context, 21 | uint32_t segment_index, 22 | uint32_t frame_duration, 23 | uint32_t frame_start, 24 | uint32_t frame_size); 25 | 26 | typedef struct { 27 | bool_t interleave_frames; 28 | bool_t align_frames; 29 | bool_t align_pts; 30 | vod_str_t id3_data; 31 | } hls_mpegts_muxer_conf_t; 32 | 33 | typedef struct { 34 | int media_type; 35 | 36 | // input frames 37 | frame_list_part_t* first_frame_part; 38 | frame_list_part_t cur_frame_part; 39 | input_frame_t* cur_frame; 40 | media_clip_source_t* source; 41 | 42 | // time offsets 43 | uint64_t first_frame_time_offset; 44 | uint64_t next_frame_time_offset; 45 | int32_t clip_from_frame_offset; 46 | 47 | // iframes simulation only 48 | uint64_t segment_limit; 49 | bool_t is_first_segment_frame; 50 | uint32_t prev_key_frame; 51 | uint64_t prev_frame_pts; 52 | 53 | // top filter 54 | media_filter_t filter; 55 | media_filter_context_t filter_context; 56 | 57 | // mpegts 58 | mpegts_encoder_state_t mpegts_encoder_state; 59 | } hls_muxer_stream_state_t; 60 | 61 | typedef struct { 62 | request_context_t* request_context; 63 | 64 | // fixed 65 | hls_muxer_stream_state_t* first_stream; 66 | hls_muxer_stream_state_t* last_stream; 67 | bool_t align_pts; 68 | uint32_t video_duration; 69 | 70 | // child states 71 | write_buffer_queue_t queue; 72 | struct id3_context_s* id3_context; 73 | 74 | // cur clip state 75 | media_set_t* media_set; 76 | media_track_t* first_clip_track; 77 | bool_t use_discontinuity; 78 | 79 | // cur frame state 80 | input_frame_t* cur_frame; 81 | bool_t last_stream_frame; 82 | const media_filter_t* cur_writer; 83 | media_filter_context_t* cur_writer_context; 84 | int cache_slot_id; 85 | frames_source_t* frames_source; 86 | void* frames_source_context; 87 | bool_t first_time; 88 | } hls_muxer_state_t; 89 | 90 | // functions 91 | vod_status_t hls_muxer_init_segment( 92 | request_context_t* request_context, 93 | hls_mpegts_muxer_conf_t* conf, 94 | hls_encryption_params_t* encryption_params, 95 | uint32_t segment_index, 96 | media_set_t* media_set, 97 | write_callback_t write_callback, 98 | void* write_context, 99 | bool_t reuse_buffers, 100 | size_t* response_size, 101 | vod_str_t* response_header, 102 | hls_muxer_state_t** processor_state); 103 | 104 | vod_status_t hls_muxer_process(hls_muxer_state_t* state); 105 | 106 | vod_status_t hls_muxer_simulate_get_iframes( 107 | request_context_t* request_context, 108 | segment_durations_t* segment_durations, 109 | hls_mpegts_muxer_conf_t* muxer_conf, 110 | hls_encryption_params_t* encryption_params, 111 | media_set_t* media_set, 112 | hls_get_iframe_positions_callback_t callback, 113 | void* context); 114 | 115 | #endif // __HLS_MUXER_H__ 116 | -------------------------------------------------------------------------------- /vod/subtitle/webvtt_format_template.h: -------------------------------------------------------------------------------- 1 | static int64_t 2 | METHOD(webvtt_read_timestamp)(CHAR_TYPE* cur_pos, CHAR_TYPE** end_pos) 3 | { 4 | int64_t hours; 5 | int64_t minutes; 6 | int64_t seconds; 7 | int64_t millis; 8 | int8_t sign; 9 | 10 | // minus 11 | if (*cur_pos == '-') 12 | { 13 | cur_pos += CHAR_SIZE; 14 | sign = 0; // clamp the timestamp to zero (negative is interpreted as error) 15 | } 16 | else 17 | { 18 | sign = 1; 19 | } 20 | 21 | // hour digits 22 | if (!isdigit(*cur_pos)) 23 | { 24 | return -1; 25 | } 26 | 27 | hours = 0; 28 | for (; isdigit(*cur_pos); cur_pos += CHAR_SIZE) 29 | { 30 | hours = hours * 10 + (*cur_pos - '0'); 31 | } 32 | 33 | // colon 34 | if (*cur_pos != ':') 35 | { 36 | return -1; 37 | } 38 | cur_pos += CHAR_SIZE; 39 | 40 | // 2 minute digits 41 | if (!isdigit(cur_pos[0]) || !isdigit(cur_pos[CHAR_SIZE])) 42 | { 43 | return -1; 44 | } 45 | minutes = (cur_pos[0] - '0') * 10 + (cur_pos[CHAR_SIZE] - '0'); 46 | cur_pos += 2 * CHAR_SIZE; 47 | 48 | // colon 49 | if (*cur_pos == ':') 50 | { 51 | cur_pos += CHAR_SIZE; 52 | 53 | // 2 second digits 54 | if (!isdigit(cur_pos[0]) || !isdigit(cur_pos[CHAR_SIZE])) 55 | { 56 | return -1; 57 | } 58 | seconds = (cur_pos[0] - '0') * 10 + (cur_pos[CHAR_SIZE] - '0'); 59 | cur_pos += 2 * CHAR_SIZE; 60 | } 61 | else 62 | { 63 | // no hours 64 | seconds = minutes; 65 | minutes = hours; 66 | hours = 0; 67 | } 68 | 69 | // dot 70 | if (*cur_pos != '.' && *cur_pos != ',') 71 | { 72 | if (end_pos != NULL) 73 | { 74 | *end_pos = cur_pos; 75 | } 76 | 77 | return sign * 1000 * (seconds + 60 * (minutes + 60 * hours)); 78 | } 79 | cur_pos += CHAR_SIZE; 80 | 81 | // 1-3 digit millis 82 | if (!isdigit(cur_pos[0])) 83 | { 84 | return -1; 85 | } 86 | 87 | millis = (*cur_pos - '0') * 100; 88 | cur_pos += CHAR_SIZE; 89 | 90 | if (isdigit(*cur_pos)) 91 | { 92 | millis += (*cur_pos - '0') * 10; 93 | cur_pos += CHAR_SIZE; 94 | 95 | if (isdigit(*cur_pos)) 96 | { 97 | millis += (*cur_pos - '0'); 98 | cur_pos += CHAR_SIZE; 99 | 100 | while (isdigit(*cur_pos)) 101 | { 102 | cur_pos += CHAR_SIZE; 103 | } 104 | } 105 | } 106 | 107 | if (end_pos != NULL) 108 | { 109 | *end_pos = cur_pos; 110 | } 111 | 112 | return sign * (millis + 1000 * (seconds + 60 * (minutes + 60 * hours))); 113 | } 114 | 115 | static bool_t 116 | METHOD(webvtt_identify_srt)(CHAR_TYPE* p) 117 | { 118 | for (; isspace(*p); p += CHAR_SIZE); 119 | 120 | // n digits 121 | if (!isdigit(*p)) 122 | { 123 | return FALSE; 124 | } 125 | 126 | for (; isdigit(*p); p += CHAR_SIZE); 127 | 128 | for (; *p == ' ' || *p == '\t'; p += CHAR_SIZE); 129 | 130 | // new line 131 | switch (*p) 132 | { 133 | case '\r': 134 | p += CHAR_SIZE; 135 | if (*p == '\n') 136 | { 137 | p += CHAR_SIZE; 138 | } 139 | break; 140 | 141 | case '\n': 142 | p += CHAR_SIZE; 143 | break; 144 | 145 | default: 146 | return FALSE; 147 | } 148 | 149 | // timestamp 150 | if (METHOD(webvtt_read_timestamp)(p, &p) < 0) 151 | { 152 | return FALSE; 153 | } 154 | 155 | for (; *p == ' ' || *p == '\t'; p += CHAR_SIZE); 156 | 157 | // cue marker 158 | return p[0] == '-' && p[CHAR_SIZE] == '-' && p[2 * CHAR_SIZE] == '>'; 159 | } 160 | -------------------------------------------------------------------------------- /vod/mp4/mp4_cenc_encrypt.h: -------------------------------------------------------------------------------- 1 | #ifndef __MP4_CENC_ENCRYPT_H__ 2 | #define __MP4_CENC_ENCRYPT_H__ 3 | 4 | // includes 5 | #include "../dynamic_buffer.h" 6 | #include "../write_buffer.h" 7 | #include "../media_set.h" 8 | #include "mp4_aes_ctr.h" 9 | 10 | // constants 11 | #define VOD_GUID_LENGTH (sizeof("00000000-0000-0000-0000-000000000000") - 1) 12 | 13 | // typedef 14 | struct mp4_cenc_encrypt_video_state_s; 15 | typedef struct mp4_cenc_encrypt_video_state_s mp4_cenc_encrypt_video_state_t; 16 | 17 | typedef vod_status_t (*mp4_cenc_encrypt_video_build_fragment_header_t)( 18 | mp4_cenc_encrypt_video_state_t* state, 19 | vod_str_t* header, 20 | size_t* total_fragment_size); 21 | 22 | typedef struct { 23 | // fixed 24 | segment_writer_t segment_writer; 25 | request_context_t* request_context; 26 | media_set_t* media_set; 27 | media_sequence_t* sequence; 28 | uint32_t segment_index; 29 | 30 | // write buffer 31 | write_buffer_state_t write_buffer; 32 | 33 | // encryption state 34 | mp4_aes_ctr_state_t cipher; 35 | u_char iv[MP4_AES_CTR_IV_SIZE]; 36 | 37 | // frame state 38 | media_clip_filtered_t* cur_clip; 39 | 40 | frame_list_part_t* cur_frame_part; 41 | input_frame_t* cur_frame; 42 | input_frame_t* last_frame; 43 | uint32_t frame_size_left; 44 | 45 | // saiz / saio 46 | size_t saiz_atom_size; 47 | size_t saio_atom_size; 48 | } mp4_cenc_encrypt_state_t; 49 | 50 | struct mp4_cenc_encrypt_video_state_s { 51 | mp4_cenc_encrypt_state_t base; 52 | 53 | // fixed 54 | mp4_cenc_encrypt_video_build_fragment_header_t build_fragment_header; 55 | uint32_t nal_packet_size_length; 56 | uint32_t codec_id; 57 | 58 | // auxiliary data state 59 | vod_dynamic_buf_t auxiliary_data; 60 | u_char* auxiliary_sample_sizes; 61 | u_char* auxiliary_sample_sizes_pos; 62 | uint16_t subsample_count; 63 | 64 | // nal packet state 65 | int cur_state; 66 | uint32_t length_bytes_left; 67 | uint32_t packet_size_left; 68 | bool_t single_nalu_warning_printed; 69 | 70 | // saiz / saio 71 | u_char default_auxiliary_sample_size; 72 | uint32_t saiz_sample_count; 73 | }; 74 | 75 | // functions 76 | u_char* mp4_cenc_encrypt_write_guid(u_char* p, u_char* guid); 77 | 78 | vod_status_t mp4_cenc_encrypt_video_get_fragment_writer( 79 | segment_writer_t* segment_writer, 80 | request_context_t* request_context, 81 | media_set_t* media_set, 82 | uint32_t segment_index, 83 | bool_t single_nalu_per_frame, 84 | mp4_cenc_encrypt_video_build_fragment_header_t build_fragment_header, 85 | const u_char* iv, 86 | vod_str_t* fragment_header, 87 | size_t* total_fragment_size); 88 | 89 | vod_status_t mp4_cenc_encrypt_audio_get_fragment_writer( 90 | segment_writer_t* segment_writer, 91 | request_context_t* request_context, 92 | media_set_t* media_set, 93 | uint32_t segment_index, 94 | const u_char* iv); 95 | 96 | u_char* mp4_cenc_encrypt_video_write_saiz_saio(mp4_cenc_encrypt_video_state_t* state, u_char* p, size_t auxiliary_data_offset); 97 | 98 | size_t mp4_cenc_encrypt_audio_get_auxiliary_data_size(mp4_cenc_encrypt_state_t* state); 99 | 100 | u_char* mp4_cenc_encrypt_audio_write_auxiliary_data(mp4_cenc_encrypt_state_t* state, u_char* p); 101 | 102 | u_char* mp4_cenc_encrypt_audio_write_saiz_saio(mp4_cenc_encrypt_state_t* state, u_char* p, size_t auxiliary_data_offset); 103 | 104 | #endif //__MP4_CENC_ENCRYPT_H__ 105 | -------------------------------------------------------------------------------- /vod/json_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef __JSON_PARSER_H__ 2 | #define __JSON_PARSER_H__ 3 | 4 | // includes 5 | #include "common.h" 6 | 7 | // enums 8 | enum { 9 | VOD_JSON_NULL, 10 | VOD_JSON_BOOL, 11 | VOD_JSON_INT, 12 | VOD_JSON_FRAC, 13 | VOD_JSON_STRING, 14 | VOD_JSON_ARRAY, 15 | VOD_JSON_OBJECT, 16 | }; 17 | 18 | enum { 19 | VOD_JSON_OK = 0, 20 | VOD_JSON_BAD_DATA = -1, 21 | VOD_JSON_ALLOC_FAILED = -2, 22 | VOD_JSON_BAD_LENGTH = -3, 23 | VOD_JSON_BAD_TYPE = -4, 24 | }; 25 | 26 | // typedefs 27 | typedef vod_status_t vod_json_status_t; 28 | 29 | typedef struct { 30 | int64_t num; 31 | uint64_t denom; 32 | } vod_json_fraction_t; 33 | 34 | typedef struct { 35 | int type; 36 | size_t count; 37 | vod_array_part_t part; 38 | } vod_json_array_t; 39 | 40 | typedef vod_array_t vod_json_object_t; 41 | 42 | typedef struct { 43 | int type; 44 | union { 45 | bool_t boolean; 46 | vod_json_fraction_t num; 47 | vod_str_t str; // Note: the string is not unescaped (e.g. may contain \n, \t etc.) 48 | vod_json_array_t arr; 49 | vod_json_object_t obj; // of vod_json_key_value_t 50 | } v; 51 | } vod_json_value_t; 52 | 53 | typedef struct { 54 | vod_uint_t key_hash; 55 | vod_str_t key; 56 | vod_json_value_t value; 57 | } vod_json_key_value_t; 58 | 59 | // functions 60 | vod_json_status_t vod_json_parse( 61 | vod_pool_t* pool, 62 | u_char* string, 63 | vod_json_value_t* result, 64 | u_char* error, 65 | size_t error_size); 66 | 67 | vod_json_status_t vod_json_decode_string(vod_str_t* dest, vod_str_t* src); 68 | 69 | vod_status_t vod_json_init_hash( 70 | vod_pool_t* pool, 71 | vod_pool_t* temp_pool, 72 | char* hash_name, 73 | void* elements, 74 | size_t element_size, 75 | vod_hash_t* result); 76 | 77 | // key extraction - use when the fields have to be parsed in a certain order 78 | typedef struct { 79 | vod_str_t key; 80 | int type; 81 | int index; 82 | } json_object_key_def_t; 83 | 84 | void vod_json_get_object_values( 85 | vod_json_object_t* object, 86 | vod_hash_t* values_hash, 87 | vod_json_value_t** result); 88 | 89 | // value parsing - use when the fields can be parsed in any order, make sure to check for mandatory fields 90 | typedef vod_status_t (*vod_json_object_value_parser_t)( 91 | void* context, 92 | vod_json_value_t* value, 93 | void* dest); 94 | 95 | typedef struct { 96 | vod_str_t key; 97 | int type; 98 | size_t offset; 99 | vod_json_object_value_parser_t parse; 100 | } json_object_value_def_t; 101 | 102 | vod_status_t vod_json_parse_object_values( 103 | vod_json_object_t* object, 104 | vod_hash_t* values_hash, 105 | void* context, 106 | void* result); 107 | 108 | // union parsing 109 | typedef vod_status_t(*json_parser_union_type_parser_t)( 110 | void* context, 111 | vod_json_object_t* object, 112 | void** dest); 113 | 114 | typedef struct { 115 | vod_str_t type; 116 | json_parser_union_type_parser_t parser; 117 | } json_parser_union_type_def_t; 118 | 119 | vod_status_t vod_json_parse_union( 120 | request_context_t* request_context, 121 | vod_json_object_t* object, 122 | vod_str_t* type_field, 123 | vod_uint_t type_field_hash, 124 | vod_hash_t* union_hash, 125 | void* context, 126 | void** dest); 127 | 128 | // misc 129 | vod_status_t vod_json_replace( 130 | vod_json_value_t* json1, 131 | vod_json_value_t* json2); 132 | 133 | #endif // __JSON_PARSER_H__ 134 | -------------------------------------------------------------------------------- /test/uri_compare.py: -------------------------------------------------------------------------------- 1 | import compare_utils 2 | import stress_base 3 | import http_utils 4 | import random 5 | import time 6 | import re 7 | 8 | from uri_compare_params import * 9 | 10 | class TestThread(stress_base.TestThreadBase): 11 | 12 | def getURL(self, hostHeader, url, range = None): 13 | headers = {} 14 | headers.update(EXTRA_HEADERS) 15 | headers['Host'] = hostHeader 16 | headers['Accept-encoding'] = 'gzip' 17 | if range != None: 18 | headers['Range'] = 'bytes=%s' % range 19 | code, headers, body = http_utils.getUrl(url, headers) 20 | if code == 0: 21 | self.writeOutput(body) 22 | return code, headers, body 23 | 24 | def runTest(self, line): 25 | splittedLine = line.split(' ') 26 | if len(splittedLine) == 2: 27 | hostHeader, uri = splittedLine 28 | range = None 29 | elif len(splittedLine) == 3: 30 | range, hostHeader, uri = splittedLine 31 | range = range.split('/')[0] 32 | else: 33 | return True 34 | 35 | urlBase1 = random.choice(URL1_BASE) 36 | urlBase2 = random.choice(URL2_BASE) 37 | url1 = urlBase1 + uri 38 | url2 = urlBase2 + uri 39 | 40 | self.writeOutput('Info: testing %s %s' % (url1, url2)) 41 | 42 | # avoid billing real partners 43 | useRealPartner = False 44 | for curPrefix in USE_REAL_PARTNER_PREFIXES: 45 | if uri.startswith(curPrefix): 46 | useRealPartner = True 47 | if not useRealPartner: 48 | url1 = re.sub('/p/\d+/sp/\d+/', '/p/%s/sp/%s00/' % (TEST_PARTNER_ID, TEST_PARTNER_ID), url1) 49 | url2 = re.sub('/p/\d+/sp/\d+/', '/p/%s/sp/%s00/' % (TEST_PARTNER_ID, TEST_PARTNER_ID), url2) 50 | 51 | code1, headers1, body1 = self.getURL(hostHeader, url1, range) 52 | code2, headers2, body2 = self.getURL(hostHeader, url2, range) 53 | if code1 != code2: 54 | self.writeOutput('Error: got different status codes %s vs %s' % (code1, code2)) 55 | return False 56 | 57 | headerCompare = compare_utils.compareHeaders(headers1, headers2) 58 | if headerCompare != None: 59 | self.writeOutput(headerCompare) 60 | return False 61 | 62 | if not str(code1) in ['200', '206']: 63 | self.writeOutput('Notice: got status code %s' % (code1)) 64 | body1 = re.sub('nginx/\d+\.\d+\.\d+', 'nginx/0.0.0', body1) 65 | body2 = re.sub('nginx/\d+\.\d+\.\d+', 'nginx/0.0.0', body2) 66 | 67 | if (headers1.has_key('content-type') and 68 | headers1['content-type'][0] in set(['application/vnd.apple.mpegurl', 'application/dash+xml'])): 69 | body1 = body1.replace(urlBase1, urlBase2) 70 | body1 = body1.replace('-a1-v1', '-v1-a1') 71 | body2 = body2.replace('-a1-v1', '-v1-a1') 72 | # must strip CF tokens since they sign the domain 73 | body1 = re.sub('&Signature=[^&]+', '&Signature=', re.sub('\?Policy=[^&]+', '?Policy=', body1)) 74 | body2 = re.sub('&Signature=[^&]+', '&Signature=', re.sub('\?Policy=[^&]+', '?Policy=', body2)) 75 | 76 | if body1.startswith('[0-9\.]+<\/executionTime>', '', body1) 78 | body2 = re.sub('[0-9\.]+<\/executionTime>', '', body2) 79 | 80 | if body1.startswith(''): 81 | body1 = body1.replace(' bgcolor="white"', '') 82 | body2 = body2.replace(' bgcolor="white"', '') 83 | 84 | if body1 != body2: 85 | self.writeOutput('Error: comparison failed - url1=%s, url2=%s' % (url1, url2)) 86 | self.writeOutput(body1) 87 | self.writeOutput(body2) 88 | return False 89 | 90 | return True 91 | 92 | if __name__ == '__main__': 93 | stress_base.main(TestThread, STOP_FILE) 94 | -------------------------------------------------------------------------------- /vod/filters/mix_filter.c: -------------------------------------------------------------------------------- 1 | #include "mix_filter.h" 2 | #include "audio_filter.h" 3 | #include "../media_set_parser.h" 4 | 5 | // macros 6 | #define MIX_FILTER_DESC_PATTERN "amix=inputs=%uD[%uD]" 7 | #define INPUT_LINK_PATTERN "[%uD]" 8 | 9 | // typedefs 10 | typedef struct { 11 | media_clip_t base; 12 | } media_clip_mix_filter_t; 13 | 14 | // constants 15 | static json_object_value_def_t mix_filter_params[] = { 16 | { vod_string("sources"), VOD_JSON_ARRAY, offsetof(media_clip_mix_filter_t, base), media_set_parse_filter_sources }, 17 | { vod_null_string, 0, 0, NULL } 18 | }; 19 | 20 | // globals 21 | static vod_hash_t mix_filter_hash; 22 | 23 | static uint32_t 24 | mix_filter_get_desc_size(media_clip_t* clip) 25 | { 26 | return sizeof(MIX_FILTER_DESC_PATTERN) + VOD_INT32_LEN * 2 + 27 | (sizeof(INPUT_LINK_PATTERN) + VOD_INT32_LEN) * clip->source_count; 28 | } 29 | 30 | static u_char* 31 | mix_filter_append_desc(u_char* p, media_clip_t* clip) 32 | { 33 | media_clip_t** sources_end; 34 | media_clip_t** sources_cur; 35 | uint32_t source_count = 0; 36 | 37 | sources_end = clip->sources + clip->source_count; 38 | for (sources_cur = clip->sources; sources_cur < sources_end; sources_cur++) 39 | { 40 | if (*sources_cur == NULL) 41 | { 42 | continue; 43 | } 44 | 45 | p = vod_sprintf( 46 | p, 47 | INPUT_LINK_PATTERN, 48 | (*sources_cur)->id); 49 | 50 | source_count++; 51 | } 52 | 53 | return vod_sprintf( 54 | p, 55 | MIX_FILTER_DESC_PATTERN, 56 | source_count, 57 | clip->id); 58 | } 59 | 60 | static audio_filter_t mix_filter = { 61 | mix_filter_get_desc_size, 62 | mix_filter_append_desc, 63 | }; 64 | 65 | vod_status_t 66 | mix_filter_parse( 67 | void* ctx, 68 | vod_json_object_t* element, 69 | void** result) 70 | { 71 | media_filter_parse_context_t* context = ctx; 72 | media_clip_mix_filter_t* filter; 73 | vod_status_t rc; 74 | 75 | vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0, 76 | "mix_filter_parse: started"); 77 | 78 | filter = vod_alloc(context->request_context->pool, sizeof(*filter)); 79 | if (filter == NULL) 80 | { 81 | vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0, 82 | "mix_filter_parse: vod_alloc failed"); 83 | return VOD_ALLOC_FAILED; 84 | } 85 | 86 | filter->base.type = MEDIA_CLIP_MIX_FILTER; 87 | filter->base.audio_filter = &mix_filter; 88 | filter->base.sources = NULL; 89 | filter->base.source_count = 0; 90 | 91 | rc = vod_json_parse_object_values( 92 | element, 93 | &mix_filter_hash, 94 | context, 95 | filter); 96 | if (rc != VOD_OK) 97 | { 98 | return rc; 99 | } 100 | 101 | if (filter->base.source_count == 0) 102 | { 103 | vod_log_error(VOD_LOG_ERR, context->request_context->log, 0, 104 | "mix_filter_parse: \"sources\" is mandatory for mix filter"); 105 | return VOD_BAD_MAPPING; 106 | } 107 | 108 | *result = &filter->base; 109 | 110 | vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0, 111 | "mix_filter_parse: done"); 112 | 113 | return VOD_OK; 114 | } 115 | 116 | vod_status_t 117 | mix_filter_parser_init( 118 | vod_pool_t* pool, 119 | vod_pool_t* temp_pool) 120 | { 121 | vod_status_t rc; 122 | 123 | rc = vod_json_init_hash( 124 | pool, 125 | temp_pool, 126 | "mix_filter_hash", 127 | mix_filter_params, 128 | sizeof(mix_filter_params[0]), 129 | &mix_filter_hash); 130 | if (rc != VOD_OK) 131 | { 132 | return rc; 133 | } 134 | 135 | return VOD_OK; 136 | } 137 | --------------------------------------------------------------------------------