├── .dockerignore ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Dockerfile ├── Doxyfile ├── Jenkinsfile ├── LICENSE ├── README.md ├── build.bat ├── ftl_app ├── bitstream.c ├── bitstream.h ├── cavlc.c ├── cavlc.h ├── dec_obj.h ├── decode.c ├── decode.h ├── file_parser.c ├── file_parser.h ├── gettimeofday.c ├── gettimeofday.h ├── main.c ├── main.h ├── nalu.c ├── nalu.h ├── posix │ └── ctrlc_handler.c ├── utils.c ├── utils.h └── win32 │ ├── ctrlc_handler.c │ ├── xgetopt.c │ └── xgetopt.h ├── get-audio ├── get-video ├── libftl ├── ftl-sdk.c ├── ftl.h ├── ftl_helpers.c ├── ftl_private.h ├── gettimeofday │ ├── gettimeofday.c │ └── gettimeofday.h ├── handshake.c ├── hmac │ ├── hmac.c │ ├── hmac.h │ ├── sha2.c │ └── sha2.h ├── ingest.c ├── init.c ├── logging.c ├── media.c ├── posix │ ├── socket.c │ ├── socket.h │ ├── threads.c │ └── threads.h └── win32 │ ├── socket.c │ ├── socket.h │ ├── threads.c │ └── threads.h ├── make-deployment-yml ├── scripts ├── build ├── docker-build ├── docker-test ├── lint ├── prep-artifacts └── test └── start-stream /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | **/.git -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | !.vscode/settings.json 3 | *.orig 4 | .vs/* 5 | publish 6 | build-output 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libcurl"] 2 | path = libcurl 3 | url = https://github.com/curl/curl 4 | [submodule "libjansson"] 5 | path = libjansson 6 | url = https://github.com/akheron/jansson 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8.0) 2 | enable_language(C) 3 | project(libftl) 4 | 5 | option(DISABLE_AUTO_INGEST "Set to TRUE to disable auto ingest feature which removes curl and jansson dependancies" FALSE) 6 | MESSAGE(STATUS "FTL DISABLE_AUTO_INGEST: " ${DISABLE_AUTO_INGEST}) 7 | 8 | option(DISABLE_FTL_APP "Set to TRUE to disable including the ftl app in the cmake output." FALSE) 9 | MESSAGE(STATUS "FTL DISABLE_FTL_APP: " ${DISABLE_FTL_APP}) 10 | 11 | option(FTL_STATIC_COMPILE "Set to TRUE if you want ftl to be compiled as a static lib. If TRUE, the program will want to statically link to the ftl cmake object." FALSE) 12 | MESSAGE(STATUS "FTL FTL_STATIC_COMPILE: " ${FTL_STATIC_COMPILE}) 13 | 14 | find_package(Threads REQUIRED) 15 | 16 | set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 17 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 18 | 19 | if (DISABLE_AUTO_INGEST) 20 | add_definitions(-DDISABLE_AUTO_INGEST) 21 | endif() 22 | 23 | # We will only try to include curl if we have auto ingest enabled. 24 | if (NOT DISABLE_AUTO_INGEST) 25 | FIND_PACKAGE(CURL) 26 | endif() 27 | if (NOT CURL_FOUND AND NOT DISABLE_AUTO_INGEST) 28 | SET(CURL_DISABLE_NTLM ON CACHE BOOL "Disabling NTLM") 29 | SET(CURL_DISABLE_TELNET ON CACHE BOOL "Disabling Telnet") 30 | SET(CURL_DISABLE_LDAP ON CACHE BOOL "Disabling Ldap") 31 | SET(CURL_DISABLE_LDAPS ON CACHE BOOL "Disabling secure ldap") 32 | SET(BUILD_CURL_EXE OFF CACHE BOOL "Building libcurl") 33 | SET(HTTP_ONLY ON CACHE BOOL "using compiling HTTP") 34 | SET(BUILD_TESTING OFF CACHE BOOL "Not building Tests") 35 | add_subdirectory(libcurl) 36 | SET(CURL_INCLUDE_DIRS libcurl/include ${CMAKE_CURRENT_BINARY_DIR}/libcurl/include/curl) 37 | SET(CURL_LIBRARIES libcurl) 38 | include_directories(${CURL_INCLUDE_DIRS}) 39 | endif() 40 | 41 | # We will only try to include lib jansson if auto ingest is enabled. 42 | SET(JANSSON_LIBRARIES "") 43 | if (NOT DISABLE_AUTO_INGEST) 44 | SET(JANSSON_BUILD_DOCS OFF CACHE BOOL "Jansson docs off") 45 | SET(JANSSON_WITHOUT_TESTS ON CACHE BOOL "Jansson build without tests") 46 | SET(JANSSON_EXAMPLES OFF CACHE BOOL "Jansson disable examples") 47 | SET(USE_WINDOWS_CRYPTOAPI off) 48 | add_subdirectory(libjansson) 49 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/libjansson/include) 50 | SET(JANSSON_LIBRARIES jansson) 51 | endif() 52 | 53 | if (WIN32) 54 | set(FTL_PLATFORM_FILES ftl_app/win32/xgetopt.c 55 | ftl_app/win32/xgetopt.h 56 | ftl_app/win32/ctrlc_handler.c) 57 | #set(FTL_PLATFORM_LIBS kernel32 user32 gdi32 advapi32 ) 58 | #set(FTL_PLATFORM_LIBS ws2_32 ) 59 | set(FTLSDK_PLATFORM_FILES libftl/win32/socket.c 60 | libftl/win32/socket.h 61 | libftl/win32/threads.c 62 | libftl/win32/threads.h) 63 | include_directories(libftl/win32) 64 | else() 65 | set(FTL_PLATFORM_FILES ftl_app/posix/ctrlc_handler.c) 66 | set(FTLSDK_PLATFORM_FILES libftl/posix/socket.c 67 | libftl/posix/socket.h 68 | libftl/posix/threads.c 69 | libftl/posix/threads.h) 70 | include_directories(libftl/posix) 71 | endif() 72 | 73 | # Figure out what kind of lib we should be producing. 74 | if(FTL_STATIC_COMPILE) 75 | set(FTL_LIB_TYPE STATIC) 76 | add_definitions(-DFTL_STATIC_COMPILE=1) 77 | else(FTL_STATIC_COMPILE) 78 | set(FTL_LIB_TYPE SHARED) 79 | endif(FTL_STATIC_COMPILE) 80 | 81 | add_library(ftl ${FTL_LIB_TYPE} libftl/hmac/hmac.c 82 | libftl/hmac/hmac.h 83 | libftl/hmac/sha2.c 84 | libftl/hmac/sha2.h 85 | libftl/gettimeofday/gettimeofday.c 86 | libftl/gettimeofday/gettimeofday.h 87 | libftl/ftl-sdk.c 88 | libftl/handshake.c 89 | libftl/ingest.c 90 | libftl/ftl_helpers.c 91 | libftl/media.c 92 | libftl/logging.c 93 | libftl/ftl.h 94 | libftl/ftl_private.h 95 | ${FTLSDK_PLATFORM_FILES}) 96 | include_directories(libftl libftl/gettimeofday) 97 | 98 | set_target_properties(ftl PROPERTIES VERSION "0.5.0") 99 | set_target_properties(ftl PROPERTIES SOVERSION 0) 100 | 101 | target_link_libraries(ftl ${CURL_LIBRARIES} ${JANSSON_LIBRARIES}) 102 | 103 | if(WIN32) 104 | target_link_libraries(ftl ws2_32) 105 | endif() 106 | 107 | if (NOT DISABLE_FTL_APP) 108 | add_executable(ftl_app 109 | ftl_app/main.c 110 | ftl_app/main.h 111 | ftl_app/file_parser.c 112 | ftl_app/file_parser.h 113 | ftl_app/gettimeofday.c 114 | ftl_app/gettimeofday.h 115 | ftl_app/bitstream.c 116 | ftl_app/bitstream.h 117 | ftl_app/cavlc.c 118 | ftl_app/cavlc.h 119 | ftl_app/decode.c 120 | ftl_app/decode.h 121 | ftl_app/nalu.c 122 | ftl_app/nalu.h 123 | ftl_app/utils.c 124 | ftl_app/utils.h 125 | ftl_app/dec_obj.h 126 | ${FTL_PLATFORM_FILES}) 127 | 128 | target_link_libraries(ftl_app ftl ${CURL_LIBRARIES} ${JANSSON_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${FTL_PLATFORM_LIBS}) 129 | target_include_directories(ftl_app PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/ftl_app) 130 | endif() 131 | 132 | # Install rules 133 | install(TARGETS ftl DESTINATION lib) -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:9 2 | 3 | RUN apt-get update -y 4 | RUN apt-get install wget -y 5 | RUN apt-get install -y cmake 6 | RUN apt-get install git -y 7 | RUN apt-get install build-essential -y 8 | RUN apt-get install libssl-dev -y 9 | RUN apt-get install libcurl4-openssl-dev -y 10 | RUN apt-get upgrade ca-certificates -y 11 | 12 | RUN apt-get install -y jq curl 13 | 14 | RUN useradd ftl-user 15 | 16 | RUN mkdir -p /opt/ftl-sdk/vid 17 | 18 | RUN chown -R ftl-user:ftl-user /opt/ftl-sdk 19 | 20 | WORKDIR /opt/ftl-sdk/vid 21 | 22 | ARG VIDEO_URL=https://videotestmedia.blob.core.windows.net/ftl/sintel.h264 23 | RUN wget ${VIDEO_URL} 24 | ARG AUDIO_URL=https://videotestmedia.blob.core.windows.net/ftl/sintel.opus 25 | RUN wget ${AUDIO_URL} 26 | 27 | COPY --chown=ftl-user:ftl-user ./CMakeLists.txt /opt/ftl-sdk/CMakeLists.txt 28 | COPY --chown=ftl-user:ftl-user ./libcurl /opt/ftl-sdk/libcurl 29 | COPY --chown=ftl-user:ftl-user ./libjansson /opt/ftl-sdk/libjansson 30 | COPY --chown=ftl-user:ftl-user ./libftl /opt/ftl-sdk/libftl 31 | COPY --chown=ftl-user:ftl-user ./Doxyfile /opt/ftl-sdk/Doxyfile 32 | COPY --chown=ftl-user:ftl-user ./ftl_app /opt/ftl-sdk/ftl_app 33 | COPY --chown=ftl-user:ftl-user ./get-video /opt/ftl-sdk/get-video 34 | COPY --chown=ftl-user:ftl-user ./get-audio /opt/ftl-sdk/get-audio 35 | COPY --chown=ftl-user:ftl-user ./scripts /opt/ftl-sdk/scripts 36 | 37 | USER ftl-user 38 | 39 | WORKDIR /opt/ftl-sdk 40 | 41 | RUN ./scripts/build 42 | 43 | COPY --chown=ftl-user:ftl-user ./start-stream /opt/ftl-sdk/start-stream 44 | 45 | ENTRYPOINT ["./start-stream"] 46 | 47 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | properties([[$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', artifactNumToKeepStr: '2', numToKeepStr: '2']]]) 2 | 3 | node("master") { 4 | def projectName = "ftl-sdk" 5 | 6 | def projectDir = pwd()+ "/${projectName}" 7 | def artifactDir = "${projectDir}/build" 8 | 9 | try { 10 | sh "mkdir -p '${projectDir}'" 11 | dir (projectDir) { 12 | stage("Checkout") { 13 | checkout scm 14 | } 15 | stage("submodules") { 16 | sh 'git submodule update --init' 17 | } 18 | stage("make all") { 19 | sh "mkdir -p build && cd build && cmake .. && make" 20 | } 21 | stage("deploy") { 22 | archiveArtifacts artifacts: "build/ftl_app,build/libftl.so*", fingerprint: false 23 | } 24 | currentBuild.result = "SUCCESS" 25 | } 26 | } catch(e) { 27 | currentBuild.result = "FAILURE" 28 | throw e 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Mixer Interactive, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![FTL-SDK](https://dl.dropboxusercontent.com/u/20701844/tachyon.png) FTL-SDK 2 | 3 | FTL-SDK is a cross platform SDK written in C to enable sending audio/video to mixer using our FTL service 4 | 5 | ### Support Platforms 6 | 7 | - [x] Windows 8 | - [x] iOS/OSX 9 | - [x] Android/Linux 10 | 11 | ### Requirements 12 | 13 | Due to the nature of WebRTC the following audio and video formats are required 14 | 15 | #### Audio 16 | - Opus at 48khz 17 | 18 | #### video 19 | - H.264 (most profiles are supported including baseline, main and high) 20 | - for the lowest delay B Frames should be disabled 21 | 22 | ### Building 23 | 24 | Prerequisites: 25 | 26 | - cmake 2.8.0 or later 27 | - gcc or Visual Studio 2015 Community (or better) and many other tool chains 28 | 29 | getting the code: 30 | 31 | ``` 32 | git clone https://github.com/mixer/ftl-sdk 33 | cd ftl-sdk 34 | git submodule update --init 35 | ``` 36 | 37 | #### Linux/Android/OSX/etc command line instructions 38 | in the directory containing CMakeList.txt (ftl-sdk/) create a folder 39 | ``` 40 | mkdir build 41 | cd build 42 | cmake .. 43 | make 44 | ``` 45 | 46 | #### Windows (specifically for Visual Studio 2015) command line instructions 47 | in the directory containing CMakeList.txt (ftl-sdk) create a folder 48 | ``` 49 | mkdir build 50 | cd build 51 | cmake -G "Visual Studio 14 2015 Win64" .. 52 | msbuild /p:Configuration=Release ALL_BUILD.vcxproj OR open libftl.sln in Visual Studio 53 | ``` 54 | *ftl_app.exe will be placed in build/release directory* 55 | 56 | ### Running Test Application 57 | 58 | download the following test files: 59 | 60 | - sintel.h264: https://www.dropbox.com/s/ruijibs0lgjnq51/sintel.h264 61 | - sintel.opus: https://www.dropbox.com/s/s2r6lggopt9ftw5/sintel.opus 62 | 63 | In the directory containing ftl_app 64 | 65 | ``` 66 | ftl_app -i auto -s "" -v path\to\sintel.h264 -a path\to\sintel.opus -f 24 67 | ``` 68 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | IF EXIST build ( 4 | echo Build Folder Exists 5 | ) ELSE ( 6 | mkdir build 7 | ) 8 | 9 | cd build 10 | 11 | echo Running cmake 12 | 13 | cmake -G "Visual Studio 14 2015 Win64" .. -------------------------------------------------------------------------------- /ftl_app/bitstream.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "bitstream.h" 3 | 4 | 5 | unsigned int _read_bits(struct bitstream_elmt_t *bs, int bits, int update); 6 | 7 | 8 | int bitstream_init(struct bitstream_elmt_t *bs, unsigned char *buf) 9 | { 10 | bs->bit_idx = 8; 11 | bs->byte_idx = 0; 12 | bs->buf = buf; 13 | 14 | return 0; 15 | } 16 | 17 | 18 | /* 19 | T-REC-H.264-200503-P!!MSW-E.doc 20 | Section 9.1 21 | */ 22 | unsigned int bitstream_ue(struct bitstream_elmt_t *bs) 23 | { 24 | int leading_zeros = -1; 25 | int b; 26 | unsigned int code_num; 27 | 28 | for(b = 0; !b; leading_zeros++) 29 | { 30 | b = _read_bits(bs, 1, 1); 31 | } 32 | 33 | code_num = (1 << leading_zeros) - 1 + _read_bits(bs, leading_zeros, 1); 34 | 35 | return code_num; 36 | } 37 | 38 | /* 39 | T-REC-H.264-200503-P!!MSW-E.doc 40 | Section 9.1.1 41 | */ 42 | int bitstream_se(struct bitstream_elmt_t *bs) 43 | { 44 | unsigned int code_num; 45 | int signed_code_num; 46 | int sign; 47 | 48 | code_num = bitstream_ue(bs); 49 | 50 | sign = ~code_num & 0x1; 51 | 52 | signed_code_num = ((code_num + 1) >> 1) * -sign; 53 | 54 | return signed_code_num; 55 | } 56 | 57 | /* 58 | T-REC-H.264-200503-P!!MSW-E.doc 59 | Section 7.2 60 | */ 61 | unsigned int bitstream_u(struct bitstream_elmt_t *bs, int bits) 62 | { 63 | unsigned int value; 64 | 65 | value = _read_bits(bs, bits, 1); 66 | 67 | return value; 68 | } 69 | 70 | /* 71 | T-REC-H.264-200503-P!!MSW-E.doc 72 | Section 7.2 73 | */ 74 | int bitstream_i(struct bitstream_elmt_t *bs, int bits) 75 | { 76 | unsigned int value; 77 | 78 | value = _read_bits(bs, bits, 1); 79 | 80 | return ~value + 1; 81 | } 82 | 83 | unsigned int bitstream_peak(struct bitstream_elmt_t *bs, int bits) 84 | { 85 | unsigned int value; 86 | 87 | value = _read_bits(bs, bits, 0); 88 | 89 | return value; 90 | } 91 | 92 | int bitstream_bytealigned(struct bitstream_elmt_t *bs) 93 | { 94 | return (bs->bit_idx == 8); 95 | } 96 | 97 | unsigned int _read_bits(struct bitstream_elmt_t *bs, int bits, int update) 98 | { 99 | unsigned int val = 0; 100 | int byte_idx; 101 | int bit_idx; 102 | int b_cnt; 103 | int rshft, lshft; 104 | 105 | if(bits == 0) 106 | { 107 | return 0; 108 | } 109 | 110 | byte_idx = bs->byte_idx; 111 | bit_idx = bs->bit_idx; 112 | 113 | do{ 114 | b_cnt = MIN(bit_idx, bits); 115 | 116 | val <<= b_cnt; 117 | 118 | lshft = 8 - bit_idx; 119 | rshft = bit_idx - b_cnt + lshft; 120 | 121 | val |= ((bs->buf[byte_idx] << lshft) & 0xFF) >> rshft; 122 | 123 | bits -= b_cnt; 124 | 125 | bit_idx -= b_cnt; 126 | 127 | if(bit_idx == 0) 128 | { 129 | bit_idx = 8; 130 | byte_idx++; 131 | } 132 | 133 | }while(bits > 0); 134 | 135 | if(update) 136 | { 137 | bs->byte_idx = byte_idx; 138 | bs->bit_idx = bit_idx; 139 | } 140 | 141 | return val; 142 | } -------------------------------------------------------------------------------- /ftl_app/bitstream.h: -------------------------------------------------------------------------------- 1 | #ifndef _BITSTREAM_H_ 2 | #define _BITSTREAM_H_ 3 | 4 | struct bitstream_elmt_t 5 | { 6 | unsigned char *buf; 7 | int byte_idx; 8 | int bit_idx; 9 | }; 10 | 11 | int bitstream_init(struct bitstream_elmt_t *bs, unsigned char *buf); 12 | 13 | 14 | unsigned int bitstream_ue(struct bitstream_elmt_t *bs); 15 | int bitstream_se(struct bitstream_elmt_t *bs); 16 | unsigned int bitstream_u(struct bitstream_elmt_t *bs, int bits); 17 | int bitstream_i(struct bitstream_elmt_t *bs, int bits); 18 | unsigned int bitstream_peak(struct bitstream_elmt_t *bs, int bits); 19 | int bitstream_bytealigned(struct bitstream_elmt_t *bs); 20 | 21 | #endif -------------------------------------------------------------------------------- /ftl_app/cavlc.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ftl-sdk/d0c8469f66806b5ea738d607f7d2b000af8b1129/ftl_app/cavlc.c -------------------------------------------------------------------------------- /ftl_app/cavlc.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ftl-sdk/d0c8469f66806b5ea738d607f7d2b000af8b1129/ftl_app/cavlc.h -------------------------------------------------------------------------------- /ftl_app/dec_obj.h: -------------------------------------------------------------------------------- 1 | #include "bitstream.h" 2 | 3 | enum nalu_type_codes 4 | { 5 | NALU_TYPE_NON_IDR_SLICE = 1, 6 | NALU_TYPE_IDR_SLICE = 5, 7 | NALU_TYPE_SPS = 7, 8 | NALU_TYPE_PPS = 8 9 | }; 10 | 11 | enum slice_types 12 | { 13 | SLICE_TYPE_P = 0, 14 | SLICE_TYPE_B = 1, 15 | SLICE_TYPE_I = 2, 16 | SLICE_TYPE_SP = 3, 17 | SLICE_TYPE_SI = 4 18 | }; 19 | 20 | enum sample_aspect_ratio_indicator 21 | { 22 | VUI_SAR_EXTENDED = 255 23 | }; 24 | 25 | struct nalu_t 26 | { 27 | int forbidden_zero_bit; 28 | int nal_ref_idc; 29 | int nal_unit_type; 30 | int svc_extension_flag; 31 | int idr_flag; 32 | int priority_id; 33 | int no_inter_layer_pred_flag; 34 | int dependency_id; 35 | int quality_id; 36 | int temporal_id; 37 | int use_ref_base_pic_flag; 38 | int discardable_flag; 39 | int output_flag; 40 | int reserved_three_2bits; 41 | }; 42 | 43 | struct hrd_params_t 44 | { 45 | int cpb_cnt_minus1; 46 | int bit_rate_scale; 47 | int cpb_size_scale; 48 | int *bit_rate_value_minus1; 49 | int *cpb_size_value_minus1; 50 | int *cbr_flag; 51 | int initial_cpb_removal_delay_length_minus1; 52 | int cpb_removal_delay_length_minus1; 53 | int dpb_output_delay_length_minus1; 54 | int time_offset_length; 55 | }; 56 | 57 | struct vui_params_t 58 | { 59 | int aspect_ratio_info_present_flag; 60 | int aspect_ratio_idc; 61 | int sar_width; 62 | int sar_height; 63 | int overscan_info_present_flag; 64 | int overscan_appropriate_flag; 65 | int video_signal_type_present_flag; 66 | int video_format; 67 | int video_full_range_flag; 68 | int colour_description_present_flag; 69 | int colour_primaries; 70 | int transfer_characteristics; 71 | int matrix_coefficients; 72 | int chroma_loc_info_present_flag; 73 | int chroma_sample_loc_type_top_field; 74 | int chroma_sample_loc_type_bottom_field; 75 | int timing_info_present_flag; 76 | int num_units_in_tick; 77 | int time_scale; 78 | int fixed_frame_rate_flag; 79 | int nal_hrd_parameters_present_flag; 80 | struct hrd_params_t nal_hrd; 81 | int vcl_hrd_parameters_present_flag; 82 | struct hrd_params_t vlc_hrd; 83 | int low_delay_hrd_flag; 84 | int pic_struct_present_flag; 85 | int bitstream_restriction_flag; 86 | int motion_vectors_over_pic_boundaries_flag; 87 | int max_bytes_per_pic_denom; 88 | int max_bits_per_mb_denom; 89 | int log2_max_mv_length_horizontal; 90 | int log2_max_mv_length_vertical; 91 | int num_reorder_frames; 92 | int max_dec_frame_buffering; 93 | }; 94 | 95 | struct sequence_params_set_t 96 | { 97 | int profile_idc; 98 | int constraint_set0_flag; 99 | int constraint_set1_flag; 100 | int constraint_set2_flag; 101 | int constraint_set3_flag; 102 | int reserved_zero_4bits; /* equal to 0 */ 103 | int level_idc; 104 | int seq_parameter_set_id; 105 | int log2_max_frame_num_minus4; 106 | int pic_order_cnt_type; 107 | int log2_max_pic_order_cnt_lsb_minus4; 108 | int delta_pic_order_always_zero_flag; 109 | int offset_for_non_ref_pic; 110 | int offset_for_top_to_bottom_field; 111 | int num_ref_frames_in_pic_order_cnt_cycle; 112 | int *offset_for_ref_frame; 113 | int num_ref_frames; 114 | int gaps_in_frame_num_value_allowed_flag; 115 | int pic_width_in_mbs_minus1; 116 | int pic_height_in_map_units_minus1; 117 | int frame_mbs_only_flag; 118 | int mb_adaptive_frame_field_flag; 119 | int direct_8x8_inference_flag; 120 | int frame_cropping_flag; 121 | int frame_crop_left_offset; 122 | int frame_crop_right_offset; 123 | int frame_crop_top_offset; 124 | int frame_crop_bottom_offset; 125 | int vui_parameters_present_flag; 126 | struct vui_params_t vui; 127 | 128 | struct sequence_params_set_t *next; 129 | }; 130 | 131 | struct picture_params_set_t 132 | { 133 | int pic_parameter_set_id; 134 | int seq_parameter_set_id; 135 | int entropy_coding_mode_flag; 136 | int pic_order_present_flag; 137 | int num_slice_groups_minus1; 138 | int slice_group_map_type; 139 | int *run_length_minus1; 140 | int *top_left; 141 | int *bottom_right; 142 | int slice_group_change_direction_flag; 143 | int slice_group_change_rate_minus1; 144 | int pic_size_in_map_units_minus1; 145 | int *slice_group_id; 146 | int num_ref_idx_l0_active_minus1; 147 | int num_ref_idx_l1_active_minus1; 148 | int weighted_pred_flag; 149 | int weighted_bipred_idc; 150 | int pic_init_qp_minus26; /* relative to 26 */ 151 | int pic_init_qs_minus26; /* relative to 26 */ 152 | int chroma_qp_index_offset; 153 | int deblocking_filter_control_present_flag; 154 | int constrained_intra_pred_flag; 155 | int redundant_pic_cnt_present_flag; 156 | int transform_8x8_mode_flag; 157 | int pic_scaling_matrix_present_flag; 158 | int *pic_scaling_list_present_flag; 159 | int second_chroma_qp_index_offset; 160 | 161 | struct picture_params_set_t *next; 162 | }; 163 | 164 | struct ref_pic_list_t 165 | { 166 | int reordering_of_pic_nums_idc; 167 | int abs_diff_pic_num_minus1; 168 | int long_term_pic_num; 169 | }; 170 | 171 | struct ref_pic_reorder_t 172 | { 173 | int ref_pic_list_reordering_flag_l0; 174 | struct ref_pic_list_t list0[4]; 175 | int ref_pic_list_reordering_flag_l1; 176 | struct ref_pic_list_t list1[4]; 177 | }; 178 | 179 | struct ref_pic_marking_t 180 | { 181 | int no_output_of_prior_pics_flag; 182 | int long_term_reference_flag; 183 | int adaptive_ref_pic_marking_mode_flag; 184 | int memory_management_control_operation[4]; 185 | int difference_of_pic_nums_minus1; 186 | int long_term_pic_num; 187 | int long_term_frame_idx; 188 | int max_long_term_frame_idx_plus1; 189 | }; 190 | 191 | struct ref_base_pic_marking_t 192 | { 193 | int adaptive_ref_base_pic_marking_mode_flag; 194 | int memory_management_base_control_operation[4]; 195 | int difference_of_base_pic_nums_minus1; 196 | int long_term_base_pic_num; 197 | }; 198 | 199 | struct slice_header_t 200 | { 201 | int first_mb_in_slice; 202 | int slice_type; 203 | int pic_parameter_set_id; 204 | int frame_num; 205 | int field_pic_flag; 206 | int bottom_field_flag; 207 | int idr_pic_id; 208 | int pic_order_cnt_lsb; 209 | int delta_pic_order_cnt_bottom; 210 | int delta_pic_order_cnt[2]; 211 | int redundant_pic_cnt; 212 | int direct_spatial_mv_pred_flag; 213 | int num_ref_idx_active_override_flag; 214 | int num_ref_idx_l0_active_minus1; 215 | int num_ref_idx_l1_active_minus1; 216 | 217 | struct ref_pic_reorder_t ref_pic_reorder; 218 | 219 | struct ref_pic_marking_t ref_pic_marking; 220 | 221 | struct ref_base_pic_marking_t ref_base_pic_marking; 222 | 223 | int cabac_init_idc; 224 | int slice_qp_delta; 225 | int sp_for_switch_flag; 226 | int slice_qs_delta; 227 | int disable_deblocking_filter_idc; 228 | int slice_alpha_c0_offset_div2; 229 | int slice_beta_offset_div2; 230 | int slice_group_change_cycle; 231 | 232 | /*svc specific headers*/ 233 | int store_ref_base_pic_flag; 234 | int slice_skip_flag; 235 | int scan_idx_start; 236 | int scan_idx_end; 237 | }; 238 | 239 | typedef struct _h264_dec_obj_t 240 | { 241 | struct bitstream_elmt_t bs; 242 | unsigned char *nalu_buf; 243 | struct nalu_t nalu; 244 | struct sequence_params_set_t *sps; 245 | struct picture_params_set_t *pps; 246 | struct slice_header_t slice; 247 | }h264_dec_obj_t; -------------------------------------------------------------------------------- /ftl_app/decode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "decode.h" 5 | #include "nalu.h" 6 | #include "utils.h" 7 | 8 | 9 | h264_dec_obj_t * H264_Decode_Open() 10 | { 11 | h264_dec_obj_t *obj; 12 | 13 | if( (obj = malloc(sizeof(h264_dec_obj_t))) == NULL) 14 | { 15 | return NULL; 16 | } 17 | 18 | memset(obj, 0, sizeof(h264_dec_obj_t)); 19 | 20 | 21 | if ((obj->nalu_buf = malloc(10000000)) == NULL) 22 | { 23 | return NULL; 24 | } 25 | 26 | return obj; 27 | } 28 | 29 | int H264_Decode_Nalu(h264_dec_obj_t* h264_dec_obj, unsigned char *nalu, int len) 30 | { 31 | static int last_mba = -1, last_frame_num = -1; 32 | 33 | bitstream_init(&h264_dec_obj->bs, nalu); 34 | 35 | /*get the nalu type and remove picture start code emulation prevention bits*/ 36 | len = nal_unit(h264_dec_obj, &h264_dec_obj->bs, &h264_dec_obj->nalu, len); 37 | 38 | bitstream_init(&h264_dec_obj->bs, h264_dec_obj->nalu_buf); 39 | 40 | switch(h264_dec_obj->nalu.nal_unit_type) 41 | { 42 | case NALU_TYPE_NON_IDR_SLICE: 43 | case NALU_TYPE_IDR_SLICE: 44 | nalu_parse_slice_header(&h264_dec_obj->bs, &h264_dec_obj->nalu, &h264_dec_obj->slice); 45 | break; 46 | case NALU_TYPE_SPS: 47 | { 48 | struct sequence_params_set_t sps; 49 | 50 | nalu_parse_sps(&h264_dec_obj->bs, &sps); 51 | 52 | store_sps(&sps); 53 | break; 54 | } 55 | case NALU_TYPE_PPS: 56 | { 57 | struct picture_params_set_t pps; 58 | 59 | nalu_parse_pps(&h264_dec_obj->bs, &pps); 60 | 61 | store_pps(&pps); 62 | break; 63 | } 64 | default: 65 | printf("Unknown nal unit type: %d\n", h264_dec_obj->nalu.nal_unit_type); 66 | } 67 | 68 | if(h264_dec_obj->nalu.nal_unit_type == NALU_TYPE_NON_IDR_SLICE || h264_dec_obj->nalu.nal_unit_type == NALU_TYPE_IDR_SLICE ) 69 | { 70 | if(h264_dec_obj->slice.first_mb_in_slice == 0) 71 | { 72 | //printf("Frame %d\n", h264_dec_obj->slice.frame_num); 73 | } 74 | 75 | if(last_mba == -1) 76 | { 77 | last_mba = h264_dec_obj->slice.first_mb_in_slice; 78 | last_frame_num = h264_dec_obj->slice.frame_num; 79 | } 80 | else if(last_frame_num == h264_dec_obj->slice.frame_num) 81 | { 82 | if(last_mba >= h264_dec_obj->slice.first_mb_in_slice) 83 | printf("Error: frame %d: current mba is %d, last was %d\n", h264_dec_obj->slice.frame_num, h264_dec_obj->slice.first_mb_in_slice, last_mba); 84 | } 85 | 86 | last_mba = h264_dec_obj->slice.first_mb_in_slice; 87 | last_frame_num = h264_dec_obj->slice.frame_num; 88 | } 89 | 90 | 91 | return 0; 92 | } 93 | 94 | int H264_Decode_Close() 95 | { 96 | 97 | return 0; 98 | } -------------------------------------------------------------------------------- /ftl_app/decode.h: -------------------------------------------------------------------------------- 1 | #include "dec_obj.h" 2 | 3 | h264_dec_obj_t* H264_Decode_Open(); 4 | int H264_Decode_Nalu(h264_dec_obj_t* h264_dec_obj, unsigned char *nalu, int len); 5 | -------------------------------------------------------------------------------- /ftl_app/file_parser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifdef _WIN32 5 | #include 6 | #endif 7 | 8 | 9 | #include "ftl.h" 10 | #include "file_parser.h" 11 | 12 | int _store_first_nalu(h264_obj_t *handle); 13 | 14 | int h264_get_nalu(FILE *fp, uint8_t *buf, uint32_t *length); 15 | int get_ogg_page(opus_obj_t *handle); 16 | 17 | int init_video(h264_obj_t *handle, const char *video_file) { 18 | 19 | if (video_file == NULL) { 20 | return 0; 21 | } 22 | 23 | if ((handle->fp = fopen(video_file, "rb")) == NULL) { 24 | return 0; 25 | } 26 | 27 | if ((handle->h264_handle = H264_Decode_Open()) < 0) 28 | { 29 | return -1; 30 | } 31 | 32 | nalu_item_t *nalu; 33 | 34 | if ((nalu = malloc(sizeof(nalu_item_t))) == NULL) 35 | { 36 | printf("Failed to allocate memory for bitstream\n"); 37 | return -1; 38 | } 39 | 40 | if ((nalu->buf = malloc(10000000)) == NULL) 41 | { 42 | printf("Failed to allocate memory for bitstream\n"); 43 | free(nalu); 44 | return -1; 45 | } 46 | 47 | handle->curr_nalu = nalu; 48 | 49 | if ((nalu = malloc(sizeof(nalu_item_t))) == NULL) 50 | { 51 | printf("Failed to allocate memory for bitstream\n"); 52 | return -1; 53 | } 54 | 55 | if ((nalu->buf = malloc(10000000)) == NULL) 56 | { 57 | printf("Failed to allocate memory for bitstream\n"); 58 | free(nalu); 59 | return -1; 60 | } 61 | 62 | handle->next_nalu = nalu; 63 | 64 | _store_first_nalu(handle); 65 | 66 | return 1; 67 | } 68 | 69 | int reset_video(h264_obj_t *handle) { 70 | fseek(handle->fp, 0, SEEK_SET); 71 | _store_first_nalu(handle); 72 | return 1; 73 | } 74 | 75 | int _store_first_nalu(h264_obj_t *handle) { 76 | h264_get_nalu(handle->fp, handle->curr_nalu->buf, &handle->curr_nalu->len); 77 | H264_Decode_Nalu(handle->h264_handle, handle->curr_nalu->buf, handle->curr_nalu->len); 78 | memcpy(&handle->curr_nalu->slice, &handle->h264_handle->slice, sizeof(struct slice_header_t)); 79 | memcpy(&handle->curr_nalu->nalu, &handle->h264_handle->nalu, sizeof(struct nalu_t)); 80 | 81 | return 1; 82 | } 83 | 84 | int get_video_frame(h264_obj_t *handle, uint8_t *buf, uint32_t *length, int *last_nalu_in_frame) { 85 | int got_sc = 0; 86 | uint32_t pos = 0; 87 | uint8_t nalu_type = 0; 88 | size_t file_pos; 89 | struct slice_header_t *curr_slice, *next_slice;; 90 | struct nalu_t *curr_nalu, *next_nalu; 91 | 92 | *last_nalu_in_frame = 0; 93 | curr_nalu = &handle->curr_nalu->nalu; 94 | curr_slice = &handle->curr_nalu->slice; 95 | 96 | /*read ahead to next packet*/ 97 | if (h264_get_nalu(handle->fp, handle->next_nalu->buf, &handle->next_nalu->len)) { 98 | H264_Decode_Nalu(handle->h264_handle, handle->next_nalu->buf, handle->next_nalu->len); 99 | memcpy(&handle->next_nalu->slice, &handle->h264_handle->slice, sizeof(struct slice_header_t)); 100 | memcpy(&handle->next_nalu->nalu, &handle->h264_handle->nalu, sizeof(struct nalu_t)); 101 | next_nalu = &handle->next_nalu->nalu; 102 | next_slice = &handle->next_nalu->slice; 103 | } 104 | 105 | if (curr_nalu->nal_unit_type == NALU_TYPE_NON_IDR_SLICE || curr_nalu->nal_unit_type == NALU_TYPE_IDR_SLICE) { 106 | //if the next packet is an sps/pps then the current packet is the end of the frame 107 | if (next_nalu->nal_unit_type != NALU_TYPE_NON_IDR_SLICE && next_nalu->nal_unit_type != NALU_TYPE_IDR_SLICE) { 108 | *last_nalu_in_frame = 1; 109 | } 110 | else if (curr_slice->frame_num != next_slice->frame_num) { 111 | *last_nalu_in_frame = 1; 112 | } 113 | } 114 | 115 | memcpy(buf, handle->curr_nalu->buf, handle->curr_nalu->len); 116 | *length = handle->curr_nalu->len; 117 | 118 | /*swap next with current*/ 119 | nalu_item_t *tmp = handle->curr_nalu; 120 | handle->curr_nalu = handle->next_nalu; 121 | handle->next_nalu = tmp; 122 | 123 | return 1; 124 | } 125 | 126 | int init_audio(opus_obj_t *handle, const char *audio_file, int raw_opus) { 127 | 128 | 129 | if (audio_file == NULL) { 130 | return 0; 131 | } 132 | 133 | if ((handle->fp = fopen(audio_file, "rb")) == NULL) { 134 | return 0; 135 | } 136 | 137 | if ((handle->page_buf = malloc(MAX_OGG_PAGE_LEN)) == NULL) { 138 | return 0; 139 | } 140 | 141 | handle->raw_opus = raw_opus; 142 | 143 | handle->current_segment = 0; 144 | handle->consumed = 0; 145 | handle->page_len = 0; 146 | 147 | return 1; 148 | } 149 | 150 | void close_audio(opus_obj_t *handle) { 151 | if (handle->page_buf != NULL) { 152 | free(handle->page_buf); 153 | } 154 | } 155 | 156 | int reset_audio(opus_obj_t *handle) { 157 | fseek(handle->fp, 0, SEEK_SET); 158 | 159 | handle->consumed = 0; 160 | handle->page_len = 0; 161 | 162 | return 1; 163 | } 164 | 165 | int get_audio_packet(opus_obj_t *handle, uint8_t *buf, uint32_t *length) { 166 | 167 | *length = 0; 168 | 169 | if (handle->raw_opus) { 170 | if (feof(handle->fp)) { 171 | return 0; 172 | } 173 | 174 | fread(length, 1, 4, handle->fp); 175 | fread(buf, 1, *length, handle->fp); 176 | } 177 | else { 178 | 179 | if (handle->consumed >= handle->page_len) { 180 | if (get_ogg_page(handle) != 1) { 181 | return 0; 182 | } 183 | } 184 | 185 | int seg_len; 186 | 187 | do { 188 | seg_len = handle->seg_len_table[handle->current_segment]; 189 | memcpy(buf, handle->page_buf + handle->consumed, seg_len); 190 | buf += seg_len; 191 | *length += seg_len; 192 | handle->consumed += seg_len; 193 | handle->current_segment++; 194 | } while (seg_len == 255); 195 | } 196 | 197 | return 1; 198 | } 199 | 200 | int h264_get_nalu(FILE *fp, uint8_t *buf, uint32_t *length) { 201 | uint32_t sc = 0; 202 | uint8_t byte; 203 | uint32_t pos = 0; 204 | int got_sc = 0; 205 | 206 | do { 207 | while (!feof(fp)) { 208 | fread(&byte, 1, 1, fp); 209 | 210 | if (buf != NULL) { 211 | buf[pos] = byte; 212 | } 213 | 214 | pos++; 215 | 216 | sc = (sc << 8) | byte; 217 | 218 | if (sc == 1 || ((sc & 0xFFFFFF) == 1)) { 219 | 220 | pos -= 3; 221 | 222 | if (sc == 1) { 223 | pos -= 1; 224 | } 225 | 226 | got_sc = 1; 227 | break; 228 | } 229 | } 230 | } while (pos == 0); 231 | 232 | *length = pos; 233 | 234 | return got_sc; 235 | } 236 | 237 | uint8_t get_8bits(uint8_t **buf, uint32_t *len) { 238 | uint8_t val = 0; 239 | uint32_t bytes = sizeof(uint8_t); 240 | 241 | if (*len >= bytes) { 242 | *len -= bytes; 243 | } 244 | 245 | val = (*buf)[0]; 246 | 247 | (*buf) += bytes; 248 | 249 | return val; 250 | } 251 | 252 | uint16_t get_16bits(uint8_t **buf, uint32_t *len) { 253 | uint16_t val; 254 | uint32_t bytes = sizeof(uint16_t); 255 | 256 | if (*len >= bytes) { 257 | *len -= bytes; 258 | } 259 | 260 | int i; 261 | for (i = sizeof(uint16_t) - 1; i >= 0; i--) { 262 | val = (val << 8) | (*buf)[i]; 263 | } 264 | 265 | (*buf) += bytes; 266 | 267 | return val; 268 | } 269 | 270 | uint32_t get_32bits(uint8_t **buf, uint32_t *len) { 271 | uint32_t val; 272 | uint32_t bytes = sizeof(uint32_t); 273 | 274 | if (*len >= bytes) { 275 | *len -= bytes; 276 | } 277 | 278 | int i; 279 | for (i = bytes - 1; i >= 0; i--) { 280 | val = (val << 8) | (*buf)[i]; 281 | } 282 | 283 | (*buf) += bytes; 284 | 285 | return val; 286 | } 287 | 288 | uint64_t get_64bits(uint8_t **buf, uint32_t *len) { 289 | uint64_t val; 290 | uint32_t bytes = sizeof(uint64_t); 291 | 292 | if (*len >= bytes) { 293 | *len -= bytes; 294 | } 295 | 296 | int i; 297 | for (i = bytes - 1; i >= 0; i--) { 298 | val = (val << 8) | (*buf)[i]; 299 | } 300 | 301 | (*buf) += bytes; 302 | 303 | return val; 304 | } 305 | 306 | int get_ogg_page(opus_obj_t *handle) { 307 | uint32_t magic_num = 0; 308 | uint8_t byte; 309 | uint32_t pos = 0; 310 | int got_page = 0; 311 | 312 | while (!feof(handle->fp)) { 313 | fread(&byte, 1, 1, handle->fp); 314 | 315 | handle->page_buf[pos] = byte; 316 | pos++; 317 | 318 | if (pos >= MAX_OGG_PAGE_LEN) { 319 | printf("Error page size exceeds max\n"); 320 | exit(-1); 321 | } 322 | 323 | magic_num = (magic_num << 8) | byte; 324 | 325 | if (magic_num == 0x4F676753) { 326 | 327 | pos -= 4; 328 | 329 | if (pos == 0) { 330 | continue; 331 | } 332 | 333 | uint8_t *p = handle->page_buf; 334 | uint32_t bytes_available = pos; 335 | 336 | handle->packets_in_page = 0; 337 | 338 | handle->version = get_8bits(&p, &bytes_available); 339 | handle->header_type = get_8bits(&p, &bytes_available); 340 | handle->granule_pos = get_64bits(&p, &bytes_available); 341 | handle->bs_serial = get_32bits(&p, &bytes_available); 342 | handle->page_sn = get_32bits(&p, &bytes_available); 343 | handle->checksum = get_32bits(&p, &bytes_available); 344 | handle->page_segs = get_8bits(&p, &bytes_available); 345 | 346 | int i; 347 | for (i = 0; i < handle->page_segs; i++) { 348 | handle->seg_len_table[i] = get_8bits(&p, &bytes_available); 349 | if (handle->seg_len_table[i] != 255) { 350 | handle->packets_in_page++; 351 | } 352 | } 353 | 354 | //printf("Page %d, pos %ul, page segs %d, packets %d\n", handle->page_sn, handle->granule_pos, handle->page_segs, handle->packets_in_page); 355 | 356 | handle->consumed = pos - bytes_available; 357 | handle->current_segment = 0; 358 | 359 | got_page = 1; 360 | break; 361 | } 362 | } 363 | 364 | handle->page_len = pos; 365 | 366 | return got_page; 367 | } 368 | 369 | 370 | 371 | -------------------------------------------------------------------------------- /ftl_app/file_parser.h: -------------------------------------------------------------------------------- 1 | #include "decode.h" 2 | 3 | #define MAX_OGG_PAGE_LEN 100000 4 | 5 | 6 | typedef struct { 7 | FILE *fp; 8 | uint8_t *page_buf; 9 | int page_len; 10 | int consumed; 11 | int raw_opus; 12 | uint8_t version; 13 | uint8_t header_type; 14 | uint8_t seg_length; 15 | uint8_t page_segs; 16 | uint64_t granule_pos; 17 | uint32_t bs_serial; 18 | uint32_t page_sn; 19 | uint32_t checksum; 20 | uint8_t seg_len_table[255]; 21 | uint8_t current_segment; 22 | uint8_t packets_in_page; 23 | }opus_obj_t; 24 | 25 | typedef struct _nalu_item_t { 26 | uint8_t *buf; 27 | int len; 28 | struct slice_header_t slice; 29 | struct nalu_t nalu; 30 | }nalu_item_t; 31 | 32 | typedef struct { 33 | FILE *fp; 34 | h264_dec_obj_t *h264_handle; 35 | nalu_item_t *curr_nalu; 36 | nalu_item_t *next_nalu; 37 | }h264_obj_t; 38 | 39 | 40 | int init_video(h264_obj_t *handle, const char *video_file); 41 | int reset_video(h264_obj_t *handle); 42 | int get_video_frame(h264_obj_t *handle, uint8_t *buf, uint32_t *length, int *end_of_frame); 43 | int init_audio(opus_obj_t *handle, const char *audio_file, int raw_opus); 44 | void close_audio(opus_obj_t *handle); 45 | int reset_audio(opus_obj_t *handle); 46 | int get_audio_packet(opus_obj_t *handle, uint8_t *buf, uint32_t *length); 47 | 48 | -------------------------------------------------------------------------------- /ftl_app/gettimeofday.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gettimeofday.c 3 | * Win32 gettimeofday() replacement 4 | * 5 | * src/port/gettimeofday.c 6 | * 7 | * Copyright (c) 2003 SRA, Inc. 8 | * Copyright (c) 2003 SKC, Inc. 9 | * 10 | * Permission to use, copy, modify, and distribute this software and 11 | * its documentation for any purpose, without fee, and without a 12 | * written agreement is hereby granted, provided that the above 13 | * copyright notice and this paragraph and the following two 14 | * paragraphs appear in all copies. 15 | * 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, 17 | * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING 18 | * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS 19 | * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED 20 | * OF THE POSSIBILITY OF SUCH DAMAGE. 21 | * 22 | * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS 25 | * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, 26 | * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 27 | */ 28 | #include "gettimeofday.h" 29 | #ifdef _WIN32 30 | #include 31 | 32 | /* FILETIME of Jan 1 1970 00:00:00. */ 33 | static const unsigned __int64 epoch = ((unsigned __int64)116444736000000000ULL); 34 | 35 | /* 36 | * timezone information is stored outside the kernel so tzp isn't used anymore. 37 | * 38 | * Note: this function is not for Win32 high precision timing purpose. See 39 | * elapsed_time(). 40 | */ 41 | int gettimeofday(struct timeval * tp, struct timezone * tzp) 42 | { 43 | FILETIME file_time; 44 | SYSTEMTIME system_time; 45 | ULARGE_INTEGER ularge; 46 | 47 | GetSystemTime(&system_time); 48 | SystemTimeToFileTime(&system_time, &file_time); 49 | ularge.LowPart = file_time.dwLowDateTime; 50 | ularge.HighPart = file_time.dwHighDateTime; 51 | 52 | tp->tv_sec = (long)((ularge.QuadPart - epoch) / 10000000L); 53 | tp->tv_usec = (long)(system_time.wMilliseconds * 1000); 54 | 55 | return 0; 56 | } 57 | #endif 58 | 59 | int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) 60 | { 61 | /* Perform the carry for the later subtraction by updating y. */ 62 | if (x->tv_usec < y->tv_usec) { 63 | int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; 64 | y->tv_usec -= 1000000 * nsec; 65 | y->tv_sec += nsec; 66 | } 67 | if (x->tv_usec - y->tv_usec > 1000000) { 68 | int nsec = (x->tv_usec - y->tv_usec) / 1000000; 69 | y->tv_usec += 1000000 * nsec; 70 | y->tv_sec -= nsec; 71 | } 72 | 73 | /* Compute the time remaining to wait. 74 | tv_usec is certainly positive. */ 75 | result->tv_sec = x->tv_sec - y->tv_sec; 76 | result->tv_usec = x->tv_usec - y->tv_usec; 77 | 78 | /* Return 1 if result is negative. */ 79 | return x->tv_sec < y->tv_sec; 80 | } 81 | 82 | float timeval_to_ms(struct timeval *tv) { 83 | double sec, usec; 84 | 85 | sec = (float)tv->tv_sec; 86 | usec = (float)tv->tv_usec; 87 | 88 | return (float)(sec * 1000 + usec / 1000); 89 | } 90 | 91 | uint64_t timeval_to_us(struct timeval *tv) 92 | { 93 | return tv->tv_sec * (uint64_t)1000000 + tv->tv_usec; 94 | } 95 | -------------------------------------------------------------------------------- /ftl_app/gettimeofday.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef _WIN32 4 | int gettimeofday(struct timeval * tp, struct timezone * tzp); 5 | #else 6 | #include 7 | #endif 8 | int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y); 9 | float timeval_to_ms(struct timeval *tv); 10 | uint64_t timeval_to_us(struct timeval *tv); 11 | -------------------------------------------------------------------------------- /ftl_app/main.c: -------------------------------------------------------------------------------- 1 | /** 2 | * main.c - Charon client for the FTL SDK 3 | * 4 | * Copyright (c) 2015 Michael Casadevall 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #include "main.h" 26 | #include "gettimeofday.h" 27 | #ifdef _WIN32 28 | #define _CRTDBG_MAP_ALLOC 29 | #include 30 | #include 31 | #include 32 | #include 33 | #else 34 | #include 35 | #include 36 | #endif 37 | #include "file_parser.h" 38 | 39 | void sleep_ms(int ms) 40 | { 41 | #ifdef _WIN32 42 | Sleep(ms); 43 | #else 44 | usleep(ms * 1000); 45 | #endif 46 | } 47 | 48 | void log_test(ftl_log_severity_t log_level, const char *message) 49 | { 50 | fprintf(stderr, "libftl message: %s\n", message); 51 | return; 52 | } 53 | 54 | void usage() 55 | { 56 | printf("Usage: ftl_app -i -s - v -a \n"); 57 | exit(0); 58 | } 59 | 60 | #ifdef _WIN32 61 | int WINAPI ftl_status_thread(LPVOID data); 62 | #else 63 | static void *ftl_status_thread(void *data); 64 | #endif 65 | 66 | int speedtest_duration = 0; 67 | 68 | int main(int argc, char **argv) 69 | { 70 | ftl_status_t status_code; 71 | 72 | char *ingest_location = NULL; 73 | char *video_input = NULL; 74 | char *audio_input = NULL; 75 | char *stream_key = NULL; 76 | int fps_num = 30; 77 | int fps_den = 1; 78 | int raw_opus = 0; 79 | int speedtest_kbps = 1000; 80 | int c; 81 | int audio_pps = 50; 82 | int target_bw_kbps = 0; 83 | 84 | int success = 0; 85 | int verbose = 0; 86 | 87 | opterr = 0; 88 | 89 | charon_install_ctrlc_handler(); 90 | 91 | 92 | if (FTL_VERSION_MAINTENANCE != 0) 93 | { 94 | printf("FTLSDK - version %d.%d.%d\n", FTL_VERSION_MAJOR, FTL_VERSION_MINOR, FTL_VERSION_MAINTENANCE); 95 | } 96 | else 97 | { 98 | printf("FTLSDK - version %d.%d\n", FTL_VERSION_MAJOR, FTL_VERSION_MINOR); 99 | } 100 | 101 | while ((c = getopt(argc, argv, "a:i:v:s:f:b:t:r:?")) != -1) 102 | { 103 | switch (c) 104 | { 105 | case 'i': 106 | ingest_location = optarg; 107 | break; 108 | case 'v': 109 | video_input = optarg; 110 | break; 111 | case 'a': 112 | audio_input = optarg; 113 | break; 114 | case 'r': 115 | if (strcmp(optarg, "a") == 0) { 116 | raw_opus = 1; 117 | } 118 | break; 119 | case 's': 120 | stream_key = optarg; 121 | break; 122 | case 'f': 123 | sscanf(optarg, "%d:%d", &fps_num, &fps_den); 124 | break; 125 | case 'b': 126 | sscanf(optarg, "%d", &target_bw_kbps); 127 | break; 128 | case 't': 129 | sscanf(optarg, "%d:%d", &speedtest_duration, &speedtest_kbps); 130 | break; 131 | case '?': 132 | usage(); 133 | break; 134 | } 135 | } 136 | 137 | /* Make sure we have all the required bits */ 138 | if ((!stream_key || !ingest_location) || ((!video_input || !audio_input) && (!speedtest_duration))) 139 | { 140 | usage(); 141 | } 142 | 143 | FILE *video_fp = NULL; 144 | uint32_t len = 0; 145 | uint8_t *h264_frame = 0; 146 | uint8_t *audio_frame = 0; 147 | opus_obj_t opus_handle; 148 | h264_obj_t h264_handle; 149 | int retval = 0; 150 | 151 | if (video_input != NULL) 152 | { 153 | if ((h264_frame = malloc(10000000)) == NULL) 154 | { 155 | printf("Failed to allocate memory for bitstream\n"); 156 | return -1; 157 | } 158 | 159 | if (!init_video(&h264_handle, video_input)) 160 | { 161 | printf("Failed to open video file\n"); 162 | return -1; 163 | } 164 | } 165 | 166 | if (audio_input != NULL) 167 | { 168 | if ((audio_frame = malloc(1000)) == NULL) 169 | { 170 | printf("Failed to allocate memory for bitstream\n"); 171 | return -1; 172 | } 173 | 174 | if (!init_audio(&opus_handle, audio_input, raw_opus)) 175 | { 176 | printf("Failed to open audio file\n"); 177 | return -1; 178 | } 179 | } 180 | 181 | ftl_init(); 182 | ftl_handle_t handle; 183 | ftl_ingest_params_t params; 184 | 185 | params.stream_key = stream_key; 186 | params.video_codec = FTL_VIDEO_H264; 187 | params.audio_codec = FTL_AUDIO_OPUS; 188 | params.ingest_hostname = ingest_location; 189 | params.fps_num = fps_num; 190 | params.fps_den = fps_den; 191 | params.peak_kbps = target_bw_kbps; 192 | params.vendor_name = "ftl_app"; 193 | params.vendor_version = "0.0.1"; 194 | 195 | struct timeval proc_start_tv, proc_end_tv, proc_delta_tv; 196 | struct timeval profile_start, profile_stop, profile_delta; 197 | #ifdef _WIN32 198 | HANDLE status_thread_handle; 199 | int status_thread_id; 200 | #else 201 | pthread_t status_thread_handle; 202 | #endif 203 | 204 | if ((status_code = ftl_ingest_create(&handle, ¶ms)) != FTL_SUCCESS) 205 | { 206 | printf("Failed to create ingest handle: %s\n", ftl_status_code_to_string(status_code)); 207 | return -1; 208 | } 209 | 210 | if ((status_code = ftl_ingest_connect(&handle)) != FTL_SUCCESS) 211 | { 212 | printf("Failed to connect to ingest: %s\n", ftl_status_code_to_string(status_code)); 213 | return -1; 214 | } 215 | #ifdef _WIN32 216 | if ((status_thread_handle = CreateThread(NULL, 0, ftl_status_thread, &handle, 0, &status_thread_id)) == NULL) 217 | { 218 | #else 219 | if ((pthread_create(&status_thread_handle, NULL, ftl_status_thread, &handle)) != 0) 220 | { 221 | #endif 222 | return FTL_MALLOC_FAILURE; 223 | } 224 | 225 | if (speedtest_duration) 226 | { 227 | printf("Running Speed test: sending %d kbps for %d ms", speedtest_kbps, speedtest_duration); 228 | speed_test_t results; 229 | if ((status_code = ftl_ingest_speed_test_ex(&handle, speedtest_kbps, speedtest_duration, &results)) == FTL_SUCCESS) { 230 | printf("Speed test completed: Peak kbps %d, initial rtt %d, final rtt %d, %3.2f lost packets\n", 231 | results.peak_kbps, results.starting_rtt, results.ending_rtt, (float)results.lost_pkts * 100.f / (float)results.pkts_sent); 232 | } 233 | else { 234 | printf("Speed test failed with: %s\n", ftl_status_code_to_string(status_code)); 235 | } 236 | 237 | goto cleanup; 238 | } 239 | 240 | printf("Stream online!\n"); 241 | printf("Press Ctrl-C to shutdown your stream in this window\n"); 242 | 243 | float video_send_delay = 0, actual_sleep, time_delta; 244 | float video_time_step = (float)(1000 * params.fps_den) / (float)params.fps_num; 245 | 246 | float audio_send_accumulator = video_time_step; 247 | float audio_time_step = 1000.f / audio_pps; 248 | int audio_pkts_sent; 249 | int end_of_frame; 250 | 251 | gettimeofday(&proc_start_tv, NULL); 252 | struct timeval frameTime; 253 | gettimeofday(&frameTime, NULL); // NOTE! In a real app these timestamps should come from the samples! 254 | 255 | while (!ctrlc_pressed()) 256 | { 257 | uint8_t nalu_type; 258 | int audio_read_len; 259 | 260 | if (feof(h264_handle.fp) || feof(opus_handle.fp)) 261 | { 262 | printf("Restarting Stream\n"); 263 | reset_video(&h264_handle); 264 | reset_audio(&opus_handle); 265 | continue; 266 | } 267 | 268 | if (get_video_frame(&h264_handle, h264_frame, &len, &end_of_frame) == 0) 269 | { 270 | continue; 271 | } 272 | 273 | ftl_ingest_send_media_dts(&handle, FTL_VIDEO_DATA, timeval_to_us(&frameTime), h264_frame, len, end_of_frame); 274 | 275 | audio_pkts_sent = 0; 276 | while (audio_send_accumulator > audio_time_step) 277 | { 278 | if (get_audio_packet(&opus_handle, audio_frame, &len) == 0) 279 | { 280 | break; 281 | } 282 | 283 | ftl_ingest_send_media(&handle, FTL_AUDIO_DATA, audio_frame, len, 0); 284 | audio_send_accumulator -= audio_time_step; 285 | audio_pkts_sent++; 286 | } 287 | 288 | nalu_type = h264_frame[0] & 0x1F; 289 | 290 | if (end_of_frame) 291 | { 292 | gettimeofday(&frameTime, NULL); // NOTE! In a real app these timestamps should come from the samples! 293 | gettimeofday(&proc_end_tv, NULL); 294 | timeval_subtract(&proc_delta_tv, &proc_end_tv, &proc_start_tv); 295 | 296 | video_send_delay += video_time_step; 297 | time_delta = (float)timeval_to_ms(&proc_delta_tv); 298 | video_send_delay -= time_delta; 299 | 300 | if (video_send_delay > 0) 301 | { 302 | gettimeofday(&profile_start, NULL); 303 | sleep_ms((int)video_send_delay); 304 | gettimeofday(&profile_stop, NULL); 305 | timeval_subtract(&profile_delta, &profile_stop, &profile_start); 306 | actual_sleep = (float)timeval_to_ms(&profile_delta); 307 | } 308 | else 309 | { 310 | actual_sleep = 0; 311 | } 312 | 313 | video_send_delay -= actual_sleep; 314 | 315 | gettimeofday(&proc_start_tv, NULL); 316 | 317 | audio_send_accumulator += video_time_step; 318 | } 319 | } 320 | 321 | cleanup: 322 | 323 | if (h264_frame != NULL) { 324 | free(h264_frame); 325 | } 326 | 327 | if (audio_frame != NULL) { 328 | free(audio_frame); 329 | } 330 | 331 | close_audio(&opus_handle); 332 | 333 | if ((status_code = ftl_ingest_disconnect(&handle)) != FTL_SUCCESS) 334 | { 335 | printf("Failed to disconnect from ingest: %s\n", ftl_status_code_to_string(status_code)); 336 | retval = -1; 337 | } 338 | 339 | if ((status_code = ftl_ingest_destroy(&handle)) != FTL_SUCCESS) 340 | { 341 | printf("Failed to disconnect from ingest: %s\n", ftl_status_code_to_string(status_code)); 342 | retval = -1; 343 | } 344 | 345 | #ifdef _WIN32 346 | WaitForSingleObject(status_thread_handle, INFINITE); 347 | CloseHandle(status_thread_handle); 348 | #else 349 | pthread_join(status_thread_handle, NULL); 350 | #endif 351 | 352 | #ifdef _WIN32 353 | _CrtDumpMemoryLeaks(); 354 | #endif 355 | 356 | return retval; 357 | } 358 | 359 | #ifdef _WIN32 360 | int WINAPI ftl_status_thread(LPVOID data) 361 | #else 362 | static void *ftl_status_thread(void *data) 363 | #endif 364 | { 365 | ftl_handle_t *handle = (ftl_handle_t *)data; 366 | ftl_status_msg_t status; 367 | ftl_status_t status_code; 368 | int retries = 10; 369 | int retry_sleep = 5000; 370 | 371 | while (1) 372 | { 373 | if ((status_code = ftl_ingest_get_status(handle, &status, 1000)) == FTL_STATUS_TIMEOUT) 374 | { 375 | continue; 376 | } 377 | 378 | if (status_code == FTL_NOT_INITIALIZED) 379 | { 380 | break; 381 | } 382 | 383 | if (status.type == FTL_STATUS_EVENT && status.msg.event.type == FTL_STATUS_EVENT_TYPE_DESTROYED) 384 | { 385 | break; 386 | } 387 | 388 | if (status.type == FTL_STATUS_EVENT && status.msg.event.type == FTL_STATUS_EVENT_TYPE_DISCONNECTED) 389 | { 390 | printf("Disconnected from ingest for reason: %s (%d)\n", ftl_status_code_to_string(status.msg.event.error_code), status.msg.event.reason); 391 | 392 | if (status.msg.event.reason == FTL_STATUS_EVENT_REASON_API_REQUEST) 393 | { 394 | continue; 395 | } 396 | 397 | /*dont reconnect for speed test*/ 398 | if (speedtest_duration) { 399 | continue; 400 | } 401 | 402 | //attempt reconnection 403 | while (retries-- > 0) 404 | { 405 | sleep_ms(retry_sleep); 406 | printf("Attempting to reconnect to ingest (retires left %d)\n", retries); 407 | if ((status_code = ftl_ingest_connect(handle)) == FTL_SUCCESS) 408 | { 409 | retries = 10; 410 | break; 411 | } 412 | printf("Failed to connect to ingest: %s (%d)\n", ftl_status_code_to_string(status_code), status_code); 413 | retry_sleep = retry_sleep * 3 / 2; 414 | } 415 | if (retries <= 0) 416 | { 417 | break; 418 | } 419 | } 420 | else if (status.type == FTL_STATUS_LOG) 421 | { 422 | printf("[%d] %s\n", status.msg.log.log_level, status.msg.log.string); 423 | } 424 | else if (status.type == FTL_STATUS_VIDEO_PACKETS) 425 | { 426 | ftl_packet_stats_msg_t *p = &status.msg.pkt_stats; 427 | 428 | printf("Avg packet send per second %3.1f, total nack requests %d\n", 429 | (float)p->sent * 1000.f / p->period, 430 | p->nack_reqs); 431 | } 432 | else if (status.type == FTL_STATUS_VIDEO_PACKETS_INSTANT) 433 | { 434 | ftl_packet_stats_instant_msg_t *p = &status.msg.ipkt_stats; 435 | 436 | printf("avg transmit delay %dms (min: %d, max: %d), avg rtt %dms (min: %d, max: %d)\n", 437 | p->avg_xmit_delay, p->min_xmit_delay, p->max_xmit_delay, 438 | p->avg_rtt, p->min_rtt, p->max_rtt); 439 | } 440 | else if (status.type == FTL_STATUS_VIDEO) 441 | { 442 | ftl_video_frame_stats_msg_t *v = &status.msg.video_stats; 443 | 444 | printf("Queue an average of %3.2f fps (%3.1f kbps), sent an average of %3.2f fps (%3.1f kbps), queue fullness %d, max frame size %d\n", 445 | (float)v->frames_queued * 1000.f / v->period, 446 | (float)v->bytes_queued / v->period * 8, 447 | (float)v->frames_sent * 1000.f / v->period, 448 | (float)v->bytes_sent / v->period * 8, 449 | v->queue_fullness, v->max_frame_size); 450 | } 451 | else 452 | { 453 | printf("Status: Got Status message of type %d\n", status.type); 454 | } 455 | } 456 | 457 | printf("exited ftl_status_thread\n"); 458 | 459 | return 0; 460 | } 461 | -------------------------------------------------------------------------------- /ftl_app/main.h: -------------------------------------------------------------------------------- 1 | /** 2 | * charon.h - Global Header for the Charon client 3 | * 4 | * Copyright (c) 2015 Michael Casadevall 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | /* Include definitions */ 26 | #include "ftl.h" 27 | 28 | /* Windows specific includes */ 29 | #ifdef _WIN32 30 | 31 | /** 32 | * We have to define LEAN_AND_MEAN because winsock will pull in duplicate 33 | * header definitions without it as the winsock 1.1 API is in windows.h vs 34 | * ws2_32.h. 35 | * 36 | * Yay for backwards source compatability 37 | **/ 38 | 39 | #define WIN32_LEAN_AND_MEAN 1 40 | #include 41 | #include 42 | #include 43 | #include "win32/xgetopt.h" 44 | #else 45 | /* POSIX headers */ 46 | #include 47 | #include 48 | #include 49 | #include 50 | #endif 51 | 52 | /*** 53 | * Functions related to Ctrl-C handling 54 | **/ 55 | 56 | void charon_install_ctrlc_handler(); 57 | void charon_loop_until_ctrlc(); 58 | int ctrlc_pressed(); 59 | -------------------------------------------------------------------------------- /ftl_app/nalu.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define NALU_READ_PRELOAD 100 4 | 5 | int nal_unit(h264_dec_obj_t *obj, struct bitstream_elmt_t *bs, struct nalu_t *nalu, int len); 6 | int nalu_read(FILE *fp, unsigned char *buf); 7 | int nalu_parse_sps(struct bitstream_elmt_t *bs, struct sequence_params_set_t *sps); 8 | int nalu_parse_pps(struct bitstream_elmt_t *bs, struct picture_params_set_t *pps); 9 | void nalu_parse_slice_header(struct bitstream_elmt_t *bs, struct nalu_t *nalu, struct slice_header_t *slice); -------------------------------------------------------------------------------- /ftl_app/posix/ctrlc_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * ctrlc_handler.cpp - POSIX handler for Ctrl-C behavior 3 | * 4 | * Copyright (c) 2015 Michael Casadevall 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #include "main.h" 26 | 27 | /** 28 | * On POSIX platforms, we need to catch SIGINT, and and change the state 29 | * of the shutdown flag. When we're in a signal handler, we've very limited 30 | * in the type of calls we can safely make due to the state of the stack 31 | * and potentially being anywhere in the calling program's execution. 32 | * 33 | * As such, the "safest" thing to do is simply set a global variable which 34 | * breaks us out of the loop, and brings us to our shutdown code. 35 | */ 36 | 37 | volatile sig_atomic_t shutdown_flag = 0; 38 | 39 | void charon_shutdown_stream(int sig) { 40 | shutdown_flag = 1; 41 | } 42 | 43 | void charon_install_ctrlc_handler() { 44 | signal(SIGINT, charon_shutdown_stream); 45 | } 46 | 47 | void charon_loop_until_ctrlc() { 48 | while (shutdown_flag != 1) { 49 | sleep(1); 50 | } 51 | } 52 | 53 | int ctrlc_pressed() { 54 | return shutdown_flag; 55 | } 56 | -------------------------------------------------------------------------------- /ftl_app/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bitstream.h" 5 | #include "dec_obj.h" 6 | #include "utils.h" 7 | 8 | struct picture_params_set_t *pps_head = NULL; 9 | struct sequence_params_set_t *sps_head = NULL; 10 | 11 | void store_sps(struct sequence_params_set_t *sps) 12 | { 13 | struct sequence_params_set_t *sps_new, *tmp; 14 | 15 | sps_new = (struct sequence_params_set_t *)malloc(sizeof(struct sequence_params_set_t)); 16 | memcpy(sps_new, sps, sizeof(struct sequence_params_set_t)); 17 | sps_new->next = NULL; 18 | 19 | if(sps_head == NULL) 20 | { 21 | sps_head = sps_new; 22 | } 23 | else 24 | { 25 | /*find the tail*/ 26 | tmp = sps_head; 27 | 28 | while(tmp->next != NULL) 29 | { 30 | tmp = tmp->next; 31 | } 32 | 33 | tmp->next = sps_new; 34 | } 35 | } 36 | 37 | struct sequence_params_set_t * find_sps(int nal_unit_type, int seq_parameter_set_id) 38 | { 39 | struct sequence_params_set_t *tmp; 40 | 41 | tmp = sps_head; 42 | 43 | while(tmp != NULL) 44 | { 45 | if(nal_unit_type == 20 || nal_unit_type == 14) 46 | { 47 | if( (tmp->profile_idc == 83 || tmp->profile_idc == 86) && tmp->seq_parameter_set_id == seq_parameter_set_id) 48 | { 49 | return tmp; 50 | } 51 | } 52 | else 53 | { 54 | 55 | if(tmp->seq_parameter_set_id == seq_parameter_set_id) 56 | { 57 | return tmp; 58 | } 59 | } 60 | 61 | tmp = tmp->next; 62 | } 63 | 64 | return NULL; 65 | } 66 | 67 | 68 | 69 | void store_pps(struct picture_params_set_t *pps) 70 | { 71 | struct picture_params_set_t *pps_new, *tmp; 72 | 73 | pps_new = (struct picture_params_set_t *)malloc(sizeof(struct picture_params_set_t)); 74 | memcpy(pps_new, pps, sizeof(struct picture_params_set_t)); 75 | pps_new->next = NULL; 76 | 77 | if(pps_head == NULL) 78 | { 79 | pps_head = pps_new; 80 | } 81 | else 82 | { 83 | /*find the tail*/ 84 | tmp = pps_head; 85 | 86 | while(tmp->next != NULL) 87 | { 88 | tmp = tmp->next; 89 | } 90 | 91 | tmp->next = pps_new; 92 | } 93 | } 94 | 95 | struct picture_params_set_t * find_pps(int pic_parameter_set_id) 96 | { 97 | struct picture_params_set_t *tmp; 98 | 99 | tmp = pps_head; 100 | 101 | while(tmp != NULL) 102 | { 103 | if(tmp->pic_parameter_set_id == pic_parameter_set_id) 104 | { 105 | return tmp; 106 | } 107 | 108 | tmp = tmp->next; 109 | } 110 | 111 | return NULL; 112 | } 113 | -------------------------------------------------------------------------------- /ftl_app/utils.h: -------------------------------------------------------------------------------- 1 | #define MIN(x,y) ((x < y) ? x : y) 2 | #define MAX(x,y) ((x > y) ? x : y) 3 | 4 | void store_sps(struct sequence_params_set_t *sps); 5 | struct sequence_params_set_t * find_sps(int nal_unit_type, int seq_parameter_set_id); 6 | void store_pps(struct picture_params_set_t *pps); 7 | struct picture_params_set_t * find_pps(int pic_parameter_set_id); -------------------------------------------------------------------------------- /ftl_app/win32/ctrlc_handler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * ctrlc_handler.cpp - Win32 handler for Ctrl-C behavior 3 | * 4 | * Copyright (c) 2015 Michael Casadevall 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #include "main.h" 26 | 27 | /** 28 | * On POSIX platforms, we need to catch SIGINT, and and change the state 29 | * of the shutdown flag. When we're in a signal handler, we've very limited 30 | * in the type of calls we can safely make due to the state of the stack 31 | * and potentially being anywhere in the calling program's execution. 32 | * 33 | * As such, the "safest" thing to do is simply set a global variable which 34 | * breaks us out of the loop, and brings us to our shutdown code. 35 | */ 36 | 37 | volatile int shutdown_flag = 0; 38 | 39 | BOOL charon_shutdown_stream(DWORD fdwCtrlType) { 40 | // We always shutdown regardless of control message 41 | shutdown_flag = 1; 42 | 43 | // Ugh, hacky hacky hacky. CtrlCHandler calls this 44 | // on its own thread, not on the main thread. 45 | 46 | // Once this function returns, we get grim-reaped. On Win7 47 | // and later, we have 10 seconds before we get nuked from 48 | // orbit. 49 | 50 | Sleep(3000); 51 | switch (fdwCtrlType) { 52 | case CTRL_C_EVENT: 53 | case CTRL_CLOSE_EVENT: 54 | return TRUE; 55 | } 56 | 57 | // Some messages need to go the next handler 58 | return FALSE; 59 | } 60 | 61 | void charon_install_ctrlc_handler() { 62 | SetConsoleCtrlHandler((PHANDLER_ROUTINE)charon_shutdown_stream, TRUE); 63 | } 64 | 65 | BOOL ctrlc_pressed() { 66 | return shutdown_flag; 67 | } 68 | 69 | void charon_loop_until_ctrlc() { 70 | while (shutdown_flag != 1) { 71 | Sleep(1000); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ftl_app/win32/xgetopt.c: -------------------------------------------------------------------------------- 1 | // XGetopt.cpp Version 1.2 2 | // 3 | // Author: Hans Dietrich 4 | // hdietrich2@hotmail.com 5 | // 6 | // Description: 7 | // XGetopt.cpp implements getopt(), a function to parse command lines. 8 | // 9 | // History 10 | // Version 1.2 - 2003 May 17 11 | // - Added Unicode support 12 | // 13 | // Version 1.1 - 2002 March 10 14 | // - Added example to XGetopt.cpp module header 15 | // 16 | // This software is released into the public domain. 17 | // You are free to use it in any way you like. 18 | // 19 | // This software is provided "as is" with no expressed 20 | // or implied warranty. I accept no liability for any 21 | // damage or loss of business that this software may cause. 22 | // 23 | /////////////////////////////////////////////////////////////////////////////// 24 | 25 | 26 | /////////////////////////////////////////////////////////////////////////////// 27 | // if you are not using precompiled headers then include these lines: 28 | #include 29 | #include 30 | #include 31 | /////////////////////////////////////////////////////////////////////////////// 32 | 33 | 34 | #include "XGetopt.h" 35 | 36 | 37 | /////////////////////////////////////////////////////////////////////////////// 38 | // 39 | // X G e t o p t . c p p 40 | // 41 | // 42 | // NAME 43 | // getopt -- parse command line options 44 | // 45 | // SYNOPSIS 46 | // int getopt(int argc, TCHAR *argv[], TCHAR *optstring) 47 | // 48 | // extern TCHAR *optarg; 49 | // extern int optind; 50 | // 51 | // DESCRIPTION 52 | // The getopt() function parses the command line arguments. Its 53 | // arguments argc and argv are the argument count and array as 54 | // passed into the application on program invocation. In the case 55 | // of Visual C++ programs, argc and argv are available via the 56 | // variables __argc and __argv (double underscores), respectively. 57 | // getopt returns the next option letter in argv that matches a 58 | // letter in optstring. (Note: Unicode programs should use 59 | // __targv instead of __argv. Also, all character and string 60 | // literals should be enclosed in _T( ) ). 61 | // 62 | // optstring is a string of recognized option letters; if a letter 63 | // is followed by a colon, the option is expected to have an argument 64 | // that may or may not be separated from it by white space. optarg 65 | // is set to point to the start of the option argument on return from 66 | // getopt. 67 | // 68 | // Option letters may be combined, e.g., "-ab" is equivalent to 69 | // "-a -b". Option letters are case sensitive. 70 | // 71 | // getopt places in the external variable optind the argv index 72 | // of the next argument to be processed. optind is initialized 73 | // to 0 before the first call to getopt. 74 | // 75 | // When all options have been processed (i.e., up to the first 76 | // non-option argument), getopt returns EOF, optarg will point 77 | // to the argument, and optind will be set to the argv index of 78 | // the argument. If there are no non-option arguments, optarg 79 | // will be set to NULL. 80 | // 81 | // The special option "--" may be used to delimit the end of the 82 | // options; EOF will be returned, and "--" (and everything after it) 83 | // will be skipped. 84 | // 85 | // RETURN VALUE 86 | // For option letters contained in the string optstring, getopt 87 | // will return the option letter. getopt returns a question mark (?) 88 | // when it encounters an option letter not included in optstring. 89 | // EOF is returned when processing is finished. 90 | // 91 | // BUGS 92 | // 1) Long options are not supported. 93 | // 2) The GNU double-colon extension is not supported. 94 | // 3) The environment variable POSIXLY_CORRECT is not supported. 95 | // 4) The + syntax is not supported. 96 | // 5) The automatic permutation of arguments is not supported. 97 | // 6) This implementation of getopt() returns EOF if an error is 98 | // encountered, instead of -1 as the latest standard requires. 99 | // 100 | // EXAMPLE 101 | // BOOL CMyApp::ProcessCommandLine(int argc, TCHAR *argv[]) 102 | // { 103 | // int c; 104 | // 105 | // while ((c = getopt(argc, argv, _T("aBn:"))) != EOF) 106 | // { 107 | // switch (c) 108 | // { 109 | // case _T('a'): 110 | // TRACE(_T("option a\n")); 111 | // // 112 | // // set some flag here 113 | // // 114 | // break; 115 | // 116 | // case _T('B'): 117 | // TRACE( _T("option B\n")); 118 | // // 119 | // // set some other flag here 120 | // // 121 | // break; 122 | // 123 | // case _T('n'): 124 | // TRACE(_T("option n: value=%d\n"), atoi(optarg)); 125 | // // 126 | // // do something with value here 127 | // // 128 | // break; 129 | // 130 | // case _T('?'): 131 | // TRACE(_T("ERROR: illegal option %s\n"), argv[optind-1]); 132 | // return FALSE; 133 | // break; 134 | // 135 | // default: 136 | // TRACE(_T("WARNING: no handler for option %c\n"), c); 137 | // return FALSE; 138 | // break; 139 | // } 140 | // } 141 | // // 142 | // // check for non-option args here 143 | // // 144 | // return TRUE; 145 | // } 146 | // 147 | /////////////////////////////////////////////////////////////////////////////// 148 | 149 | TCHAR *optarg; // global argument pointer 150 | int optind = 0; // global argv index 151 | 152 | int getopt(int argc, TCHAR *argv[], TCHAR *optstring) 153 | { 154 | static TCHAR *next = NULL; 155 | if (optind == 0) 156 | next = NULL; 157 | 158 | optarg = NULL; 159 | 160 | if (next == NULL || *next == _T('\0')) 161 | { 162 | if (optind == 0) 163 | optind++; 164 | 165 | if (optind >= argc || argv[optind][0] != _T('-') || argv[optind][1] == _T('\0')) 166 | { 167 | optarg = NULL; 168 | if (optind < argc) 169 | optarg = argv[optind]; 170 | return EOF; 171 | } 172 | 173 | if (_tcscmp(argv[optind], _T("--")) == 0) 174 | { 175 | optind++; 176 | optarg = NULL; 177 | if (optind < argc) 178 | optarg = argv[optind]; 179 | return EOF; 180 | } 181 | 182 | next = argv[optind]; 183 | next++; // skip past - 184 | optind++; 185 | } 186 | 187 | TCHAR c = *next++; 188 | TCHAR *cp = _tcschr(optstring, c); 189 | 190 | if (cp == NULL || c == _T(':')) 191 | return _T('?'); 192 | 193 | cp++; 194 | if (*cp == _T(':')) 195 | { 196 | if (*next != _T('\0')) 197 | { 198 | optarg = next; 199 | next = NULL; 200 | } 201 | else if (optind < argc) 202 | { 203 | optarg = argv[optind]; 204 | optind++; 205 | } 206 | else 207 | { 208 | return _T('?'); 209 | } 210 | } 211 | 212 | return c; 213 | } 214 | -------------------------------------------------------------------------------- /ftl_app/win32/xgetopt.h: -------------------------------------------------------------------------------- 1 | // XGetopt.h Version 1.2 2 | // 3 | // Author: Hans Dietrich 4 | // hdietrich2@hotmail.com 5 | // 6 | // This software is released into the public domain. 7 | // You are free to use it in any way you like. 8 | // 9 | // This software is provided "as is" with no expressed 10 | // or implied warranty. I accept no liability for any 11 | // damage or loss of business that this software may cause. 12 | // 13 | /////////////////////////////////////////////////////////////////////////////// 14 | 15 | #ifndef XGETOPT_H 16 | #define XGETOPT_H 17 | 18 | int optind, opterr; 19 | extern TCHAR *optarg; 20 | 21 | int getopt(int argc, TCHAR *argv[], TCHAR *optstring); 22 | 23 | #endif //XGETOPT_H 24 | -------------------------------------------------------------------------------- /get-audio: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ue 3 | 4 | OUTPUT=$1 5 | 6 | curl 'https://www.dropbox.com/s/s2r6lggopt9ftw5/sintel.opus' \ 7 | -H 'authority: www.dropbox.com' \ 8 | -H 'cache-control: max-age=0' \ 9 | -H 'upgrade-insecure-requests: 1' \ 10 | -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36' \ 11 | -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' \ 12 | -H 'accept-encoding: gzip, deflate, br' \ 13 | -H 'accept-language: en-US,en;q=0.9' \ 14 | -H 'cookie: __Host-ss=UsByWdIPus; locale=en; gvc=MjU4NzQxNzA3MDE4MjU3MDIzNDY0Nzc2NjAxNDg5MTU4MTg0MjYz; seen-sl-signup-modal=VHJ1ZQ%3D%3D; t=0-Hq7tZ1TDyj-aBNiWYv8YQl; __Host-js_csrf=0-Hq7tZ1TDyj-aBNiWYv8YQl; seen-sl-download-modal=VHJ1ZQ%3D%3D' \ 15 | --compressed > $OUTPUT 16 | 17 | -------------------------------------------------------------------------------- /get-video: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ue 3 | 4 | OUTPUT=$1 5 | 6 | curl 'https://www.dropbox.com/s/ruijibs0lgjnq51/sintel.h264' \ 7 | -H 'authority: www.dropbox.com' \ 8 | -H 'cache-control: max-age=0' \ 9 | -H 'upgrade-insecure-requests: 1' \ 10 | -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36' \ 11 | -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' \ 12 | -H 'accept-encoding: gzip, deflate, br' \ 13 | -H 'accept-language: en-US,en;q=0.9' \ 14 | -H 'cookie: __Host-ss=UsByWdIPus; locale=en; gvc=MjU4NzQxNzA3MDE4MjU3MDIzNDY0Nzc2NjAxNDg5MTU4MTg0MjYz; seen-sl-signup-modal=VHJ1ZQ%3D%3D; t=0-Hq7tZ1TDyj-aBNiWYv8YQl; __Host-js_csrf=0-Hq7tZ1TDyj-aBNiWYv8YQl; seen-sl-download-modal=VHJ1ZQ%3D%3D' \ 15 | --compressed > $OUTPUT 16 | -------------------------------------------------------------------------------- /libftl/ftl-sdk.c: -------------------------------------------------------------------------------- 1 | 2 | #define __FTL_INTERNAL 3 | #include "ftl.h" 4 | #include "ftl_private.h" 5 | #ifndef DISABLE_AUTO_INGEST 6 | #include 7 | #endif 8 | 9 | static BOOL _get_chan_id_and_key(const char *stream_key, uint32_t *chan_id, char *key); 10 | static int _lookup_ingest_ip(const char *ingest_location, char *ingest_ip); 11 | 12 | char error_message[1000]; 13 | FTL_API const int FTL_VERSION_MAJOR = 0; 14 | FTL_API const int FTL_VERSION_MINOR = 9; 15 | FTL_API const int FTL_VERSION_MAINTENANCE = 14; 16 | 17 | // Initializes all sublibraries used by FTL 18 | FTL_API ftl_status_t ftl_init() { 19 | struct timeval now; 20 | 21 | init_sockets(); 22 | os_init(); 23 | #ifndef DISABLE_AUTO_INGEST 24 | curl_global_init(CURL_GLOBAL_ALL); 25 | #endif 26 | 27 | gettimeofday(&now, NULL); 28 | srand((unsigned int)now.tv_sec); 29 | return FTL_SUCCESS; 30 | } 31 | 32 | FTL_API ftl_status_t ftl_ingest_create(ftl_handle_t *ftl_handle, ftl_ingest_params_t *params){ 33 | ftl_status_t ret_status = FTL_SUCCESS; 34 | ftl_stream_configuration_private_t *ftl = NULL; 35 | 36 | do { 37 | if ((ftl = (ftl_stream_configuration_private_t *)malloc(sizeof(ftl_stream_configuration_private_t))) == NULL) { 38 | // Note it is important that we return here otherwise the call to 39 | // internal_ftl_ingest_destroy will fail! 40 | return FTL_MALLOC_FAILURE; 41 | } 42 | 43 | memset(ftl, 0, sizeof(ftl_stream_configuration_private_t)); 44 | 45 | // First create any components that the system relies on. 46 | os_init_mutex(&ftl->state_mutex); 47 | os_init_mutex(&ftl->disconnect_mutex); 48 | os_init_mutex(&ftl->status_q.mutex); 49 | 50 | if (os_semaphore_create(&ftl->status_q.sem, "/StatusQueue", O_CREAT, 0) < 0) { 51 | ret_status = FTL_MALLOC_FAILURE; 52 | break; 53 | } 54 | 55 | // Capture the incoming key. 56 | ftl->key = NULL; 57 | if ((ftl->key = (char*)malloc(sizeof(char)*MAX_KEY_LEN)) == NULL) { 58 | ret_status = FTL_MALLOC_FAILURE; 59 | break; 60 | } 61 | if (_get_chan_id_and_key(params->stream_key, &ftl->channel_id, ftl->key) == FALSE) { 62 | ret_status = FTL_BAD_OR_INVALID_STREAM_KEY; 63 | break; 64 | } 65 | 66 | ftl->audio.codec = params->audio_codec; 67 | ftl->video.codec = params->video_codec; 68 | 69 | ftl->audio.media_component.payload_type = AUDIO_PTYPE; 70 | ftl->video.media_component.payload_type = VIDEO_PTYPE; 71 | 72 | //TODO: this should be randomly generated, there is a potential for ssrc collisions with this 73 | ftl->audio.media_component.ssrc = ftl->channel_id; 74 | ftl->video.media_component.ssrc = ftl->channel_id + 1; 75 | 76 | ftl->video.fps_num = params->fps_num; 77 | ftl->video.fps_den = params->fps_den; 78 | ftl->video.dts_usec = 0; 79 | ftl->audio.dts_usec = 0; 80 | ftl->video.dts_error = 0; 81 | 82 | strncpy_s(ftl->vendor_name, sizeof(ftl->vendor_name) / sizeof(ftl->vendor_name[0]), params->vendor_name, sizeof(ftl->vendor_name) / sizeof(ftl->vendor_name[0]) - 1); 83 | strncpy_s(ftl->vendor_version, sizeof(ftl->vendor_version) / sizeof(ftl->vendor_version[0]), params->vendor_version, sizeof(ftl->vendor_version) / sizeof(ftl->vendor_version[0]) - 1); 84 | 85 | /*this is legacy, this isnt used anymore*/ 86 | ftl->video.width = 1280; 87 | ftl->video.height = 720; 88 | 89 | ftl->video.media_component.peak_kbps = params->peak_kbps; 90 | ftl->param_ingest_hostname = _strdup(params->ingest_hostname); 91 | 92 | ftl->status_q.count = 0; 93 | ftl->status_q.head = NULL; 94 | 95 | ftl_set_state(ftl, FTL_STATUS_QUEUE); 96 | 97 | ftl_handle->priv = ftl; 98 | return ret_status; 99 | } while (0); 100 | 101 | internal_ftl_ingest_destroy(ftl); 102 | 103 | return ret_status; 104 | } 105 | 106 | FTL_API ftl_status_t ftl_ingest_connect(ftl_handle_t *ftl_handle){ 107 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)ftl_handle->priv; 108 | ftl_status_t status = FTL_SUCCESS; 109 | 110 | do { 111 | if ((status = _init_control_connection(ftl)) != FTL_SUCCESS) { 112 | break; 113 | } 114 | 115 | if ((status = _ingest_connect(ftl)) != FTL_SUCCESS) { 116 | break; 117 | } 118 | 119 | if ((status = media_init(ftl)) != FTL_SUCCESS) { 120 | break; 121 | } 122 | 123 | return status; 124 | } while (0); 125 | 126 | if (os_trylock_mutex(&ftl->disconnect_mutex)) { 127 | internal_ingest_disconnect(ftl); 128 | os_unlock_mutex(&ftl->disconnect_mutex); 129 | } 130 | 131 | return status; 132 | } 133 | 134 | FTL_API ftl_status_t ftl_ingest_get_status(ftl_handle_t *ftl_handle, ftl_status_msg_t *msg, int ms_timeout) { 135 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)ftl_handle->priv; 136 | 137 | if (ftl == NULL) { 138 | return FTL_NOT_INITIALIZED; 139 | } 140 | 141 | return dequeue_status_msg(ftl, msg, ms_timeout); 142 | } 143 | 144 | FTL_API ftl_status_t ftl_ingest_update_params(ftl_handle_t *ftl_handle, ftl_ingest_params_t *params) { 145 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)ftl_handle->priv; 146 | ftl_status_t status = FTL_SUCCESS; 147 | 148 | ftl->video.media_component.peak_kbps = params->peak_kbps; 149 | 150 | if (params->ingest_hostname != NULL) { 151 | if (ftl->param_ingest_hostname != NULL) { 152 | free(ftl->param_ingest_hostname); 153 | ftl->param_ingest_hostname = NULL; 154 | } 155 | 156 | ftl->param_ingest_hostname = _strdup(params->ingest_hostname); 157 | } 158 | 159 | /* not going to update fps for the moment*/ 160 | /* 161 | ftl->video.fps_num = params->fps_num; 162 | ftl->video.fps_den = params->fps_den; 163 | */ 164 | 165 | return status; 166 | } 167 | 168 | FTL_API int ftl_ingest_speed_test(ftl_handle_t *ftl_handle, int speed_kbps, int duration_ms) { 169 | 170 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)ftl_handle->priv; 171 | 172 | speed_test_t results; 173 | 174 | FTL_LOG(ftl, FTL_LOG_WARN, "%s() is depricated, please use ftl_ingest_speed_test_ex()\n", __FUNCTION__); 175 | 176 | if (media_speed_test(ftl, speed_kbps, duration_ms, &results) == FTL_SUCCESS) { 177 | return results.peak_kbps; 178 | } 179 | 180 | return -1; 181 | } 182 | 183 | FTL_API ftl_status_t ftl_ingest_speed_test_ex(ftl_handle_t *ftl_handle, int speed_kbps, int duration_ms, speed_test_t *results) { 184 | 185 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)ftl_handle->priv; 186 | 187 | return media_speed_test(ftl, speed_kbps, duration_ms, results); 188 | } 189 | 190 | FTL_API int ftl_ingest_send_media_dts(ftl_handle_t *ftl_handle, ftl_media_type_t media_type, int64_t dts_usec, uint8_t *data, int32_t len, int end_of_frame) { 191 | 192 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)ftl_handle->priv; 193 | int bytes_sent = 0; 194 | 195 | if (media_type == FTL_AUDIO_DATA) { 196 | bytes_sent = media_send_audio(ftl, dts_usec, data, len); 197 | } 198 | else if (media_type == FTL_VIDEO_DATA) { 199 | bytes_sent = media_send_video(ftl, dts_usec, data, len, end_of_frame); 200 | } 201 | else { 202 | return bytes_sent; 203 | } 204 | 205 | return bytes_sent; 206 | } 207 | 208 | FTL_API int ftl_ingest_send_media(ftl_handle_t *ftl_handle, ftl_media_type_t media_type, uint8_t *data, int32_t len, int end_of_frame) { 209 | 210 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)ftl_handle->priv; 211 | int64_t dts_increment_usec, dts_usec = 0; 212 | 213 | if (media_type == FTL_AUDIO_DATA) { 214 | dts_usec = ftl->audio.dts_usec; 215 | dts_increment_usec = AUDIO_PACKET_DURATION_MS * 1000; 216 | ftl->audio.dts_usec += dts_increment_usec; 217 | } 218 | else if (media_type == FTL_VIDEO_DATA) { 219 | dts_usec = ftl->video.dts_usec; 220 | if (end_of_frame) { 221 | float dst_usec_f = (float)ftl->video.fps_den * 1000000.f / (float)ftl->video.fps_num + ftl->video.dts_error; 222 | dts_increment_usec = (int64_t)(dst_usec_f); 223 | ftl->video.dts_error = dst_usec_f - (float)dts_increment_usec; 224 | ftl->video.dts_usec += dts_increment_usec; 225 | } 226 | } 227 | 228 | return ftl_ingest_send_media_dts(ftl_handle, media_type, dts_usec, data, len, end_of_frame); 229 | } 230 | 231 | FTL_API ftl_status_t ftl_ingest_disconnect(ftl_handle_t *ftl_handle) { 232 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)ftl_handle->priv; 233 | ftl_status_t status_code = FTL_SUCCESS; 234 | 235 | os_lock_mutex(&ftl->disconnect_mutex); 236 | 237 | if (ftl_get_state(ftl, FTL_CONNECTED)) { 238 | 239 | status_code = internal_ingest_disconnect(ftl); 240 | 241 | ftl_status_msg_t status; 242 | status.type = FTL_STATUS_EVENT; 243 | status.msg.event.reason = FTL_STATUS_EVENT_REASON_API_REQUEST; 244 | status.msg.event.type = FTL_STATUS_EVENT_TYPE_DISCONNECTED; 245 | status.msg.event.error_code = FTL_USER_DISCONNECT; 246 | 247 | enqueue_status_msg(ftl, &status); 248 | } 249 | 250 | os_unlock_mutex(&ftl->disconnect_mutex); 251 | 252 | return status_code; 253 | } 254 | 255 | ftl_status_t internal_ingest_disconnect(ftl_stream_configuration_private_t *ftl) { 256 | 257 | ftl_status_t status_code; 258 | 259 | ftl_set_state(ftl, FTL_DISCONNECT_IN_PROGRESS); 260 | 261 | if ((status_code = media_destroy(ftl)) != FTL_SUCCESS) { 262 | FTL_LOG(ftl, FTL_LOG_ERROR, "failed to clean up media channel with error %d\n", status_code); 263 | } 264 | 265 | if ((status_code = _ingest_disconnect(ftl)) != FTL_SUCCESS) { 266 | FTL_LOG(ftl, FTL_LOG_ERROR, "Disconnect failed with error %d\n", status_code); 267 | } 268 | 269 | ftl_clear_state(ftl, FTL_DISCONNECT_IN_PROGRESS); 270 | 271 | 272 | return FTL_SUCCESS; 273 | } 274 | 275 | ftl_status_t internal_ftl_ingest_destroy(ftl_stream_configuration_private_t *ftl) { 276 | 277 | if (ftl != NULL) { 278 | 279 | ftl_clear_state(ftl, FTL_STATUS_QUEUE); 280 | //if a thread is waiting send a destroy event 281 | if (ftl->status_q.thread_waiting) { 282 | ftl_status_msg_t status; 283 | status.type = FTL_STATUS_EVENT; 284 | status.msg.event.reason = FTL_STATUS_EVENT_REASON_API_REQUEST; 285 | status.msg.event.type = FTL_STATUS_EVENT_TYPE_DESTROYED; 286 | status.msg.event.error_code = FTL_SUCCESS; 287 | enqueue_status_msg(ftl, &status); 288 | } 289 | 290 | //wait a few ms for the thread to pull that last message and exit 291 | 292 | int wait_retries = 5; 293 | while (ftl->status_q.thread_waiting && wait_retries-- > 0) { 294 | sleep_ms(20); 295 | }; 296 | 297 | if (ftl->status_q.thread_waiting) { 298 | fprintf(stderr, "Thread is still waiting in ftl_ingest_get_status()\n"); 299 | } 300 | 301 | os_lock_mutex(&ftl->status_q.mutex); 302 | 303 | status_queue_elmt_t *elmt; 304 | while (ftl->status_q.head != NULL) { 305 | elmt = ftl->status_q.head; 306 | ftl->status_q.head = elmt->next; 307 | free(elmt); 308 | ftl->status_q.count--; 309 | } 310 | 311 | os_unlock_mutex(&ftl->status_q.mutex); 312 | os_delete_mutex(&ftl->status_q.mutex); 313 | 314 | os_semaphore_delete(&ftl->status_q.sem); 315 | 316 | ingest_release(ftl); 317 | 318 | if (ftl->key != NULL) { 319 | free(ftl->key); 320 | } 321 | 322 | if (ftl->ingest_hostname != NULL) { 323 | free(ftl->ingest_hostname); 324 | } 325 | 326 | if (ftl->param_ingest_hostname != NULL) { 327 | free(ftl->param_ingest_hostname); 328 | } 329 | 330 | free(ftl); 331 | } 332 | 333 | return FTL_SUCCESS; 334 | } 335 | 336 | FTL_API ftl_status_t ftl_ingest_destroy(ftl_handle_t *ftl_handle){ 337 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)ftl_handle->priv; 338 | 339 | ftl_handle->priv = NULL; 340 | 341 | return internal_ftl_ingest_destroy(ftl); 342 | } 343 | 344 | FTL_API char* ftl_status_code_to_string(ftl_status_t status) { 345 | 346 | switch (status) { 347 | case FTL_SUCCESS: 348 | return "Success"; 349 | case FTL_SOCKET_NOT_CONNECTED: 350 | return "The socket is no longer connected"; 351 | case FTL_MALLOC_FAILURE: 352 | return "Internal memory allocation error"; 353 | case FTL_DNS_FAILURE: 354 | return "Failed to get an ip address for the specified ingest (DNS lookup failure)"; 355 | case FTL_CONNECT_ERROR: 356 | return "An unknown error occurred connecting to the socket"; 357 | case FTL_INTERNAL_ERROR: 358 | return "An Internal error occurred"; 359 | case FTL_CONFIG_ERROR: 360 | return "The parameters supplied are invalid or incomplete"; 361 | case FTL_STREAM_REJECTED: 362 | return "The Ingest rejected the stream"; 363 | case FTL_NOT_ACTIVE_STREAM: 364 | return "The stream is not active"; 365 | case FTL_UNAUTHORIZED: 366 | return "This channel is not authorized to connect to this ingest"; 367 | case FTL_AUDIO_SSRC_COLLISION: 368 | return "The Audio SSRC is already in use"; 369 | case FTL_VIDEO_SSRC_COLLISION: 370 | return "The Video SSRC is already in use"; 371 | case FTL_BAD_REQUEST: 372 | return "A request to the ingest was invalid"; 373 | case FTL_OLD_VERSION: 374 | return "The current version of the FTL-SDK is no longer supported"; 375 | case FTL_BAD_OR_INVALID_STREAM_KEY: 376 | return "Invalid stream key"; 377 | case FTL_UNSUPPORTED_MEDIA_TYPE: 378 | return "The specified media type is not supported"; 379 | case FTL_NOT_CONNECTED: 380 | return "The channel is not connected"; 381 | case FTL_ALREADY_CONNECTED: 382 | return "The channel is already connected"; 383 | case FTL_STATUS_TIMEOUT: 384 | return "Timed out waiting for status message"; 385 | case FTL_QUEUE_FULL: 386 | return "The status queue is full"; 387 | case FTL_STATUS_WAITING_FOR_KEY_FRAME: 388 | return "dropping packets until a key frame is received"; 389 | case FTL_QUEUE_EMPTY: 390 | return "The status queue is empty"; 391 | case FTL_NOT_INITIALIZED: 392 | return "The parameters were not correctly initialized"; 393 | case FTL_CHANNEL_IN_USE: 394 | return "Channel is already actively streaming"; 395 | case FTL_REGION_UNSUPPORTED: 396 | return "The location you are attempting to stream from is not authorized to do so by the local government"; 397 | case FTL_NO_MEDIA_TIMEOUT: 398 | return "The ingest did not receive any audio or video media for an extended period of time"; 399 | case FTL_USER_DISCONNECT: 400 | return "ftl ingest disconnect api was called"; 401 | case FTL_INGEST_NO_RESPONSE: 402 | return "ingest did not respond to request"; 403 | case FTL_NO_PING_RESPONSE: 404 | return "ingest did not respond to keepalive"; 405 | case FTL_SPEED_TEST_ABORTED: 406 | return "the speed test was aborted, possibly due to a network interruption"; 407 | case FTL_INGEST_SOCKET_CLOSED: 408 | return "the ingest socket was closed"; 409 | case FTL_INGEST_SOCKET_TIMEOUT: 410 | return "the ingest socket was hit a timeout."; 411 | case FTL_INGEST_SERVER_TERMINATE: 412 | return "The server has terminated the stream."; 413 | case FTL_UNKNOWN_ERROR_CODE: 414 | default: 415 | /* Unknown FTL error */ 416 | return "Unknown status code"; 417 | } 418 | } 419 | 420 | BOOL _get_chan_id_and_key(const char *stream_key, uint32_t *chan_id, char *key) { 421 | size_t len, i = 0; 422 | 423 | if (stream_key == NULL) { 424 | return FALSE; 425 | } 426 | 427 | char * copy_of_key = _strdup(stream_key); 428 | 429 | len = strlen(stream_key); 430 | 431 | // Restream.io stream keys start with 're_' and 3 first letters should be skipped 432 | char *restream_stream_key_marker = "re_"; 433 | if (len >= 3 && strncmp(stream_key, restream_stream_key_marker, strlen(restream_stream_key_marker)) == 0) { 434 | for (i = 0; i < len - 3; i++) { 435 | copy_of_key[i] = copy_of_key[i + 3]; 436 | } 437 | 438 | copy_of_key[i] = '\0'; 439 | 440 | len = strlen(copy_of_key); 441 | } 442 | 443 | for (i = 0; i != len; i++) { 444 | if (copy_of_key[i] == '-' || copy_of_key[i] == ',' || copy_of_key[i] == '_') { 445 | /* stream key gets copied */ 446 | strcpy_s(key, MAX_KEY_LEN, copy_of_key+i+1); 447 | 448 | /* Now get the channel id */ 449 | copy_of_key[i] = '\0'; 450 | *chan_id = atol(copy_of_key); 451 | 452 | free(copy_of_key); 453 | return TRUE; 454 | } 455 | } 456 | 457 | free(copy_of_key); 458 | return FALSE; 459 | } 460 | 461 | 462 | -------------------------------------------------------------------------------- /libftl/ftl.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file ftl.h - Public Interface for the FTL SDK 3 | * 4 | * Copyright (c) 2015 Michael Casadevall 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #ifndef __FTL_H 26 | #define __FTL_H 27 | 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | #ifdef _WIN32 35 | # ifdef FTL_STATIC_COMPILE 36 | # define FTL_API 37 | # else 38 | # ifdef __FTL_INTERNAL 39 | # define FTL_API __declspec(dllexport) 40 | # else 41 | # define FTL_API __declspec(dllimport) 42 | # endif 43 | # endif 44 | #else 45 | # define FTL_API 46 | #endif 47 | 48 | FTL_API extern const int FTL_VERSION_MAJOR; 49 | FTL_API extern const int FTL_VERSION_MINOR; 50 | FTL_API extern const int FTL_VERSION_MAINTENANCE; 51 | 52 | 53 | /*! \defgroup ftl_public Public Interfaces for libftl */ 54 | 55 | /*! \brief Status codes used by libftl 56 | * \ingroup ftl_public 57 | */ 58 | 59 | typedef enum { 60 | FTL_SUCCESS, /**< Operation was successful */ 61 | FTL_SOCKET_NOT_CONNECTED, 62 | FTL_NON_ZERO_POINTER, /**< Function required a zero-ed pointer, but didn't get one */ 63 | FTL_MALLOC_FAILURE, /**< memory allocation failed */ 64 | FTL_DNS_FAILURE, /**< DNS probe failed */ 65 | FTL_CONNECT_ERROR, /**< Failed to connect to ingest */ 66 | FTL_INTERNAL_ERROR, /**< Got valid inputs, but FTL failed to complete the action due to internal failure */ 67 | FTL_CONFIG_ERROR, /**< The configuration supplied was invalid or incomplete */ 68 | FTL_STREAM_REJECTED, /**< Ingest rejected our connect command */ 69 | FTL_NOT_ACTIVE_STREAM, /**< The function required an active stream and was passed an inactive one */ 70 | FTL_UNAUTHORIZED, /**< Parameters were correct, but streamer not authorized to use FTL */ 71 | FTL_AUDIO_SSRC_COLLISION, /**< The audio SSRC from this IP is currently in use */ 72 | FTL_VIDEO_SSRC_COLLISION, /**< The video SSRC from this IP is currently in use */ 73 | FTL_BAD_REQUEST, /**< Ingest didn't like our request. Should never happen */ 74 | FTL_OLD_VERSION, /**< libftl needs to be updated */ 75 | FTL_BAD_OR_INVALID_STREAM_KEY, 76 | FTL_UNSUPPORTED_MEDIA_TYPE, 77 | FTL_GAME_BLOCKED, /**< The current game set by this profile can't be streamed. */ 78 | FTL_NOT_CONNECTED, 79 | FTL_ALREADY_CONNECTED, 80 | FTL_UNKNOWN_ERROR_CODE, 81 | FTL_STATUS_TIMEOUT, 82 | FTL_QUEUE_FULL, 83 | FTL_STATUS_WAITING_FOR_KEY_FRAME, 84 | FTL_QUEUE_EMPTY, 85 | FTL_NOT_INITIALIZED, 86 | FTL_CHANNEL_IN_USE, /**< The channel is already actively streaming */ 87 | FTL_REGION_UNSUPPORTED, /**< The region you are attempting to stream from is not authorized to stream by local governments */ 88 | FTL_NO_MEDIA_TIMEOUT, 89 | FTL_USER_DISCONNECT, 90 | FTL_INGEST_NO_RESPONSE, 91 | FTL_NO_PING_RESPONSE, 92 | FTL_SPEED_TEST_ABORTED, 93 | FTL_INGEST_SOCKET_CLOSED, 94 | FTL_INGEST_SOCKET_TIMEOUT, 95 | FTL_INGEST_SERVER_TERMINATE, 96 | } ftl_status_t; 97 | 98 | typedef enum { 99 | FTL_CONNECTION_DISCONNECTED, 100 | FTL_CONNECTION_RECONNECTED 101 | } ftl_connection_status_t; 102 | 103 | 104 | #define FOREVER -1 105 | /*! \brief Video codecs supported by FTL 106 | * \ingroug ftl_public 107 | */ 108 | 109 | typedef enum { 110 | FTL_VIDEO_NULL, /**< No video for this stream */ 111 | FTL_VIDEO_VP8, /**< Google's VP8 codec (recommended default) */ 112 | FTL_VIDEO_H264 113 | } ftl_video_codec_t; 114 | 115 | /*! \brief Audio codecs supported by FTL 116 | * \ingroup ftl_public 117 | */ 118 | 119 | typedef enum { 120 | FTL_AUDIO_NULL, /**< No audio for this stream */ 121 | FTL_AUDIO_OPUS, /**< Xiph's Opus audio codec */ 122 | FTL_AUDIO_AAC 123 | } ftl_audio_codec_t; 124 | 125 | typedef enum { 126 | FTL_AUDIO_DATA, 127 | FTL_VIDEO_DATA 128 | } ftl_media_type_t; 129 | 130 | /*! \brief Log levels used by libftl; returned via logging callback 131 | * \ingroup ftl_public 132 | */ 133 | 134 | typedef enum { 135 | FTL_LOG_CRITICAL, 136 | FTL_LOG_ERROR, 137 | FTL_LOG_WARN, 138 | FTL_LOG_INFO, 139 | FTL_LOG_DEBUG 140 | } ftl_log_severity_t; 141 | 142 | /*! \brief Function prototype for FTL logging callback 143 | * \ingroup ftl_public 144 | */ 145 | 146 | typedef void(*ftl_logging_function_t)(ftl_log_severity_t log_level, const char * log_message); 147 | typedef void(*ftl_status_function_t)(ftl_connection_status_t status); 148 | 149 | typedef struct { 150 | char const *ingest_hostname; 151 | char const *stream_key; 152 | ftl_video_codec_t video_codec; 153 | ftl_audio_codec_t audio_codec; 154 | int peak_kbps; //used for the leaky bucket to smooth out packet flow rate, set to 0 to bypass 155 | int fps_num; 156 | int fps_den; 157 | char const *vendor_name; 158 | char const *vendor_version; 159 | } ftl_ingest_params_t; 160 | 161 | typedef struct { 162 | int pkts_sent; 163 | int nack_requests; 164 | int lost_pkts; 165 | int starting_rtt; 166 | int ending_rtt; 167 | int bytes_sent; 168 | int duration_ms; 169 | int peak_kbps; 170 | }speed_test_t; 171 | 172 | typedef struct { 173 | void* priv; 174 | } ftl_handle_t; 175 | 176 | typedef enum { 177 | FTL_STATUS_NONE, 178 | FTL_STATUS_LOG, 179 | FTL_STATUS_EVENT, 180 | FTL_STATUS_VIDEO_PACKETS, 181 | FTL_STATUS_VIDEO_PACKETS_INSTANT, 182 | FTL_STATUS_AUDIO_PACKETS, 183 | FTL_STATUS_VIDEO, 184 | FTL_STATUS_AUDIO, 185 | FTL_STATUS_FRAMES_DROPPED, 186 | FTL_STATUS_NETWORK, 187 | FTL_BITRATE_CHANGED 188 | } ftl_status_types_t; 189 | 190 | typedef enum { 191 | FTL_STATUS_EVENT_TYPE_UNKNOWN, 192 | FTL_STATUS_EVENT_TYPE_CONNECTED, 193 | FTL_STATUS_EVENT_TYPE_DISCONNECTED, 194 | FTL_STATUS_EVENT_TYPE_DESTROYED, 195 | FTL_STATUS_EVENT_INGEST_ERROR_CODE 196 | } ftl_status_event_types_t; 197 | 198 | typedef enum { 199 | FTL_STATUS_EVENT_REASON_NONE, 200 | FTL_STATUS_EVENT_REASON_NO_MEDIA, 201 | FTL_STATUS_EVENT_REASON_API_REQUEST, 202 | FTL_STATUS_EVENT_REASON_UNKNOWN, 203 | } ftl_status_event_reasons_t; 204 | 205 | typedef struct { 206 | int log_level; 207 | char string[1024]; 208 | }ftl_status_log_msg_t; 209 | 210 | typedef struct { 211 | ftl_status_event_types_t type; 212 | ftl_status_event_reasons_t reason; 213 | ftl_status_t error_code; 214 | }ftl_status_event_msg_t; 215 | 216 | typedef struct { 217 | int64_t period; //period of time in ms the stats were collected over 218 | int64_t sent; 219 | int64_t nack_reqs; 220 | int64_t lost; 221 | int64_t recovered; 222 | int64_t late; 223 | }ftl_packet_stats_msg_t; 224 | 225 | typedef struct { 226 | int64_t period; //period of time in ms the stats were collected over 227 | int min_rtt; 228 | int max_rtt; 229 | int avg_rtt; 230 | int min_xmit_delay; 231 | int max_xmit_delay; 232 | int avg_xmit_delay; 233 | }ftl_packet_stats_instant_msg_t; 234 | 235 | typedef struct { 236 | int64_t period; //period of time in ms the stats were collected over 237 | int64_t frames_queued; 238 | int64_t frames_sent; 239 | int64_t bytes_queued; 240 | int64_t bytes_sent; 241 | int64_t bw_throttling_count; 242 | int queue_fullness; 243 | int max_frame_size; 244 | }ftl_video_frame_stats_msg_t; 245 | 246 | typedef enum 247 | { 248 | FTL_BITRATE_DECREASED, 249 | FTL_BITRATE_INCREASED, 250 | FTL_BITRATE_STABILIZED 251 | }ftl_bitrate_changed_type_t; 252 | 253 | typedef enum 254 | { 255 | FTL_BANDWIDTH_CONSTRAINED, 256 | FTL_UPGRADE_EXCESSIVE, 257 | FTL_BANDWIDTH_AVAILABLE, 258 | FTL_STABILIZE_ON_LOWER_BITRATE, 259 | FTL_STABILIZE_ON_ORIGINAL_BITRATE, 260 | } ftl_bitrate_changed_reason_t; 261 | 262 | typedef struct 263 | { 264 | ftl_bitrate_changed_type_t bitrate_changed_type; 265 | ftl_bitrate_changed_reason_t bitrate_changed_reason; 266 | uint64_t current_encoding_bitrate; 267 | uint64_t previous_encoding_bitrate; 268 | float nacks_to_frames_ratio; 269 | float avg_rtt; 270 | uint64_t avg_frames_dropped; 271 | float queue_fullness; 272 | } ftl_bitrate_changed_msg_t; 273 | 274 | /*status messages*/ 275 | typedef struct 276 | { 277 | ftl_status_types_t type; 278 | union 279 | { 280 | ftl_status_log_msg_t log; 281 | ftl_status_event_msg_t event; 282 | ftl_packet_stats_msg_t pkt_stats; 283 | ftl_packet_stats_instant_msg_t ipkt_stats; 284 | ftl_video_frame_stats_msg_t video_stats; 285 | ftl_bitrate_changed_msg_t bitrate_changed_msg; 286 | } msg; 287 | }ftl_status_msg_t; 288 | 289 | /*! 290 | * \ingroup ftl_public 291 | * \brief FTL Initialization 292 | * 293 | * Before using FTL, you must call ftl_init before making any additional calls 294 | * in this library. ftl_init initializes any submodules that FTL depends on such 295 | * as libsrtp. Under normal cirmstances, this function should never fail. 296 | * 297 | * On Windows, this function calls WSAStartup() to initialize Winsock. It is 298 | * the responsibility of the calling app to call WSACleanup() at application 299 | * shutdown as FTL can't safely call it (as your application may be using sockets 300 | * elsewhere 301 | * 302 | * @returns FTL_INIT_SUCCESS on successful initialization. Otherwise, returns 303 | * ftl_init_status_t enum with the failure state. 304 | */ 305 | FTL_API ftl_status_t ftl_init(); 306 | 307 | FTL_API ftl_status_t ftl_ingest_create(ftl_handle_t *ftl_handle, ftl_ingest_params_t *params); 308 | 309 | FTL_API ftl_status_t ftl_ingest_connect(ftl_handle_t *ftl_handle); 310 | 311 | FTL_API int ftl_ingest_speed_test(ftl_handle_t *ftl_handle, int speed_kbps, int duration_ms); 312 | FTL_API ftl_status_t ftl_ingest_speed_test_ex(ftl_handle_t *ftl_handle, int speed_kbps, int duration_ms, speed_test_t *results); 313 | 314 | // Deprecated! Please use the DTS version. 315 | FTL_API int ftl_ingest_send_media(ftl_handle_t *ftl_handle, ftl_media_type_t media_type, uint8_t *data, int32_t len, int end_of_frame); 316 | 317 | FTL_API int ftl_ingest_send_media_dts(ftl_handle_t *ftl_handle, ftl_media_type_t media_type, int64_t dts_usec, uint8_t *data, int32_t len, int end_of_frame); 318 | 319 | FTL_API ftl_status_t ftl_ingest_get_status(ftl_handle_t *ftl_handle, ftl_status_msg_t *msg, int ms_timeout); 320 | 321 | FTL_API ftl_status_t ftl_ingest_update_params(ftl_handle_t *ftl_handle, ftl_ingest_params_t *params); 322 | 323 | FTL_API ftl_status_t ftl_ingest_disconnect(ftl_handle_t *ftl_handle); 324 | 325 | FTL_API ftl_status_t ftl_ingest_destroy(ftl_handle_t *ftl_handle); 326 | 327 | FTL_API char* ftl_status_code_to_string(ftl_status_t status); 328 | 329 | FTL_API ftl_status_t ftl_find_closest_available_ingest(const char* ingestHosts[], int ingestsCount, char* bestIngestHostComputed); 330 | 331 | FTL_API ftl_status_t ftl_get_video_stats(ftl_handle_t* handle, uint64_t* frames_sent, uint64_t* nacks_received, uint64_t* rtt_recorded, uint64_t* frames_dropped, float* queue_fullness); 332 | 333 | FTL_API ftl_status_t ftl_adaptive_bitrate_thread( 334 | ftl_handle_t* ftl_handle, 335 | void* context, 336 | int(*change_bitrate_callback)(void*, uint64_t), 337 | uint64_t initial_encoding_bitrate, 338 | uint64_t min_encoding_bitrate, 339 | uint64_t max_encoding_bitrate 340 | ); 341 | 342 | #ifdef __cplusplus 343 | } // extern "C" 344 | #endif 345 | 346 | #endif // __FTL_H 347 | -------------------------------------------------------------------------------- /libftl/ftl_helpers.c: -------------------------------------------------------------------------------- 1 | /** 2 | * ftl_helpers.c - 3 | * 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | **/ 23 | 24 | #include 25 | #include "ftl.h" 26 | #include "ftl_private.h" 27 | #include "hmac/hmac.h" 28 | 29 | /* 30 | Please note that throughout the code, we send "\r\n\r\n", where a normal newline ("\n") would suffice. 31 | This is done due to some firewalls / anti-malware systems not passing our packets through when we don't send those double-windows-newlines. 32 | They seem to incorrectly detect our protocol as HTTP. 33 | */ 34 | 35 | int ftl_read_response_code(const char * response_str) { 36 | int response_code = 0; 37 | int count; 38 | 39 | count = sscanf_s(response_str, "%d", &response_code); 40 | 41 | return count ? response_code : -1; 42 | 43 | } 44 | 45 | int ftl_read_media_port(const char *response_str) { 46 | int port = -1; 47 | 48 | if ((sscanf_s(response_str, "%*[^.]. Use UDP port %d\n", &port)) != 1) { 49 | return -1; 50 | } 51 | 52 | return port; 53 | } 54 | 55 | unsigned char decode_hex_char(char c) { 56 | if (c >= '0' && c <= '9') { 57 | return c - '0'; 58 | } 59 | 60 | // Set the 5th bit. Makes ASCII chars lowercase :) 61 | c |= (1 << 5); 62 | 63 | if (c >= 'a' && c <= 'z') { 64 | return (c - 'a') + 10; 65 | } 66 | 67 | return 0; 68 | } 69 | 70 | int recv_all(SOCKET sock, char * buf, int buflen, const char line_terminator) { 71 | int pos = 0; 72 | int n; 73 | int bytes_recd = 0; 74 | 75 | do { 76 | n = recv(sock, buf, buflen, 0); 77 | if (n < 0) { 78 | //this will abort in the event of an error or in the buffer is filled before the terminator is reached 79 | return n; 80 | } 81 | else if (n == 0) { 82 | // The socket is closed. 83 | return 0; 84 | } 85 | 86 | buf += n; 87 | buflen -= n; 88 | bytes_recd += n; 89 | } while(buf[-1] != line_terminator); 90 | 91 | buf[bytes_recd] = '\0'; 92 | 93 | return bytes_recd; 94 | } 95 | 96 | int ftl_get_hmac(SOCKET sock, char * auth_key, char * dst) { 97 | char buf[2048]; 98 | int string_len; 99 | int response_code; 100 | 101 | send(sock, "HMAC\r\n\r\n", 8, 0); 102 | string_len = recv_all(sock, buf, 2048, '\n'); 103 | if (string_len < 4 || string_len == 2048) { 104 | return 0; 105 | } 106 | 107 | response_code = ftl_read_response_code(buf); 108 | if (response_code != FTL_INGEST_RESP_OK) { 109 | return 0; 110 | } 111 | 112 | int len = string_len - 5; // Strip "200 " and "\n" 113 | if (len % 2) { 114 | return 0; 115 | } 116 | 117 | int messageLen = len / 2; 118 | unsigned char *msg; 119 | 120 | if( (msg = (unsigned char*)malloc(messageLen * sizeof(*msg))) == NULL){ 121 | return 0; 122 | } 123 | 124 | int i; 125 | const char *hexMsgBuf = buf + 4; 126 | for(i = 0; i < messageLen; i++) { 127 | msg[i] = (decode_hex_char(hexMsgBuf[i * 2]) << 4) + 128 | decode_hex_char(hexMsgBuf[(i * 2) + 1]); 129 | } 130 | 131 | hmacsha512(auth_key, msg, messageLen, dst); 132 | free(msg); 133 | return 1; 134 | } 135 | 136 | const char * ftl_audio_codec_to_string(ftl_audio_codec_t codec) { 137 | switch (codec) { 138 | case FTL_AUDIO_NULL: return ""; 139 | case FTL_AUDIO_OPUS: return "OPUS"; 140 | case FTL_AUDIO_AAC: return "AAC"; 141 | } 142 | 143 | // Should be never reached 144 | return ""; 145 | } 146 | 147 | const char * ftl_video_codec_to_string(ftl_video_codec_t codec) { 148 | switch (codec) { 149 | case FTL_VIDEO_NULL: return ""; 150 | case FTL_VIDEO_VP8: return "VP8"; 151 | case FTL_VIDEO_H264: return "H264"; 152 | } 153 | 154 | // Should be never reached 155 | return ""; 156 | } 157 | 158 | void ftl_set_state(ftl_stream_configuration_private_t *ftl, ftl_state_t state) { 159 | os_lock_mutex(&ftl->state_mutex); 160 | ftl->state |= state; 161 | os_unlock_mutex(&ftl->state_mutex); 162 | } 163 | 164 | void ftl_clear_state(ftl_stream_configuration_private_t *ftl, ftl_state_t state) { 165 | os_lock_mutex(&ftl->state_mutex); 166 | ftl->state &= ~state; 167 | os_unlock_mutex(&ftl->state_mutex); 168 | } 169 | 170 | BOOL ftl_get_state(ftl_stream_configuration_private_t *ftl, ftl_state_t state) { 171 | BOOL is_set; 172 | 173 | os_lock_mutex(&ftl->state_mutex); 174 | is_set = ftl->state & state; 175 | os_unlock_mutex(&ftl->state_mutex); 176 | 177 | return is_set; 178 | 179 | } 180 | 181 | BOOL is_legacy_ingest(ftl_stream_configuration_private_t *ftl) { 182 | return ftl->media.assigned_port == FTL_UDP_MEDIA_PORT; 183 | } 184 | 185 | ftl_status_t enqueue_status_msg(ftl_stream_configuration_private_t *ftl, ftl_status_msg_t *stats_msg) { 186 | status_queue_elmt_t *elmt; 187 | ftl_status_t retval = FTL_SUCCESS; 188 | 189 | os_lock_mutex(&ftl->status_q.mutex); 190 | 191 | if ( (elmt = (status_queue_elmt_t*)malloc(sizeof(status_queue_elmt_t))) == NULL) { 192 | fprintf(stderr, "Unable to allocate status msg"); 193 | return FTL_MALLOC_FAILURE; 194 | } 195 | 196 | memcpy(&elmt->stats_msg, stats_msg, sizeof(status_queue_elmt_t)); 197 | elmt->next = NULL; 198 | 199 | if (ftl->status_q.head == NULL) { 200 | ftl->status_q.head = elmt; 201 | } 202 | else { 203 | status_queue_elmt_t *tail = ftl->status_q.head; 204 | 205 | do { 206 | if (tail->next == NULL) { 207 | tail->next = elmt; 208 | break; 209 | } 210 | tail = tail->next; 211 | } while (tail != NULL); 212 | } 213 | 214 | /*if queue is full remove head*/ 215 | if (ftl->status_q.count >= MAX_STATUS_MESSAGE_QUEUED) { 216 | elmt = ftl->status_q.head; 217 | ftl->status_q.head = elmt->next; 218 | free(elmt); 219 | retval = FTL_QUEUE_FULL; 220 | } 221 | else { 222 | ftl->status_q.count++; 223 | os_semaphore_post(&ftl->status_q.sem); 224 | } 225 | 226 | os_unlock_mutex(&ftl->status_q.mutex); 227 | return retval; 228 | } 229 | 230 | ftl_status_t dequeue_status_msg(ftl_stream_configuration_private_t *ftl, ftl_status_msg_t *stats_msg, int ms_timeout) { 231 | status_queue_elmt_t *elmt; 232 | ftl_status_t retval = FTL_SUCCESS; 233 | 234 | if (!ftl_get_state(ftl, FTL_STATUS_QUEUE)) { 235 | return FTL_NOT_INITIALIZED; 236 | } 237 | 238 | ftl->status_q.thread_waiting = 1; 239 | 240 | if (os_semaphore_pend(&ftl->status_q.sem, ms_timeout) < 0) { 241 | return FTL_STATUS_TIMEOUT; 242 | } 243 | 244 | os_lock_mutex(&ftl->status_q.mutex); 245 | 246 | if (ftl->status_q.head != NULL) { 247 | elmt = ftl->status_q.head; 248 | memcpy(stats_msg, &elmt->stats_msg, sizeof(elmt->stats_msg)); 249 | ftl->status_q.head = elmt->next; 250 | free(elmt); 251 | ftl->status_q.count--; 252 | } 253 | else { 254 | retval = FTL_QUEUE_EMPTY; 255 | } 256 | 257 | os_unlock_mutex(&ftl->status_q.mutex); 258 | 259 | ftl->status_q.thread_waiting = 0; 260 | 261 | return retval; 262 | } 263 | 264 | ftl_status_t _set_ingest_hostname(ftl_stream_configuration_private_t *ftl) { 265 | 266 | ftl_status_t ret_status = FTL_SUCCESS; 267 | 268 | do { 269 | #ifndef DISABLE_AUTO_INGEST 270 | if (strcmp(ftl->param_ingest_hostname, "auto") == 0) { 271 | ftl->ingest_hostname = ingest_find_best(ftl); 272 | } 273 | else 274 | #endif 275 | ftl->ingest_hostname = _strdup(ftl->param_ingest_hostname); 276 | } while (0); 277 | 278 | return ret_status; 279 | } 280 | 281 | int _get_remote_ip(struct sockaddr *addr, size_t addrlen, char *remote_ip, size_t ip_len) { 282 | if (addr->sa_family == AF_INET) 283 | { 284 | struct sockaddr_in *ipv4_addr = (struct sockaddr_in *)addr; 285 | 286 | if (inet_ntop(AF_INET, &ipv4_addr->sin_addr.s_addr, remote_ip, ip_len) == NULL) { 287 | return -1; 288 | } 289 | } 290 | else if (addr->sa_family == AF_INET6) { 291 | struct sockaddr_in6 *ipv6_addr = (struct sockaddr_in6 *)addr; 292 | 293 | if (inet_ntop(AF_INET6, &ipv6_addr->sin6_addr.s6_addr, remote_ip, ip_len) == NULL) { 294 | return -1; 295 | } 296 | } 297 | 298 | return 0; 299 | } 300 | -------------------------------------------------------------------------------- /libftl/ftl_private.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file ftl_private.h - Private Interfaces for the FTL SDK 3 | * 4 | * Copyright (c) 2015 Mixer Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #ifndef __FTL_PRIVATE_H 26 | #define __FTL_PRIVATE_H 27 | 28 | #define __STDC_WANT_LIB_EXT1__ 1 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "gettimeofday/gettimeofday.h" 36 | 37 | #ifdef _WIN32 38 | #include 39 | #include 40 | #else 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #endif 48 | 49 | #include "threads.h" 50 | #include "socket.h" 51 | 52 | 53 | #define MAX_INGEST_COMMAND_LEN 512 54 | #define INGEST_PORT 8084 55 | #define MAX_KEY_LEN 100 56 | #define VIDEO_PTYPE 96 57 | #define AUDIO_PTYPE 97 58 | #define SOCKET_RECV_TIMEOUT_MS 5000 59 | #define SOCKET_SEND_TIMEOUT_MS 1000 60 | #define KEEPALIVE_FREQUENCY_MS 5000 61 | #define KEEPALIVE_SEND_WARN_TOLERANCE_MS 1000 62 | #define STATUS_THREAD_SLEEP_TIME_MS 500 63 | #define MAX_PACKET_BUFFER 1500 //Max length of buffer 64 | #define MAX_MTU 1392 65 | #define FTL_UDP_MEDIA_PORT 8082 //legacy port 66 | #define RTP_HEADER_BASE_LEN 12 67 | #define RTP_FUA_HEADER_LEN 2 68 | #define NACK_RB_SIZE (2048) //must be evenly divisible by 2^16 69 | #define NACK_RTT_AVG_SECONDS 5 70 | #define MAX_STATUS_MESSAGE_QUEUED 10 71 | #define MAX_FRAME_SIZE_ELEMENTS 64 //must be a minimum of 3 72 | #define MAX_XMIT_LEVEL_IN_MS 100 //allows a maximum burst size of 100ms at the target bitrate 73 | #define VIDEO_RTP_TS_CLOCK_HZ 90000 74 | #define AUDIO_SAMPLE_RATE 48000 75 | #define AUDIO_PACKET_DURATION_MS 20 76 | #define IPVX_ADDR_ASCII_LEN INET6_ADDRSTRLEN 77 | #define INGEST_LIST_URI "https://conductor.videosvc.mixer.com/api/video/v2/channels/%d/ingest" 78 | #define INGEST_LOAD_PORT 8079 79 | #define INGEST_PING_PORT 8079 80 | #define PEAK_BITRATE_KBPS 10000 /*if not supplied this is the peak from the perspective of the send buffer*/ 81 | #define PING_TX_INTERVAL_MS 25 82 | #define SENDER_REPORT_TX_INTERVAL_MS 1000 83 | #define PING_PTYPE 250 84 | #define SENDER_REPORT_PTYPE 200 85 | 86 | // Adaptive bitrate constants 87 | 88 | // If the ratio of nacks received to packets sent is greater than the following value, we request a bitrate downgrade. 89 | #define MIN_NACKS_RECEIVED_TO_PACKETS_SENT_RATIO_FOR_BITRATE_DOWNGRADE 0.1 90 | 91 | // Duration at which we capture stream stats , i.e frames sent and nacks received 92 | #define STREAM_STATS_CAPTURE_MS 1000 93 | 94 | // Duration over which we evaluate whether we need to downgrade/upgrade bitrate. Note that we 95 | // look at stats over the last c_ulBwCheckDurationMs milliseconds, every c_ulStreamStatsCaptureMs milliseconds. 96 | #define BW_CHECK_DURATION_MS 5000 97 | 98 | // Interval to wait for bw test after we update bitrate. 99 | #define BITRATE_CHANGED_COOLDOWN_INTERVAL_MS 10000 100 | 101 | // Percentage to reduce the bitrate to if bandwidth seems too constrained 102 | #define BW_INSUFFICIENT_BITRATE_DOWNGRADE_PERCENTAGE 50 103 | 104 | // Percentange to reduce the bitrate to if bw upgrade was too excessive 105 | #define REVERT_TO_STABLE_BITRATE_DOWNGRADE_PERCENTAGE 80 106 | 107 | // Percentage to increase bitrate by if conditions look ideal 108 | #define BW_IDEAL_BITRATE_UPGRADE_BPS 256000; 109 | 110 | // If ratio of nacks received to packets sent is below the following value bitrate update can be requested 111 | #define MAX_NACKS_RECEIVED_TO_PACKETS_SENT_RATIO_FORBITRATE_UPGRADE 0.01 112 | 113 | // If bandwidth is constrained within MaxBitrateUpgradeExcessiveSeconds after bitrate update, revert to last stable bitrate. 114 | #define MAX_MS_TO_DEEM_UPGRADE_EXCESSIVE 60000 115 | 116 | #define MAX_AVG_RTT_TO_DEEM_BW_STABLE 100 117 | 118 | #define MAX_QUEUE_FULLNESS_TO_DEEM_BW_STABLE 0.1 119 | 120 | #define MIN_QUEUE_FULLNESS_TO_DEEM_BW_CONSTRAINED 0.3 121 | 122 | #define MIN_AVG_RTT_TO_DEEM_BW_CONSTRAINED 300 123 | // If bitrate upgrade was excessive we freeze bitrate upgrade for the next c_bitrateUpgradeFreezeTimeMs milliseconds. 124 | #define BITRATE_UPGRADE_FREEZE_TIME_MS 600000 // 10*60*1000 125 | 126 | #define MAX_STAT_SIZE 5 127 | 128 | #ifndef _WIN32 129 | #define strncpy_s(dst, dstsz, src, cnt) strncpy(dst, src, cnt) 130 | #define sprintf_s(buf, bufsz, fmt, ...) sprintf(buf, fmt, __VA_ARGS__) 131 | #define strcpy_s(dst, dstsz, src) strcpy(dst, src) 132 | #define _strdup(src) strdup(src) 133 | #define sscanf_s sscanf 134 | #endif 135 | 136 | typedef enum { 137 | H264_NALU_TYPE_NON_IDR = 1, 138 | H264_NALU_TYPE_IDR = 5, 139 | H264_NALU_TYPE_SEI = 6, 140 | H264_NALU_TYPE_SPS = 7, 141 | H264_NALU_TYPE_PPS = 8, 142 | H264_NALU_TYPE_DELIM = 9, 143 | H264_NALU_TYPE_FILLER = 12 144 | }h264_nalu_type_t; 145 | 146 | typedef enum { 147 | FTL_CONNECTED = 0x0001, 148 | FTL_MEDIA_READY = 0x0002, 149 | FTL_STATUS_QUEUE = 0x0004, 150 | FTL_CXN_STATUS_THRD = 0x0008, 151 | FTL_KEEPALIVE_THRD = 0x0010, 152 | FTL_PING_THRD = 0x0020, 153 | FTL_RX_THRD = 0x0040, 154 | FTL_TX_THRD = 0x0080, 155 | FTL_DISABLE_TX_PING_PKTS = 0x0100, 156 | FTL_SPEED_TEST = 0x0200, 157 | FTL_BITRATE_THRD = 0x0400, 158 | FTL_DISCONNECT_IN_PROGRESS = 0x1000, 159 | FTL_DISABLE_TX_SENDER_REPORT = 0x2000 160 | }ftl_state_t; 161 | 162 | #ifndef _WIN32 163 | typedef int SOCKET; 164 | typedef bool BOOL; 165 | #define TRUE true 166 | #define FALSE false 167 | #define INVALID_SOCKET (-1) 168 | #define SOCKET_ERROR (-1) 169 | #endif 170 | 171 | /*status message queue*/ 172 | typedef struct _status_queue_t { 173 | ftl_status_msg_t stats_msg; 174 | struct _status_queue_t *next; 175 | }status_queue_elmt_t; 176 | 177 | typedef struct { 178 | status_queue_elmt_t *head; 179 | int count; 180 | int thread_waiting; 181 | OS_MUTEX mutex; 182 | OS_SEMAPHORE sem; 183 | }status_queue_t; 184 | 185 | /** 186 | * This configuration structure handles basic information for a struct such 187 | * as the authetication keys and other similar information. It's members are 188 | * private and not to be directly manipulated 189 | */ 190 | typedef struct { 191 | uint8_t packet[MAX_PACKET_BUFFER]; 192 | int len; 193 | struct timeval insert_time; 194 | struct timeval xmit_time; 195 | int sn; 196 | int first;/*first packet in frame*/ 197 | int last; /*last packet in frame*/ 198 | OS_MUTEX mutex; 199 | BOOL isPartOfIframe; 200 | }nack_slot_t; 201 | 202 | typedef struct _ping_pkt_t { 203 | uint32_t header; 204 | struct timeval xmit_time; 205 | }ping_pkt_t; 206 | 207 | typedef struct _senderReport_pkt_t { 208 | uint32_t header; 209 | uint32_t ssrc; 210 | uint32_t ntpTimestampHigh; 211 | uint32_t ntpTimestampLow; 212 | uint32_t rtpTimestamp; 213 | uint32_t senderPacketCount; 214 | uint32_t senderOctetCount; 215 | }senderReport_pkt_t; 216 | 217 | typedef struct { 218 | struct timeval start_time; 219 | int64_t frames_received; 220 | int64_t frames_sent; 221 | int64_t bw_throttling_count; 222 | int64_t bytes_queued; 223 | int64_t packets_queued; 224 | int64_t bytes_sent; 225 | int64_t payload_bytes_sent; 226 | int64_t packets_sent; 227 | int64_t late_packets; 228 | int64_t lost_packets; 229 | int64_t nack_requests; 230 | int64_t dropped_frames; 231 | int pkt_xmit_delay_max; 232 | int pkt_xmit_delay_min; 233 | int total_xmit_delay; 234 | int xmit_delay_samples; 235 | int pkt_rtt_max; 236 | int pkt_rtt_min; 237 | int total_rtt; 238 | int rtt_samples; 239 | int current_frame_size; 240 | int max_frame_size; 241 | }media_stats_t; 242 | 243 | typedef struct { 244 | uint8_t payload_type; 245 | uint32_t ssrc; 246 | uint32_t timestamp; 247 | int timestamp_clock; 248 | uint64_t timestamp_dts_usec; 249 | int64_t base_dts_usec; 250 | int64_t randomOffset; 251 | uint16_t seq_num; 252 | uint16_t tmp_seq_num; // used for stats only 253 | BOOL nack_enabled; 254 | int64_t min_nack_rtt; 255 | int64_t max_nack_rtt; 256 | int64_t nack_rtt_avg; 257 | BOOL nack_slots_initalized; 258 | int producer; 259 | int consumer; 260 | uint16_t xmit_seq_num; 261 | nack_slot_t *nack_slots[NACK_RB_SIZE]; 262 | OS_MUTEX nack_slots_lock; 263 | int peak_kbps; 264 | int kbps; 265 | media_stats_t stats; //cumulative since start of stream 266 | OS_SEMAPHORE pkt_ready; 267 | }ftl_media_component_common_t; 268 | 269 | typedef struct { 270 | ftl_audio_codec_t codec; 271 | int64_t dts_usec; 272 | ftl_media_component_common_t media_component; 273 | OS_MUTEX mutex; 274 | BOOL is_ready_to_send; 275 | } ftl_audio_component_t; 276 | 277 | typedef struct { 278 | ftl_video_codec_t codec; 279 | uint32_t height; 280 | uint32_t width; 281 | int fps_num; 282 | int fps_den; 283 | int64_t dts_usec; 284 | float dts_error; 285 | uint8_t fua_nalu_type; 286 | BOOL wait_for_idr_frame; 287 | ftl_media_component_common_t media_component; 288 | OS_MUTEX mutex; 289 | BOOL has_sent_first_frame; 290 | } ftl_video_component_t; 291 | 292 | typedef struct { 293 | size_t ingest_addrlen; 294 | struct sockaddr *ingest_addr; 295 | SOCKET media_socket; 296 | OS_MUTEX mutex; 297 | int assigned_port; 298 | OS_THREAD_HANDLE recv_thread; 299 | OS_THREAD_HANDLE video_send_thread; 300 | OS_THREAD_HANDLE audio_send_thread; 301 | OS_THREAD_HANDLE ping_thread; 302 | OS_SEMAPHORE ping_thread_shutdown; 303 | int max_mtu; 304 | struct timeval stats_tv; 305 | int last_rtt_delay; 306 | struct timeval sender_report_base_ntp; 307 | } ftl_media_config_t; 308 | 309 | typedef struct _ftl_ingest_t { 310 | char *name; 311 | int rtt; 312 | struct _ftl_ingest_t *next; 313 | }ftl_ingest_t; 314 | 315 | typedef struct 316 | { 317 | ftl_handle_t* handle; 318 | BOOL(*change_bitrate_callback)(void*, uint64_t); 319 | void* context; 320 | uint64_t initial_encoding_bitrate; 321 | uint64_t max_encoding_bitrate; 322 | uint64_t min_encoding_bitrate; 323 | } ftl_adaptive_bitrate_thread_params_t; 324 | 325 | typedef struct { 326 | SOCKET ingest_socket; 327 | ftl_state_t state; 328 | OS_MUTEX state_mutex; 329 | OS_MUTEX disconnect_mutex; 330 | char *param_ingest_hostname; 331 | char *ingest_hostname; 332 | char *ingest_ip; 333 | short socket_family; 334 | uint32_t channel_id; 335 | char *key; 336 | char hmacBuffer[512]; 337 | int video_kbps; 338 | char vendor_name[50]; 339 | char vendor_version[50]; 340 | OS_THREAD_HANDLE connection_thread; 341 | OS_THREAD_HANDLE keepalive_thread; 342 | OS_THREAD_HANDLE bitrate_monitor_thread; 343 | OS_SEMAPHORE connection_thread_shutdown; 344 | OS_SEMAPHORE keepalive_thread_shutdown; 345 | OS_SEMAPHORE bitrate_thread_shutdown; 346 | ftl_media_config_t media; 347 | ftl_audio_component_t audio; 348 | ftl_video_component_t video; 349 | status_queue_t status_q; 350 | ftl_ingest_t *ingest_list; 351 | int ingest_count; 352 | } ftl_stream_configuration_private_t; 353 | 354 | struct MemoryStruct { 355 | char *memory; 356 | size_t size; 357 | }; 358 | 359 | /** 360 | * Charon always responses with a three digit response code after each command 361 | * 362 | * This enum holds defined number sequences 363 | **/ 364 | 365 | typedef enum { 366 | FTL_INGEST_RESP_UNKNOWN = 0, 367 | FTL_INGEST_RESP_OK = 200, 368 | FTL_INGEST_RESP_PING = 201, 369 | FTL_INGEST_RESP_BAD_REQUEST= 400, // The handshake was not formatted correctly 370 | FTL_INGEST_RESP_UNAUTHORIZED = 401, // This channel id is not authorized to stream 371 | FTL_INGEST_RESP_OLD_VERSION = 402, // This ftl api version is no longer supported 372 | FTL_INGEST_RESP_AUDIO_SSRC_COLLISION = 403, 373 | FTL_INGEST_RESP_VIDEO_SSRC_COLLISION = 404, 374 | FTL_INGEST_RESP_INVALID_STREAM_KEY = 405, // The corresponding channel does not match this key 375 | FTL_INGEST_RESP_CHANNEL_IN_USE = 406, // The channel ID successfully authenticated however it is already actively streaming 376 | FTL_INGEST_RESP_REGION_UNSUPPORTED = 407, // Streaming from this country or region is not authorized by local governments 377 | FTL_INGEST_RESP_NO_MEDIA_TIMEOUT = 408, 378 | FTL_INGEST_RESP_GAME_BLOCKED = 409, // The game the user account is set to can't be streamed. 379 | FTL_INGEST_RESP_SERVER_TERMINATE = 410, // The sterver has terminated the stream. 380 | FTL_INGEST_RESP_INTERNAL_SERVER_ERROR = 500, 381 | FTL_INGEST_RESP_INTERNAL_MEMORY_ERROR = 900, 382 | FTL_INGEST_RESP_INTERNAL_COMMAND_ERROR = 901, 383 | FTL_INGEST_RESP_INTERNAL_SOCKET_CLOSED = 902, 384 | FTL_INGEST_RESP_INTERNAL_SOCKET_TIMEOUT = 903 385 | } ftl_response_code_t; 386 | 387 | /** 388 | * Logs something to the FTL logs 389 | */ 390 | 391 | #define FTL_LOG(ftl_handle, log_level, ...) ftl_log_msg (ftl_handle, log_level, __FILE__, __LINE__, __VA_ARGS__); 392 | void ftl_log_msg(ftl_stream_configuration_private_t *ftl, ftl_log_severity_t log_level, const char * file, int lineno, const char * fmt, ...); 393 | 394 | /** 395 | * Value to string conversion functions 396 | */ 397 | 398 | const char * ftl_audio_codec_to_string(ftl_audio_codec_t codec); 399 | const char * ftl_video_codec_to_string(ftl_video_codec_t codec); 400 | 401 | /** 402 | * Functions related to the charon prootocol itself 403 | **/ 404 | 405 | int recv_all(SOCKET sock, char * buf, int buflen, const char line_terminator); 406 | 407 | int ftl_get_hmac(SOCKET sock, char * auth_key, char * dst); 408 | int ftl_read_response_code(const char * response_str); 409 | int ftl_read_media_port(const char *response_str); 410 | 411 | /** 412 | * Platform abstractions 413 | **/ 414 | 415 | // FIXME: make this less global 416 | extern char error_message[1000]; 417 | 418 | ftl_status_t _log_response(ftl_stream_configuration_private_t *ftl, int response_code); 419 | void ftl_set_state(ftl_stream_configuration_private_t *ftl, ftl_state_t state); 420 | void ftl_clear_state(ftl_stream_configuration_private_t *ftl, ftl_state_t state); 421 | BOOL ftl_get_state(ftl_stream_configuration_private_t *ftl, ftl_state_t state); 422 | BOOL is_legacy_ingest(ftl_stream_configuration_private_t *ftl); 423 | ftl_status_t dequeue_status_msg(ftl_stream_configuration_private_t *ftl, ftl_status_msg_t *stats_msg, int ms_timeout); 424 | ftl_status_t enqueue_status_msg(ftl_stream_configuration_private_t *ftl, ftl_status_msg_t *stats_msg); 425 | ftl_status_t _set_ingest_hostname(ftl_stream_configuration_private_t *ftl); 426 | int _get_remote_ip(struct sockaddr *addr, size_t addrlen, char *remote_ip, size_t ip_len); 427 | 428 | ftl_status_t _init_control_connection(ftl_stream_configuration_private_t *ftl); 429 | ftl_status_t _ingest_connect(ftl_stream_configuration_private_t *stream_config); 430 | ftl_status_t _ingest_disconnect(ftl_stream_configuration_private_t *stream_config); 431 | char * ingest_find_best(ftl_stream_configuration_private_t *ftl); 432 | void ingest_release(ftl_stream_configuration_private_t *ftl); 433 | 434 | ftl_status_t media_init(ftl_stream_configuration_private_t *ftl); 435 | ftl_status_t media_destroy(ftl_stream_configuration_private_t *ftl); 436 | int media_send_video(ftl_stream_configuration_private_t *ftl, int64_t dts_usec, uint8_t *data, int32_t len, int end_of_frame); 437 | int media_send_audio(ftl_stream_configuration_private_t *ftl, int64_t dts_usec, uint8_t *data, int32_t len); 438 | ftl_status_t media_speed_test(ftl_stream_configuration_private_t *ftl, int speed_kbps, int duration_ms, speed_test_t *results); 439 | ftl_status_t internal_ingest_disconnect(ftl_stream_configuration_private_t *ftl); 440 | ftl_status_t internal_ftl_ingest_destroy(ftl_stream_configuration_private_t *ftl); 441 | void sleep_ms(int ms); 442 | 443 | #if defined(_MSC_VER) && _MSC_VER < 1900 444 | #define snprintf _snprintf 445 | #endif 446 | 447 | #endif 448 | -------------------------------------------------------------------------------- /libftl/gettimeofday/gettimeofday.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gettimeofday.c 3 | * Win32 gettimeofday() replacement 4 | * 5 | * src/port/gettimeofday.c 6 | * 7 | * Copyright (c) 2003 SRA, Inc. 8 | * Copyright (c) 2003 SKC, Inc. 9 | * 10 | * Permission to use, copy, modify, and distribute this software and 11 | * its documentation for any purpose, without fee, and without a 12 | * written agreement is hereby granted, provided that the above 13 | * copyright notice and this paragraph and the following two 14 | * paragraphs appear in all copies. 15 | * 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, 17 | * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING 18 | * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS 19 | * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED 20 | * OF THE POSSIBILITY OF SUCH DAMAGE. 21 | * 22 | * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS 25 | * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, 26 | * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 27 | */ 28 | 29 | #include 30 | #include "gettimeofday.h" 31 | 32 | #define NSEC_IN_SEC 1000000000 33 | #define USEC_IN_SEC 1000000 34 | #define MSEC_IN_SEC 1000 35 | #define MSEC_IN_USEC 1000 36 | #define MSEC_IN_NSEC 1000000 37 | 38 | #define USEC_TO_SEC(x) ((x) / USEC_IN_SEC) 39 | #define USEC_TO_MSEC(x) ((x) / MSEC_IN_USEC) 40 | #define MSEC_TO_SEC(x) ((x) / MSEC_IN_SEC) 41 | #define MSEC_TO_USEC(x) (((uint64_t)x) * MSEC_IN_USEC) 42 | #define MSEC_TO_NSEC(x) (((uint64_t)x) * MSEC_IN_NSEC) 43 | #define SEC_TO_USEC(x) (((uint64_t)x) * USEC_IN_SEC) 44 | #define SEC_TO_NSEC(x) (((uint64_t)x) * NSEC_IN_SEC) 45 | 46 | #ifdef _WIN32 47 | #include 48 | 49 | /* FILETIME of Jan 1 1970 00:00:00. */ 50 | static const unsigned __int64 epoch = ((unsigned __int64)116444736000000000ULL); 51 | 52 | /* 53 | * timezone information is stored outside the kernel so tzp isn't used anymore. 54 | * 55 | * Note: this function is not for Win32 high precision timing purpose. See 56 | * elapsed_time(). 57 | */ 58 | int gettimeofday(struct timeval * tp, struct timezone * tzp) 59 | { 60 | FILETIME file_time; 61 | SYSTEMTIME system_time; 62 | ULARGE_INTEGER ularge; 63 | 64 | GetSystemTime(&system_time); 65 | SystemTimeToFileTime(&system_time, &file_time); 66 | ularge.LowPart = file_time.dwLowDateTime; 67 | ularge.HighPart = file_time.dwHighDateTime; 68 | 69 | tp->tv_sec = (long)((ularge.QuadPart - epoch) / 10000000L); 70 | tp->tv_usec = (long)(system_time.wMilliseconds * 1000); 71 | 72 | return 0; 73 | } 74 | #else 75 | void timespec_add_ms(struct timespec *ts, int ms) { 76 | long ns_adjust; 77 | time_t sec_adjust; 78 | 79 | sec_adjust = MSEC_TO_SEC((time_t)ms); 80 | ns_adjust = MSEC_TO_NSEC((long)ms); 81 | 82 | ns_adjust -= SEC_TO_NSEC((long)sec_adjust); 83 | 84 | ts->tv_sec += sec_adjust; 85 | ts->tv_nsec += ns_adjust; 86 | 87 | if(ts->tv_nsec >= NSEC_IN_SEC) { 88 | ts->tv_nsec -= NSEC_IN_SEC; 89 | ts->tv_sec++; 90 | } 91 | } 92 | #endif // _WIN32 93 | 94 | //result = end - start 95 | int timeval_subtract(struct timeval *result, const struct timeval *end, const struct timeval *start) 96 | { 97 | int64_t differenceUs; 98 | 99 | differenceUs = timeval_subtract_to_us(end, start); 100 | 101 | // Get the number of seconds 102 | result->tv_sec = (long)USEC_TO_SEC(differenceUs); 103 | 104 | // Put the remainder NS into the second value. 105 | result->tv_usec = (long)(differenceUs - SEC_TO_USEC(result->tv_sec)); 106 | 107 | /* Return 1 if result is negative. */ 108 | return differenceUs < 0; 109 | } 110 | 111 | //result = end - start 112 | int64_t timeval_subtract_to_ms(const struct timeval *end, const struct timeval *start) 113 | { 114 | return USEC_TO_MSEC(timeval_subtract_to_us(end, start)); 115 | } 116 | 117 | int64_t timeval_subtract_to_us(const struct timeval *end, const struct timeval *start) 118 | { 119 | int64_t s, e, d; 120 | 121 | s = (int64_t)SEC_TO_USEC(start->tv_sec) + (int64_t)start->tv_usec; 122 | e = (int64_t)SEC_TO_USEC(end->tv_sec) + (int64_t)end->tv_usec; 123 | 124 | d = e - s; 125 | 126 | return d; 127 | } 128 | 129 | void timeval_add_ms(struct timeval *tv, int ms) 130 | { 131 | timeval_add_us(tv, MSEC_TO_USEC(ms)); 132 | } 133 | 134 | void timeval_add_us(struct timeval *tv, uint64_t us) 135 | { 136 | struct timeval add_timeval; 137 | us_to_timeval(&add_timeval, us); 138 | 139 | tv->tv_sec += add_timeval.tv_sec; 140 | tv->tv_usec += add_timeval.tv_usec; 141 | 142 | if (tv->tv_usec >= USEC_IN_SEC) { 143 | tv->tv_usec -= USEC_IN_SEC; 144 | tv->tv_sec++; 145 | } 146 | } 147 | 148 | float timeval_to_ms(struct timeval *tv) { 149 | double sec, usec; 150 | 151 | sec = (double)tv->tv_sec; 152 | usec = (double)tv->tv_usec; 153 | 154 | return (float)(sec * 1000 + usec / 1000); 155 | } 156 | 157 | uint64_t timeval_to_us(struct timeval *tv) 158 | { 159 | return tv->tv_sec * (uint64_t)1000000 + tv->tv_usec; 160 | } 161 | 162 | uint64_t timeval_to_ntp(struct timeval * tv) { 163 | uint64_t ntpts; 164 | 165 | ntpts = (uint64_t)((((uint64_t)tv->tv_sec + 2208988800u) << 32) + ((uint32_t)tv->tv_usec * 4294.967296)); 166 | 167 | return (ntpts); 168 | } 169 | 170 | void us_to_timeval(struct timeval *outputTimeVal, const int64_t inputTimeUs) 171 | { 172 | outputTimeVal->tv_sec = (long)USEC_TO_SEC(inputTimeUs); 173 | outputTimeVal->tv_usec = (long)(inputTimeUs - SEC_TO_USEC(outputTimeVal->tv_sec)); 174 | } 175 | 176 | int64_t get_ms_elapsed_since(struct timeval *tv) 177 | { 178 | struct timeval now; 179 | gettimeofday(&now, NULL); 180 | return timeval_subtract_to_ms(&now, tv); 181 | } 182 | -------------------------------------------------------------------------------- /libftl/gettimeofday/gettimeofday.h: -------------------------------------------------------------------------------- 1 | #ifndef __GETTIMEOFDAY_H 2 | #define __GETTIMEOFDAY_H 3 | 4 | #include 5 | 6 | #ifndef _WIN32 7 | #include 8 | #endif 9 | 10 | #ifdef _WIN32 11 | int gettimeofday(struct timeval * tp, struct timezone * tzp); 12 | #endif 13 | int timeval_subtract(struct timeval *result, const struct timeval *end, const struct timeval *start); 14 | int64_t timeval_subtract_to_ms(const struct timeval *end, const struct timeval *start); 15 | int64_t timeval_subtract_to_us(const struct timeval *end, const struct timeval *start); 16 | void timeval_add_ms(struct timeval *tv, int ms); 17 | void timespec_add_ms(struct timespec *tv, int ms); 18 | void timeval_add_us(struct timeval *tv, uint64_t us); 19 | float timeval_to_ms(struct timeval *tv); 20 | uint64_t timeval_to_us(struct timeval *tv); 21 | uint64_t timeval_to_ntp(struct timeval *tv); 22 | void us_to_timeval(struct timeval *outputTimeVal, const int64_t inputTimeUs); 23 | int64_t get_ms_elapsed_since(struct timeval *tv); 24 | 25 | #endif // __GETTIMEOFDAY_H 26 | -------------------------------------------------------------------------------- /libftl/handshake.c: -------------------------------------------------------------------------------- 1 | /** 2 | * activate.c - Activates an FTL stream 3 | * 4 | * Copyright (c) 2015 Michael Casadevall 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #define __FTL_INTERNAL 26 | #include "ftl.h" 27 | #include "ftl_private.h" 28 | #include 29 | 30 | OS_THREAD_ROUTINE connection_status_thread(void *data); 31 | OS_THREAD_ROUTINE control_keepalive_thread(void *data); 32 | static ftl_response_code_t _ftl_get_response(ftl_stream_configuration_private_t *ftl, char *response_buf, int response_len); 33 | 34 | static ftl_response_code_t _ftl_send_command(ftl_stream_configuration_private_t *ftl_cfg, BOOL need_response, char *response_buf, int response_len, const char *cmd_fmt, ...); 35 | ftl_status_t _log_response(ftl_stream_configuration_private_t *ftl, int response_code); 36 | 37 | ftl_status_t _init_control_connection(ftl_stream_configuration_private_t *ftl) { 38 | int err = 0; 39 | SOCKET sock = 0; 40 | struct addrinfo hints; 41 | memset(&hints, 0, sizeof(hints)); 42 | ftl_status_t retval = FTL_SUCCESS; 43 | 44 | hints.ai_family = AF_UNSPEC; 45 | hints.ai_socktype = SOCK_STREAM; 46 | hints.ai_protocol = 0; 47 | 48 | char ingest_ip[IPVX_ADDR_ASCII_LEN]; 49 | 50 | struct addrinfo* resolved_names = 0; 51 | struct addrinfo* p = 0; 52 | 53 | int ingest_port = INGEST_PORT; 54 | char ingest_port_str[10]; 55 | 56 | if (ftl_get_state(ftl, FTL_CONNECTED)) { 57 | return FTL_ALREADY_CONNECTED; 58 | } 59 | 60 | snprintf(ingest_port_str, 10, "%d", ingest_port); 61 | 62 | if ((retval = _set_ingest_hostname(ftl)) != FTL_SUCCESS) { 63 | return retval; 64 | } 65 | 66 | err = getaddrinfo(ftl->ingest_hostname, ingest_port_str, &hints, &resolved_names); 67 | if (err != 0) { 68 | FTL_LOG(ftl, FTL_LOG_ERROR, "getaddrinfo failed to look up ingest address %s.", ftl->ingest_hostname); 69 | FTL_LOG(ftl, FTL_LOG_ERROR, "gai error was: %s", gai_strerror(err)); 70 | return FTL_DNS_FAILURE; 71 | } 72 | 73 | /* Open a socket to the control port */ 74 | for (p = resolved_names; p != NULL; p = p->ai_next) { 75 | sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); 76 | if (sock == -1) { 77 | /* try the next candidate */ 78 | FTL_LOG(ftl, FTL_LOG_DEBUG, "failed to create socket. error: %s", get_socket_error()); 79 | continue; 80 | } 81 | 82 | if (p->ai_family == AF_INET) { 83 | struct sockaddr_in *ipv4_addr = (struct sockaddr_in *)p->ai_addr; 84 | inet_ntop(p->ai_family, &ipv4_addr->sin_addr, ingest_ip, sizeof(ingest_ip)); 85 | } 86 | else if (p->ai_family == AF_INET6) { 87 | struct sockaddr_in6 *ipv6_addr = (struct sockaddr_in6 *)p->ai_addr; 88 | inet_ntop(p->ai_family, &ipv6_addr->sin6_addr, ingest_ip, sizeof(ingest_ip)); 89 | } 90 | else { 91 | continue; 92 | } 93 | 94 | FTL_LOG(ftl, FTL_LOG_DEBUG, "Got IP: %s\n", ingest_ip); 95 | ftl->ingest_ip = _strdup(ingest_ip); 96 | ftl->socket_family = p->ai_family; 97 | 98 | /* Go for broke */ 99 | if (connect(sock, p->ai_addr, (int)p->ai_addrlen) == -1) { 100 | FTL_LOG(ftl, FTL_LOG_DEBUG, "failed to connect on candidate, error: %s", get_socket_error()); 101 | close_socket(sock); 102 | sock = 0; 103 | continue; 104 | } 105 | 106 | /* If we got here, we successfully connected */ 107 | if (set_socket_enable_keepalive(sock) != 0) { 108 | FTL_LOG(ftl, FTL_LOG_DEBUG, "failed to enable keep alives. error: %s", get_socket_error()); 109 | } 110 | 111 | if (set_socket_recv_timeout(sock, SOCKET_RECV_TIMEOUT_MS) != 0) { 112 | FTL_LOG(ftl, FTL_LOG_DEBUG, "failed to set recv timeout. error: %s", get_socket_error()); 113 | } 114 | 115 | if (set_socket_send_timeout(sock, SOCKET_SEND_TIMEOUT_MS) != 0) { 116 | FTL_LOG(ftl, FTL_LOG_DEBUG, "failed to set send timeout. error: %s", get_socket_error()); 117 | } 118 | 119 | break; 120 | } 121 | 122 | /* Free the resolved name struct */ 123 | freeaddrinfo(resolved_names); 124 | 125 | /* Check to see if we actually connected */ 126 | if (sock <= 0) { 127 | FTL_LOG(ftl, FTL_LOG_ERROR, "failed to connect to ingest. Last error was: %s", 128 | get_socket_error()); 129 | return FTL_CONNECT_ERROR; 130 | } 131 | 132 | ftl->ingest_socket = sock; 133 | 134 | return FTL_SUCCESS; 135 | } 136 | 137 | ftl_status_t _ingest_connect(ftl_stream_configuration_private_t *ftl) { 138 | ftl_response_code_t response_code = FTL_INGEST_RESP_UNKNOWN; 139 | char response[MAX_INGEST_COMMAND_LEN]; 140 | 141 | if (ftl_get_state(ftl, FTL_CONNECTED)) { 142 | return FTL_ALREADY_CONNECTED; 143 | } 144 | 145 | if (ftl->ingest_socket <= 0) { 146 | return FTL_SOCKET_NOT_CONNECTED; 147 | } 148 | 149 | do { 150 | 151 | if (!ftl_get_hmac(ftl->ingest_socket, ftl->key, ftl->hmacBuffer)) { 152 | FTL_LOG(ftl, FTL_LOG_ERROR, "could not get a signed HMAC!"); 153 | response_code = FTL_INGEST_NO_RESPONSE; 154 | break; 155 | } 156 | 157 | if ((response_code = _ftl_send_command(ftl, TRUE, response, sizeof(response), "CONNECT %d $%s", ftl->channel_id, ftl->hmacBuffer)) != FTL_INGEST_RESP_OK) { 158 | break; 159 | } 160 | 161 | /* We always send our version component first */ 162 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "ProtocolVersion: %d.%d", FTL_VERSION_MAJOR, FTL_VERSION_MINOR)) != FTL_INGEST_RESP_OK) { 163 | break; 164 | } 165 | 166 | /* Cool. Now ingest wants our stream meta-data, which we send as key-value pairs, followed by a "." */ 167 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "VendorName: %s", ftl->vendor_name)) != FTL_INGEST_RESP_OK) { 168 | break; 169 | } 170 | 171 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "VendorVersion: %s", ftl->vendor_version)) != FTL_INGEST_RESP_OK) { 172 | break; 173 | } 174 | 175 | ftl_video_component_t *video = &ftl->video; 176 | /* We're sending video */ 177 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "Video: true")) != FTL_INGEST_RESP_OK) { 178 | break; 179 | } 180 | 181 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "VideoCodec: %s", ftl_video_codec_to_string(video->codec))) != FTL_INGEST_RESP_OK) { 182 | break; 183 | } 184 | 185 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "VideoHeight: %d", video->height)) != FTL_INGEST_RESP_OK) { 186 | break; 187 | } 188 | 189 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "VideoWidth: %d", video->width)) != FTL_INGEST_RESP_OK) { 190 | break; 191 | } 192 | 193 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "VideoPayloadType: %d", video->media_component.payload_type)) != FTL_INGEST_RESP_OK) { 194 | break; 195 | } 196 | 197 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "VideoIngestSSRC: %d", video->media_component.ssrc)) != FTL_INGEST_RESP_OK) { 198 | break; 199 | } 200 | 201 | ftl_audio_component_t *audio = &ftl->audio; 202 | 203 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "Audio: true")) != FTL_INGEST_RESP_OK) { 204 | break; 205 | } 206 | 207 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "AudioCodec: %s", ftl_audio_codec_to_string(audio->codec))) != FTL_INGEST_RESP_OK) { 208 | break; 209 | } 210 | 211 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "AudioPayloadType: %d", audio->media_component.payload_type)) != FTL_INGEST_RESP_OK) { 212 | break; 213 | } 214 | 215 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "AudioIngestSSRC: %d", audio->media_component.ssrc)) != FTL_INGEST_RESP_OK) { 216 | break; 217 | } 218 | 219 | if ((response_code = _ftl_send_command(ftl, TRUE, response, sizeof(response), ".")) != FTL_INGEST_RESP_OK) { 220 | break; 221 | } 222 | 223 | /*see if there is a port specified otherwise use default*/ 224 | int port = ftl_read_media_port(response); 225 | 226 | ftl->media.assigned_port = port; 227 | 228 | ftl_set_state(ftl, FTL_CONNECTED); 229 | 230 | if (os_semaphore_create(&ftl->connection_thread_shutdown, "/ConnectionThreadShutdown", O_CREAT, 0) < 0) { 231 | response_code = FTL_MALLOC_FAILURE; 232 | break; 233 | } 234 | 235 | if (os_semaphore_create(&ftl->keepalive_thread_shutdown, "/KeepAliveThreadShutdown", O_CREAT, 0) < 0) { 236 | response_code = FTL_MALLOC_FAILURE; 237 | break; 238 | } 239 | 240 | ftl_set_state(ftl, FTL_CXN_STATUS_THRD); 241 | if ((os_create_thread(&ftl->connection_thread, NULL, connection_status_thread, ftl)) != 0) { 242 | ftl_clear_state(ftl, FTL_CXN_STATUS_THRD); 243 | response_code = FTL_MALLOC_FAILURE; 244 | break; 245 | } 246 | 247 | ftl_set_state(ftl, FTL_KEEPALIVE_THRD); 248 | if ((os_create_thread(&ftl->keepalive_thread, NULL, control_keepalive_thread, ftl)) != 0) { 249 | ftl_clear_state(ftl, FTL_KEEPALIVE_THRD);\ 250 | response_code = FTL_MALLOC_FAILURE; 251 | break; 252 | } 253 | 254 | FTL_LOG(ftl, FTL_LOG_INFO, "Successfully connected to ingest. Media will be sent to port %d\n", ftl->media.assigned_port); 255 | 256 | return FTL_SUCCESS; 257 | } while (0); 258 | 259 | _ingest_disconnect(ftl); 260 | 261 | return _log_response(ftl, response_code);; 262 | } 263 | 264 | ftl_status_t _ingest_disconnect(ftl_stream_configuration_private_t *ftl) { 265 | 266 | ftl_response_code_t response_code = FTL_INGEST_RESP_UNKNOWN; 267 | char response[MAX_INGEST_COMMAND_LEN]; 268 | 269 | if (ftl_get_state(ftl, FTL_KEEPALIVE_THRD)) { 270 | ftl_clear_state(ftl, FTL_KEEPALIVE_THRD); 271 | os_semaphore_post(&ftl->keepalive_thread_shutdown); 272 | os_wait_thread(ftl->keepalive_thread); 273 | os_destroy_thread(ftl->keepalive_thread); 274 | os_semaphore_delete(&ftl->keepalive_thread_shutdown); 275 | } 276 | 277 | if (ftl_get_state(ftl, FTL_CXN_STATUS_THRD)) { 278 | ftl_clear_state(ftl, FTL_CXN_STATUS_THRD); 279 | os_semaphore_post(&ftl->connection_thread_shutdown); 280 | os_wait_thread(ftl->connection_thread); 281 | os_destroy_thread(ftl->connection_thread); 282 | os_semaphore_delete(&ftl->connection_thread_shutdown); 283 | } 284 | 285 | if (ftl_get_state(ftl, FTL_BITRATE_THRD)) 286 | { 287 | ftl_clear_state(ftl, FTL_BITRATE_THRD); 288 | os_semaphore_post(&ftl->bitrate_thread_shutdown); 289 | os_wait_thread(ftl->bitrate_monitor_thread); 290 | os_destroy_thread(ftl->bitrate_monitor_thread); 291 | os_semaphore_delete(&ftl->bitrate_thread_shutdown); 292 | } 293 | 294 | if (ftl_get_state(ftl, FTL_CONNECTED)) { 295 | 296 | ftl_clear_state(ftl, FTL_CONNECTED); 297 | 298 | FTL_LOG(ftl, FTL_LOG_INFO, "light-saber disconnect\n"); 299 | if ((response_code = _ftl_send_command(ftl, FALSE, response, sizeof(response), "DISCONNECT", ftl->channel_id)) != FTL_INGEST_RESP_OK) { 300 | FTL_LOG(ftl, FTL_LOG_ERROR, "Ingest Disconnect failed with %d (%s)\n", response_code, response); 301 | } 302 | } 303 | 304 | if (ftl->ingest_socket > 0) { 305 | close_socket(ftl->ingest_socket); 306 | ftl->ingest_socket = 0; 307 | } 308 | 309 | return FTL_SUCCESS; 310 | } 311 | 312 | static ftl_response_code_t _ftl_get_response(ftl_stream_configuration_private_t *ftl, char *response_buf, int response_len){ 313 | int len; 314 | memset(response_buf, 0, response_len); 315 | len = recv_all(ftl->ingest_socket, response_buf, response_len, '\n'); 316 | 317 | if (len <= 0) { 318 | 319 | #ifdef _WIN32 320 | int error = WSAGetLastError(); 321 | if (error == WSAETIMEDOUT) 322 | { 323 | return FTL_INGEST_RESP_INTERNAL_SOCKET_TIMEOUT; 324 | } 325 | else 326 | { 327 | return FTL_INGEST_RESP_INTERNAL_SOCKET_CLOSED; 328 | } 329 | #else 330 | if (len == 0) 331 | { 332 | return FTL_INGEST_RESP_INTERNAL_SOCKET_CLOSED; 333 | } 334 | else 335 | { 336 | return FTL_INGEST_RESP_INTERNAL_SOCKET_TIMEOUT; 337 | } 338 | #endif 339 | } 340 | 341 | return ftl_read_response_code(response_buf); 342 | } 343 | 344 | static ftl_response_code_t _ftl_send_command(ftl_stream_configuration_private_t *ftl, BOOL need_response, char *response_buf, int response_len, const char *cmd_fmt, ...) { 345 | int resp_code = FTL_INGEST_RESP_OK; 346 | va_list valist; 347 | char *buf = NULL; 348 | int len; 349 | int buflen = MAX_INGEST_COMMAND_LEN * sizeof(char); 350 | char *format = NULL; 351 | 352 | do { 353 | if ((buf = (char*)malloc(buflen)) == NULL) { 354 | resp_code = FTL_INGEST_RESP_INTERNAL_MEMORY_ERROR; 355 | break; 356 | } 357 | 358 | if ((format = (char*)malloc(strlen(cmd_fmt) + 5)) == NULL) { 359 | resp_code = FTL_INGEST_RESP_INTERNAL_MEMORY_ERROR; 360 | break; 361 | } 362 | 363 | sprintf_s(format, strlen(cmd_fmt) + 5, "%s\r\n\r\n", cmd_fmt); 364 | 365 | va_start(valist, cmd_fmt); 366 | 367 | memset(buf, 0, buflen); 368 | 369 | len = vsnprintf(buf, buflen, format, valist); 370 | 371 | va_end(valist); 372 | 373 | if (len < 0 || len >= buflen) { 374 | resp_code = FTL_INGEST_RESP_INTERNAL_COMMAND_ERROR; 375 | break; 376 | } 377 | 378 | send(ftl->ingest_socket, buf, len, 0); 379 | 380 | if (need_response) { 381 | resp_code = _ftl_get_response(ftl, response_buf, response_len); 382 | } 383 | } while (0); 384 | 385 | if(buf != NULL){ 386 | free(buf); 387 | } 388 | 389 | if(format != NULL){ 390 | free(format); 391 | } 392 | 393 | return resp_code; 394 | } 395 | 396 | OS_THREAD_ROUTINE control_keepalive_thread(void *data) 397 | { 398 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)data; 399 | ftl_response_code_t response_code; 400 | struct timeval last_send_time, now; 401 | int64_t ms_since_send = 0; 402 | 403 | gettimeofday(&last_send_time, NULL); 404 | 405 | while (ftl_get_state(ftl, FTL_KEEPALIVE_THRD)) { 406 | os_semaphore_pend(&ftl->keepalive_thread_shutdown, KEEPALIVE_FREQUENCY_MS); 407 | 408 | if (!ftl_get_state(ftl, FTL_KEEPALIVE_THRD)) 409 | { 410 | break; 411 | } 412 | 413 | // Check how long it has been since we sent a ping last. 414 | gettimeofday(&now, NULL); 415 | ms_since_send = timeval_subtract_to_ms(&now, &last_send_time); 416 | if (ms_since_send > KEEPALIVE_FREQUENCY_MS + KEEPALIVE_SEND_WARN_TOLERANCE_MS) 417 | { 418 | FTL_LOG(ftl, FTL_LOG_INFO, "Warning, ping time tolerance warning. Time since last ping %d ms", ms_since_send); 419 | } 420 | gettimeofday(&last_send_time, NULL); 421 | 422 | // Send the ping to ingest now. 423 | if ((response_code = _ftl_send_command(ftl, FALSE, NULL, 0, "PING %d", ftl->channel_id)) != FTL_INGEST_RESP_OK) { 424 | FTL_LOG(ftl, FTL_LOG_ERROR, "Ingest ping failed with %d\n", response_code); 425 | } 426 | } 427 | 428 | FTL_LOG(ftl, FTL_LOG_INFO, "Exited control_keepalive_thread\n"); 429 | 430 | return 0; 431 | } 432 | 433 | OS_THREAD_ROUTINE connection_status_thread(void *data) 434 | { 435 | ftl_stream_configuration_private_t *ftl = (ftl_stream_configuration_private_t *)data; 436 | char buf[1024]; 437 | ftl_status_msg_t status; 438 | struct timeval last_ping, now; 439 | int64_t ms_since_ping = 0; 440 | 441 | // We ping every 5 seconds, but don't timeout the connection until 30 seconds has passed 442 | // without hearing anything back from the ingest. This time is high, but some some poor networks 443 | // this can happen. 444 | int keepalive_is_late = 6 * KEEPALIVE_FREQUENCY_MS; 445 | 446 | gettimeofday(&last_ping, NULL); 447 | 448 | // Loop while the connection status thread should be alive. 449 | while (ftl_get_state(ftl, FTL_CXN_STATUS_THRD)) { 450 | 451 | // Wait on the shutdown event for at most STATUS_THREAD_SLEEP_TIME_MS 452 | os_semaphore_pend(&ftl->connection_thread_shutdown, STATUS_THREAD_SLEEP_TIME_MS); 453 | if (!ftl_get_state(ftl, FTL_CXN_STATUS_THRD)) 454 | { 455 | break; 456 | } 457 | 458 | ftl_status_t error_code = FTL_SUCCESS; 459 | 460 | // Check if there is any data for us to consume. 461 | unsigned long bytesAvailable = 0; 462 | int ret = get_socket_bytes_available(ftl->ingest_socket, &bytesAvailable); 463 | if (ret < 0) 464 | { 465 | FTL_LOG(ftl, FTL_LOG_ERROR, "Failed to call get_socket_bytes_available, %s", get_socket_error()); 466 | error_code = FTL_UNKNOWN_ERROR_CODE; 467 | } 468 | else 469 | { 470 | // If we have data waiting, consume it now. 471 | if (bytesAvailable > 0) 472 | { 473 | int resp_code = _ftl_get_response(ftl, buf, sizeof(buf)); 474 | 475 | // If we got a ping response, mark the time and loop again. 476 | if (resp_code == FTL_INGEST_RESP_PING) { 477 | gettimeofday(&last_ping, NULL); 478 | continue; 479 | } 480 | 481 | // If it's anything else, it's an error. 482 | error_code = _log_response(ftl, resp_code); 483 | } 484 | } 485 | 486 | // If we don't have an error, check if the ping has timed out. 487 | if (error_code == FTL_SUCCESS) 488 | { 489 | // Get the current time and figure out the time since the last ping was recieved. 490 | gettimeofday(&now, NULL); 491 | ms_since_ping = timeval_subtract_to_ms(&now, &last_ping); 492 | if (ms_since_ping < keepalive_is_late) { 493 | continue; 494 | } 495 | 496 | // Otherwise, we havn't gotten the ping in too long. 497 | FTL_LOG(ftl, FTL_LOG_ERROR, "Ingest ping timeout, we haven't gotten a ping in %d ms.", ms_since_ping); 498 | error_code = FTL_NO_PING_RESPONSE; 499 | } 500 | 501 | // At this point something is wrong, and we are going to shutdown the connection. Do one more check that we 502 | // should still be running. 503 | if (!ftl_get_state(ftl, FTL_CXN_STATUS_THRD)) 504 | { 505 | break; 506 | } 507 | FTL_LOG(ftl, FTL_LOG_ERROR, "Ingest connection has dropped: error code %d\n", error_code); 508 | 509 | // Clear the state that this thread is running. If we don't do this we will dead lock 510 | // in the internal_ingest_disconnect. 511 | ftl_clear_state(ftl, FTL_CXN_STATUS_THRD); 512 | 513 | // Shutdown the ingest connection. 514 | if (os_trylock_mutex(&ftl->disconnect_mutex)) { 515 | internal_ingest_disconnect(ftl); 516 | os_unlock_mutex(&ftl->disconnect_mutex); 517 | } 518 | 519 | // Fire an event indicating we shutdown. 520 | status.type = FTL_STATUS_EVENT; 521 | if (error_code == FTL_NO_MEDIA_TIMEOUT) { 522 | status.msg.event.reason = FTL_STATUS_EVENT_REASON_NO_MEDIA; 523 | } 524 | else { 525 | status.msg.event.reason = FTL_STATUS_EVENT_REASON_UNKNOWN; 526 | } 527 | status.msg.event.type = FTL_STATUS_EVENT_TYPE_DISCONNECTED; 528 | status.msg.event.error_code = error_code; 529 | enqueue_status_msg(ftl, &status); 530 | 531 | // Exit the loop. 532 | break; 533 | } 534 | 535 | FTL_LOG(ftl, FTL_LOG_INFO, "Exited connection_status_thread"); 536 | return 0; 537 | } 538 | 539 | ftl_status_t _log_response(ftl_stream_configuration_private_t *ftl, int response_code){ 540 | 541 | switch (response_code) { 542 | case FTL_INGEST_RESP_OK: 543 | FTL_LOG(ftl, FTL_LOG_DEBUG, "ingest accepted our paramteres"); 544 | return FTL_SUCCESS; 545 | case FTL_INGEST_NO_RESPONSE: 546 | FTL_LOG(ftl, FTL_LOG_ERROR, "ingest did not respond to request"); 547 | return FTL_INGEST_NO_RESPONSE; 548 | case FTL_INGEST_RESP_PING: 549 | return FTL_SUCCESS; 550 | case FTL_INGEST_RESP_BAD_REQUEST: 551 | FTL_LOG(ftl, FTL_LOG_ERROR, "ingest responded bad request"); 552 | return FTL_BAD_REQUEST; 553 | case FTL_INGEST_RESP_UNAUTHORIZED: 554 | FTL_LOG(ftl, FTL_LOG_ERROR, "channel is not authorized for FTL"); 555 | return FTL_UNAUTHORIZED; 556 | case FTL_INGEST_RESP_OLD_VERSION: 557 | FTL_LOG(ftl, FTL_LOG_ERROR, "This version of the FTLSDK is depricated"); 558 | return FTL_OLD_VERSION; 559 | case FTL_INGEST_RESP_AUDIO_SSRC_COLLISION: 560 | FTL_LOG(ftl, FTL_LOG_ERROR, "audio SSRC collision from this IP address. Please change your audio SSRC to an unused value"); 561 | return FTL_AUDIO_SSRC_COLLISION; 562 | case FTL_INGEST_RESP_VIDEO_SSRC_COLLISION: 563 | FTL_LOG(ftl, FTL_LOG_ERROR, "video SSRC collision from this IP address. Please change your audio SSRC to an unused value"); 564 | return FTL_VIDEO_SSRC_COLLISION; 565 | case FTL_INGEST_RESP_INVALID_STREAM_KEY: 566 | FTL_LOG(ftl, FTL_LOG_ERROR, "The stream key or channel id is incorrect"); 567 | return FTL_BAD_OR_INVALID_STREAM_KEY; 568 | case FTL_INGEST_RESP_CHANNEL_IN_USE: 569 | FTL_LOG(ftl, FTL_LOG_ERROR, "the channel id is already actively streaming"); 570 | return FTL_CHANNEL_IN_USE; 571 | case FTL_INGEST_RESP_REGION_UNSUPPORTED: 572 | FTL_LOG(ftl, FTL_LOG_ERROR, "the region is not authorized to stream"); 573 | return FTL_REGION_UNSUPPORTED; 574 | case FTL_INGEST_RESP_NO_MEDIA_TIMEOUT: 575 | FTL_LOG(ftl, FTL_LOG_ERROR, "The server did not receive media (audio or video) for an extended period of time"); 576 | return FTL_NO_MEDIA_TIMEOUT; 577 | case FTL_INGEST_RESP_INTERNAL_SERVER_ERROR: 578 | FTL_LOG(ftl, FTL_LOG_ERROR, "parameters accepted, but ingest couldn't start FTL. Please contact support!"); 579 | return FTL_INTERNAL_ERROR; 580 | case FTL_INGEST_RESP_GAME_BLOCKED: 581 | FTL_LOG(ftl, FTL_LOG_ERROR, "The current game set by this profile can't be streamed."); 582 | return FTL_GAME_BLOCKED; 583 | case FTL_INGEST_RESP_INTERNAL_MEMORY_ERROR: 584 | FTL_LOG(ftl, FTL_LOG_ERROR, "Server memory error"); 585 | return FTL_INTERNAL_ERROR; 586 | case FTL_INGEST_RESP_INTERNAL_COMMAND_ERROR: 587 | FTL_LOG(ftl, FTL_LOG_ERROR, "Server command error"); 588 | return FTL_INTERNAL_ERROR; 589 | case FTL_INGEST_RESP_INTERNAL_SOCKET_CLOSED: 590 | FTL_LOG(ftl, FTL_LOG_ERROR, "Ingest socket closed."); 591 | return FTL_INGEST_SOCKET_CLOSED; 592 | case FTL_INGEST_RESP_INTERNAL_SOCKET_TIMEOUT: 593 | FTL_LOG(ftl, FTL_LOG_ERROR, "Ingest socket timeout."); 594 | return FTL_INGEST_SOCKET_TIMEOUT; 595 | case FTL_INGEST_RESP_SERVER_TERMINATE: 596 | FTL_LOG(ftl, FTL_LOG_ERROR, "The server has terminated the stream."); 597 | return FTL_INGEST_SERVER_TERMINATE; 598 | case FTL_INGEST_RESP_UNKNOWN: 599 | FTL_LOG(ftl, FTL_LOG_ERROR, "Ingest unknown response."); 600 | return FTL_INTERNAL_ERROR; 601 | } 602 | 603 | return FTL_UNKNOWN_ERROR_CODE; 604 | } 605 | -------------------------------------------------------------------------------- /libftl/hmac/hmac.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "hmac.h" 4 | #include "sha2.h" 5 | #include 6 | 7 | #define O_KEY_PAD 0x5C 8 | #define I_KEY_PAD 0x36 9 | 10 | const unsigned char hex_digits[] = "0123456789abcdef"; 11 | 12 | int hmacsha512(const char * rawKey, const unsigned char * message, const int messageLength, char * result) { 13 | Sha512Context ctx; 14 | SHA512_HASH computedHash; 15 | size_t keyLen = strlen(rawKey); 16 | 17 | const char * key; 18 | if (keyLen > SHA512_BLOCK_SIZE) { 19 | Sha512Initialise(&ctx); 20 | Sha512Update(&ctx, (void*)rawKey, (uint32_t)keyLen); 21 | Sha512Finalise(&ctx, &computedHash); 22 | 23 | key = (const char *)computedHash.bytes; 24 | keyLen = SHA512_HASH_SIZE; 25 | } else { 26 | key = rawKey; 27 | } 28 | 29 | unsigned char iKeyPad[SHA512_BLOCK_SIZE]; 30 | unsigned char oKeyPad[SHA512_BLOCK_SIZE]; 31 | 32 | memset(oKeyPad, O_KEY_PAD, SHA512_BLOCK_SIZE); 33 | memset(iKeyPad, I_KEY_PAD, SHA512_BLOCK_SIZE); 34 | 35 | size_t i = 0; 36 | for(i = 0; i < keyLen; i++) { 37 | oKeyPad[i] ^= key[i]; 38 | iKeyPad[i] ^= key[i]; 39 | } 40 | 41 | Sha512Initialise(&ctx); 42 | Sha512Update(&ctx, (void*)iKeyPad, SHA512_BLOCK_SIZE); 43 | Sha512Update(&ctx, (void*)message, messageLength); 44 | 45 | Sha512Finalise(&ctx, &computedHash); 46 | 47 | Sha512Initialise(&ctx); 48 | Sha512Update(&ctx, (void*)oKeyPad, SHA512_BLOCK_SIZE); 49 | Sha512Update(&ctx, (void*)computedHash.bytes, SHA512_HASH_SIZE); 50 | 51 | Sha512Finalise(&ctx, &computedHash); 52 | 53 | for(i = 0; i < SHA512_HASH_SIZE; i++) { 54 | result[i * 2] = hex_digits[computedHash.bytes[i] >> 4]; 55 | result[(i * 2) + 1] = hex_digits[computedHash.bytes[i] & 0x0F]; 56 | } 57 | 58 | result[SHA512_HEX_STRING_HASH_SIZE - 1] = 0; 59 | 60 | memset(computedHash.bytes, 0, sizeof(computedHash.bytes)); 61 | 62 | return SHA512_HEX_STRING_HASH_SIZE; 63 | } 64 | -------------------------------------------------------------------------------- /libftl/hmac/hmac.h: -------------------------------------------------------------------------------- 1 | int hmacsha512(const char * key, const unsigned char * message, const int messageLength, char * result); 2 | -------------------------------------------------------------------------------- /libftl/hmac/sha2.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // LibSha512 3 | // 4 | // Implementation of SHA512 hash function. 5 | // Original author: Tom St Denis, tomstdenis@gmail.com, http://libtom.org 6 | // Modified by WaterJuice retaining Public Domain license. 7 | // 8 | // This is free and unencumbered software released into the public domain - June 2013 waterjuice.org 9 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 10 | 11 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 12 | // IMPORTS 13 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 14 | 15 | #include "sha2.h" 16 | #include 17 | 18 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 19 | // MACROS 20 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 21 | 22 | #define ROR64( value, bits ) (((value) >> (bits)) | ((value) << (64 - (bits)))) 23 | 24 | #define MIN( x, y ) ( ((x)<(y))?(x):(y) ) 25 | 26 | #define LOAD64H( x, y ) \ 27 | { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \ 28 | (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \ 29 | (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \ 30 | (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } 31 | 32 | #define STORE64H( x, y ) \ 33 | { (y)[0] = (uint8_t)(((x)>>56)&255); (y)[1] = (uint8_t)(((x)>>48)&255); \ 34 | (y)[2] = (uint8_t)(((x)>>40)&255); (y)[3] = (uint8_t)(((x)>>32)&255); \ 35 | (y)[4] = (uint8_t)(((x)>>24)&255); (y)[5] = (uint8_t)(((x)>>16)&255); \ 36 | (y)[6] = (uint8_t)(((x)>>8)&255); (y)[7] = (uint8_t)((x)&255); } 37 | 38 | 39 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 40 | // CONSTANTS 41 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 42 | 43 | // The K array 44 | static const uint64_t K[80] = { 45 | 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 46 | 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 47 | 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 48 | 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, 49 | 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 50 | 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 51 | 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 52 | 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 53 | 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 54 | 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, 55 | 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 56 | 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 57 | 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 58 | 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 59 | 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 60 | 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 61 | 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 62 | 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 63 | 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 64 | 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL 65 | }; 66 | 67 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 68 | // INTERNAL FUNCTIONS 69 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 70 | 71 | // Various logical functions 72 | #define Ch( x, y, z ) (z ^ (x & (y ^ z))) 73 | #define Maj(x, y, z ) (((x | y) & z) | (x & y)) 74 | #define S( x, n ) ROR64( x, n ) 75 | #define R( x, n ) (((x)&0xFFFFFFFFFFFFFFFFULL)>>((uint64_t)n)) 76 | #define Sigma0( x ) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) 77 | #define Sigma1( x ) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) 78 | #define Gamma0( x ) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) 79 | #define Gamma1( x ) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) 80 | 81 | #define Sha512Round( a, b, c, d, e, f, g, h, i ) \ 82 | t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ 83 | t1 = Sigma0(a) + Maj(a, b, c); \ 84 | d += t0; \ 85 | h = t0 + t1; 86 | 87 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 88 | // TransformFunction 89 | // 90 | // Compress 1024-bits 91 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 92 | static 93 | void 94 | TransformFunction 95 | ( 96 | Sha512Context* Context, 97 | uint8_t* Buffer 98 | ) 99 | { 100 | uint64_t S[8]; 101 | uint64_t W[80]; 102 | uint64_t t0; 103 | uint64_t t1; 104 | int i; 105 | 106 | // Copy state into S 107 | for( i=0; i<8; i++ ) 108 | { 109 | S[i] = Context->state[i]; 110 | } 111 | 112 | // Copy the state into 1024-bits into W[0..15] 113 | for( i=0; i<16; i++ ) 114 | { 115 | LOAD64H(W[i], Buffer + (8*i)); 116 | } 117 | 118 | // Fill W[16..79] 119 | for( i=16; i<80; i++ ) 120 | { 121 | W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; 122 | } 123 | 124 | // Compress 125 | for( i=0; i<80; i+=8 ) 126 | { 127 | Sha512Round(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); 128 | Sha512Round(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); 129 | Sha512Round(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); 130 | Sha512Round(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); 131 | Sha512Round(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); 132 | Sha512Round(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); 133 | Sha512Round(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); 134 | Sha512Round(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); 135 | } 136 | 137 | // Feedback 138 | for( i=0; i<8; i++ ) 139 | { 140 | Context->state[i] = Context->state[i] + S[i]; 141 | } 142 | } 143 | 144 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 145 | // PUBLIC FUNCTIONS 146 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 147 | 148 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 149 | // Sha512Initialise 150 | // 151 | // Initialises a SHA512 Context. Use this to initialise/reset a context. 152 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 153 | void 154 | Sha512Initialise 155 | ( 156 | Sha512Context* Context 157 | ) 158 | { 159 | Context->curlen = 0; 160 | Context->length = 0; 161 | Context->state[0] = 0x6a09e667f3bcc908ULL; 162 | Context->state[1] = 0xbb67ae8584caa73bULL; 163 | Context->state[2] = 0x3c6ef372fe94f82bULL; 164 | Context->state[3] = 0xa54ff53a5f1d36f1ULL; 165 | Context->state[4] = 0x510e527fade682d1ULL; 166 | Context->state[5] = 0x9b05688c2b3e6c1fULL; 167 | Context->state[6] = 0x1f83d9abfb41bd6bULL; 168 | Context->state[7] = 0x5be0cd19137e2179ULL; 169 | } 170 | 171 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 172 | // Sha512Update 173 | // 174 | // Adds data to the SHA512 context. This will process the data and update the internal state of the context. Keep on 175 | // calling this function until all the data has been added. Then call Sha512Finalise to calculate the hash. 176 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 177 | void 178 | Sha512Update 179 | ( 180 | Sha512Context* Context, 181 | void* Buffer, 182 | uint32_t BufferSize 183 | ) 184 | { 185 | uint32_t n; 186 | 187 | if( Context->curlen > sizeof(Context->buf) ) 188 | { 189 | return; 190 | } 191 | 192 | while( BufferSize > 0 ) 193 | { 194 | if( Context->curlen == 0 && BufferSize >= SHA512_BLOCK_SIZE ) 195 | { 196 | TransformFunction( Context, (uint8_t *)Buffer ); 197 | Context->length += SHA512_BLOCK_SIZE * 8; 198 | Buffer = (uint8_t*)Buffer + SHA512_BLOCK_SIZE; 199 | BufferSize -= SHA512_BLOCK_SIZE; 200 | } 201 | else 202 | { 203 | n = MIN( BufferSize, (SHA512_BLOCK_SIZE - Context->curlen) ); 204 | memcpy( Context->buf + Context->curlen, Buffer, (size_t)n ); 205 | Context->curlen += n; 206 | Buffer = (uint8_t*)Buffer + n; 207 | BufferSize -= n; 208 | if( Context->curlen == SHA512_BLOCK_SIZE ) 209 | { 210 | TransformFunction( Context, Context->buf ); 211 | Context->length += 8*SHA512_BLOCK_SIZE; 212 | Context->curlen = 0; 213 | } 214 | } 215 | } 216 | } 217 | 218 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 219 | // Sha512Finalise 220 | // 221 | // Performs the final calculation of the hash and returns the digest (64 byte buffer containing 512bit hash). After 222 | // calling this, Sha512Initialised must be used to reuse the context. 223 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 224 | void 225 | Sha512Finalise 226 | ( 227 | Sha512Context* Context, 228 | SHA512_HASH* Digest 229 | ) 230 | { 231 | int i; 232 | 233 | if( Context->curlen >= sizeof(Context->buf) ) 234 | { 235 | return; 236 | } 237 | 238 | // Increase the length of the message 239 | Context->length += Context->curlen * 8ULL; 240 | 241 | // Append the '1' bit 242 | Context->buf[Context->curlen++] = (uint8_t)0x80; 243 | 244 | // If the length is currently above 112 bytes we append zeros 245 | // then compress. Then we can fall back to padding zeros and length 246 | // encoding like normal. 247 | if( Context->curlen > 112 ) 248 | { 249 | while( Context->curlen < 128 ) 250 | { 251 | Context->buf[Context->curlen++] = (uint8_t)0; 252 | } 253 | TransformFunction( Context, Context->buf ); 254 | Context->curlen = 0; 255 | } 256 | 257 | // Pad up to 120 bytes of zeroes 258 | // note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash 259 | // > 2^64 bits of data... :-) 260 | while( Context->curlen < 120 ) 261 | { 262 | Context->buf[Context->curlen++] = (uint8_t)0; 263 | } 264 | 265 | // Store length 266 | STORE64H( Context->length, Context->buf+120 ); 267 | TransformFunction( Context, Context->buf ); 268 | 269 | // Copy output 270 | for( i=0; i<8; i++ ) 271 | { 272 | STORE64H( Context->state[i], Digest->bytes+(8*i) ); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /libftl/hmac/sha2.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // LibSha512 3 | // 4 | // Implementation of SHA512 hash function. 5 | // Original author: Tom St Denis, tomstdenis@gmail.com, http://libtom.org 6 | // Modified by WaterJuice retaining Public Domain license. 7 | // 8 | // This is free and unencumbered software released into the public domain - June 2013 waterjuice.org 9 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 10 | 11 | #ifndef _LibSha2_h_ 12 | #define _LibSha2_h_ 13 | 14 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 15 | // IMPORTS 16 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 17 | 18 | #include 19 | #include 20 | 21 | typedef struct 22 | { 23 | uint64_t length; 24 | uint64_t state[8]; 25 | uint32_t curlen; 26 | uint8_t buf[128]; 27 | } Sha512Context; 28 | 29 | #define SHA512_BLOCK_SIZE 128 30 | #define SHA512_HASH_SIZE ( 512 / 8 ) 31 | #define SHA512_HEX_STRING_HASH_SIZE (SHA512_HASH_SIZE * 2 + 1) 32 | 33 | typedef struct 34 | { 35 | uint8_t bytes [SHA512_HASH_SIZE]; 36 | } SHA512_HASH; 37 | 38 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 39 | // PUBLIC FUNCTIONS 40 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 41 | 42 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 43 | // Sha512Initialise 44 | // 45 | // Initialises a SHA512 Context. Use this to initialise/reset a context. 46 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 47 | void Sha512Initialise 48 | ( 49 | Sha512Context* Context 50 | ); 51 | 52 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 53 | // Sha512Update 54 | // 55 | // Adds data to the SHA512 context. This will process the data and update the internal state of the context. Keep on 56 | // calling this function until all the data has been added. Then call Sha512Finalise to calculate the hash. 57 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 58 | void Sha512Update 59 | ( 60 | Sha512Context* Context, 61 | void* Buffer, 62 | uint32_t BufferSize 63 | ); 64 | 65 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 66 | // Sha512Finalise 67 | // 68 | // Performs the final calculation of the hash and returns the digest (64 byte buffer containing 512bit hash). After 69 | // calling this, Sha512Initialised must be used to reuse the context. 70 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 71 | void 72 | Sha512Finalise 73 | ( 74 | Sha512Context* Context, 75 | SHA512_HASH* Digest 76 | ); 77 | 78 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 79 | #endif //_LibSha2_h_ 80 | -------------------------------------------------------------------------------- /libftl/ingest.c: -------------------------------------------------------------------------------- 1 | #include "ftl.h" 2 | #include "ftl_private.h" 3 | #ifndef DISABLE_AUTO_INGEST 4 | #include 5 | #include 6 | #endif 7 | 8 | static int _ingest_lookup_ip(const char *ingest_location, char ***ingest_ip); 9 | static int _ping_server(const char *ip, int port); 10 | OS_THREAD_ROUTINE _ingest_get_rtt(void *data); 11 | 12 | typedef struct { 13 | ftl_ingest_t *ingest; 14 | ftl_stream_configuration_private_t *ftl; 15 | }_tmp_ingest_thread_data_t; 16 | 17 | static int _ping_server(const char *hostname, int port) { 18 | 19 | SOCKET sock; 20 | struct addrinfo hints; 21 | char dummy[4]; 22 | struct timeval start, stop, delta; 23 | int retval = -1; 24 | struct addrinfo* resolved_names = 0; 25 | struct addrinfo* p = 0; 26 | int err = 0; 27 | int off = 0; 28 | 29 | memset(&hints, 0, sizeof(hints)); 30 | hints.ai_family = PF_UNSPEC; 31 | hints.ai_socktype = SOCK_DGRAM; 32 | hints.ai_protocol = 0; 33 | 34 | int ingest_port = INGEST_PORT; 35 | char port_str[10]; 36 | 37 | snprintf(port_str, 10, "%d", port); 38 | 39 | err = getaddrinfo(hostname, port_str, &hints, &resolved_names); 40 | if (err != 0) { 41 | return FTL_DNS_FAILURE; 42 | } 43 | 44 | do { 45 | for (p = resolved_names; p != NULL; p = p->ai_next) { 46 | sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); 47 | if (sock == -1) { 48 | continue; 49 | } 50 | 51 | setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&off, sizeof(off)); 52 | set_socket_recv_timeout(sock, 500); 53 | 54 | gettimeofday(&start, NULL); 55 | 56 | if (sendto(sock, dummy, sizeof(dummy), 0, p->ai_addr, (int)p->ai_addrlen) == SOCKET_ERROR) { 57 | printf("Sendto error: %s\n", get_socket_error()); 58 | break; 59 | } 60 | 61 | if (recv(sock, dummy, sizeof(dummy), 0) < 0) { 62 | break; 63 | } 64 | 65 | gettimeofday(&stop, NULL); 66 | timeval_subtract(&delta, &stop, &start); 67 | retval = (int)timeval_to_ms(&delta); 68 | } 69 | } while (0); 70 | 71 | /* Free the resolved name struct */ 72 | freeaddrinfo(resolved_names); 73 | 74 | shutdown_socket(sock, SD_BOTH); 75 | close_socket(sock); 76 | 77 | return retval; 78 | } 79 | 80 | OS_THREAD_ROUTINE _ingest_get_rtt(void *data) { 81 | _tmp_ingest_thread_data_t *thread_data = (_tmp_ingest_thread_data_t *)data; 82 | ftl_stream_configuration_private_t *ftl = thread_data->ftl; 83 | ftl_ingest_t *ingest = thread_data->ingest; 84 | int ping; 85 | 86 | ingest->rtt = 1000; 87 | 88 | if ((ping = _ping_server(ingest->name, INGEST_PING_PORT)) >= 0) { 89 | ingest->rtt = ping; 90 | } 91 | 92 | return 0; 93 | } 94 | 95 | ftl_status_t ftl_find_closest_available_ingest(const char* ingestHosts[], int ingestsCount, char* bestIngestHostComputed) 96 | { 97 | if (ingestHosts == NULL || ingestsCount <= 0) { 98 | return FTL_UNKNOWN_ERROR_CODE; 99 | } 100 | 101 | ftl_ingest_t* ingestElements = NULL; 102 | OS_THREAD_HANDLE *handles = NULL; 103 | _tmp_ingest_thread_data_t *data = NULL; 104 | 105 | int i; 106 | 107 | ftl_status_t ret_status = FTL_SUCCESS; 108 | do { 109 | if ((ingestElements = calloc(ingestsCount, sizeof(ftl_ingest_t))) == NULL) { 110 | ret_status = FTL_MALLOC_FAILURE; 111 | break; 112 | } 113 | 114 | for (i = 0; i < ingestsCount; i++) { 115 | size_t host_len = strlen(ingestHosts[i]) + 1; 116 | if ((ingestElements[i].name = malloc(host_len)) == NULL) { 117 | ret_status = FTL_MALLOC_FAILURE; 118 | break; 119 | } 120 | strcpy_s(ingestElements[i].name, host_len, ingestHosts[i]); 121 | ingestElements[i].rtt = 1000; 122 | ingestElements[i].next = NULL; 123 | } 124 | if (ret_status != FTL_SUCCESS) { 125 | break; 126 | } 127 | 128 | if ((handles = (OS_THREAD_HANDLE *)malloc(sizeof(OS_THREAD_HANDLE) * ingestsCount)) == NULL) { 129 | ret_status = FTL_MALLOC_FAILURE; 130 | break; 131 | } 132 | 133 | if ((data = (_tmp_ingest_thread_data_t *)malloc(sizeof(_tmp_ingest_thread_data_t) * ingestsCount)) == NULL) { 134 | ret_status = FTL_MALLOC_FAILURE; 135 | break; 136 | } 137 | } while (0); 138 | 139 | // malloc failed, cleanup 140 | if (ret_status != FTL_SUCCESS) { 141 | if (ingestElements != NULL) { 142 | for (i = 0; i < ingestsCount; i++) { 143 | free(ingestElements[i].name); 144 | } 145 | } 146 | free(ingestElements); 147 | free(handles); 148 | free(data); 149 | return ret_status; 150 | } 151 | 152 | ftl_ingest_t *best = NULL; 153 | struct timeval start, stop, delta; 154 | gettimeofday(&start, NULL); 155 | 156 | /*query all the ingests about cpu and rtt*/ 157 | for (i = 0; i < ingestsCount; i++) { 158 | handles[i] = 0; 159 | data[i].ingest = &ingestElements[i]; 160 | data[i].ftl = NULL; 161 | os_create_thread(&handles[i], NULL, _ingest_get_rtt, &data[i]); 162 | sleep_ms(5); //space out the pings 163 | } 164 | 165 | /*wait for all the ingests to complete*/ 166 | for (i = 0; i < ingestsCount; i++) { 167 | 168 | if (handles[i] != 0) { 169 | os_wait_thread(handles[i]); 170 | } 171 | 172 | if (best == NULL || ingestElements[i].rtt < best->rtt) { 173 | best = &ingestElements[i]; 174 | } 175 | } 176 | 177 | gettimeofday(&stop, NULL); 178 | timeval_subtract(&delta, &stop, &start); 179 | int ms = (int)timeval_to_ms(&delta); 180 | 181 | for (i = 0; i < ingestsCount; i++) { 182 | if (handles[i] != 0) { 183 | os_destroy_thread(handles[i]); 184 | } 185 | } 186 | 187 | free(handles); 188 | free(data); 189 | 190 | if (best) { 191 | strcpy_s(bestIngestHostComputed, strlen(best->name), best->name); 192 | } else { 193 | ret_status = FTL_UNKNOWN_ERROR_CODE; 194 | } 195 | 196 | for (i = 0; i < ingestsCount; i++) { 197 | free(ingestElements[i].name); 198 | } 199 | free(ingestElements); 200 | 201 | return ret_status; 202 | } 203 | 204 | #ifndef DISABLE_AUTO_INGEST 205 | OS_THREAD_ROUTINE _ingest_get_hosts(ftl_stream_configuration_private_t *ftl); 206 | 207 | static size_t _curl_write_callback(void *contents, size_t size, size_t nmemb, void *userp) 208 | { 209 | size_t realsize = size * nmemb; 210 | struct MemoryStruct *mem = (struct MemoryStruct *)userp; 211 | 212 | mem->memory = realloc(mem->memory, mem->size + realsize + 1); 213 | if (mem->memory == NULL) { 214 | /* out of memory! */ 215 | printf("not enough memory (realloc returned NULL)\n"); 216 | return 0; 217 | } 218 | 219 | memcpy(&(mem->memory[mem->size]), contents, realsize); 220 | mem->size += realsize; 221 | mem->memory[mem->size] = 0; 222 | 223 | return realsize; 224 | } 225 | 226 | OS_THREAD_ROUTINE _ingest_get_hosts(ftl_stream_configuration_private_t *ftl) { 227 | CURL *curl_handle; 228 | CURLcode res; 229 | struct MemoryStruct chunk; 230 | char *query_result = NULL; 231 | size_t i = 0; 232 | int total_ingest_cnt = 0; 233 | json_error_t error; 234 | json_t *ingests = NULL, *ingest_item = NULL, *ingest_array = NULL; 235 | 236 | curl_handle = curl_easy_init(); 237 | 238 | chunk.memory = malloc(1); /* will be grown as needed by realloc */ 239 | chunk.size = 0; /* no data at this point */ 240 | char ingestBestUrl[1024], vendorName[100], vendorVersion[100], ftlSdkVersion[20]; 241 | struct curl_slist *list = NULL; 242 | 243 | int formatUri = snprintf(ingestBestUrl, sizeof(ingestBestUrl), INGEST_LIST_URI, ftl->channel_id); 244 | 245 | curl_easy_setopt(curl_handle, CURLOPT_URL, ingestBestUrl); 246 | 247 | int formatVendorName = snprintf(vendorName, sizeof(vendorName), "MS-ClientId: %s", ftl->vendor_name); 248 | int formatVendorVersion = snprintf(vendorVersion, sizeof(vendorVersion), "MS-ClientVersion: %s", ftl->vendor_version); 249 | int formatFtlSdkVersion = snprintf(ftlSdkVersion, sizeof(ftlSdkVersion), "ftlsdk/%d.%d.%d", FTL_VERSION_MAJOR, FTL_VERSION_MINOR, FTL_VERSION_MAINTENANCE); 250 | 251 | if (formatVendorName > 0) { 252 | list = curl_slist_append(list, vendorName); 253 | } 254 | 255 | if (formatVendorVersion > 0) { 256 | list = curl_slist_append(list, vendorVersion); 257 | } 258 | 259 | if (list != NULL) { 260 | curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list); 261 | } 262 | 263 | curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, TRUE); 264 | curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L); 265 | curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, _curl_write_callback); 266 | curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); 267 | 268 | if (formatFtlSdkVersion > 0) { 269 | curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, ftlSdkVersion); 270 | } 271 | else { 272 | curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "ftlsdk/0.10.0"); 273 | } 274 | 275 | curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); 276 | 277 | #if LIBCURL_VERSION_NUM >= 0x072400 278 | // A lot of servers don't yet support ALPN 279 | curl_easy_setopt(curl_handle, CURLOPT_SSL_ENABLE_ALPN, 0); 280 | #endif 281 | 282 | res = curl_easy_perform(curl_handle); 283 | 284 | if (res != CURLE_OK) { 285 | printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); 286 | goto cleanup; 287 | } 288 | 289 | if ((ingests = json_loadb(chunk.memory, chunk.size, 0, &error)) == NULL) { 290 | goto cleanup; 291 | } 292 | 293 | ingest_array = json_object_get(ingests, "ingests"); 294 | 295 | size_t size = json_array_size(ingest_array); 296 | 297 | for (i = 0; i < size; i++) { 298 | char *name = NULL; 299 | ingest_item = json_array_get(ingest_array, i); 300 | if (json_unpack(ingest_item, "{s:s}", "name", &name) < 0) { 301 | continue; 302 | } 303 | 304 | ftl_ingest_t *ingest_elmt; 305 | 306 | if ((ingest_elmt = malloc(sizeof(ftl_ingest_t))) == NULL) { 307 | goto cleanup; 308 | } 309 | 310 | ingest_elmt->name = _strdup(name); 311 | ingest_elmt->rtt = 500; 312 | ingest_elmt->next = NULL; 313 | 314 | if (ftl->ingest_list == NULL) { 315 | ftl->ingest_list = ingest_elmt; 316 | } 317 | else { 318 | ftl_ingest_t *tail = ftl->ingest_list; 319 | while (tail->next != NULL) { 320 | tail = tail->next; 321 | } 322 | 323 | tail->next = ingest_elmt; 324 | } 325 | 326 | total_ingest_cnt++; 327 | } 328 | 329 | cleanup: 330 | free(chunk.memory); 331 | curl_easy_cleanup(curl_handle); 332 | if (ingests != NULL) { 333 | json_decref(ingests); 334 | } 335 | 336 | ftl->ingest_count = total_ingest_cnt; 337 | 338 | return total_ingest_cnt; 339 | } 340 | 341 | char * ingest_find_best(ftl_stream_configuration_private_t *ftl) { 342 | 343 | OS_THREAD_HANDLE *handle; 344 | _tmp_ingest_thread_data_t *data; 345 | int i; 346 | ftl_ingest_t *elmt, *best = NULL; 347 | struct timeval start, stop, delta; 348 | 349 | /*get list of ingest each time as they are dynamically selected*/ 350 | while (ftl->ingest_list != NULL) { 351 | elmt = ftl->ingest_list; 352 | ftl->ingest_list = elmt->next; 353 | free(elmt->name); 354 | free(elmt); 355 | } 356 | 357 | if (_ingest_get_hosts(ftl) <= 0) { 358 | return NULL; 359 | } 360 | 361 | if ((handle = (OS_THREAD_HANDLE *)malloc(sizeof(OS_THREAD_HANDLE) * ftl->ingest_count)) == NULL) { 362 | return NULL; 363 | } 364 | 365 | if ((data = (_tmp_ingest_thread_data_t *)malloc(sizeof(_tmp_ingest_thread_data_t) * ftl->ingest_count)) == NULL) { 366 | return NULL; 367 | } 368 | 369 | gettimeofday(&start, NULL); 370 | 371 | /*query all the ingests about cpu and rtt*/ 372 | elmt = ftl->ingest_list; 373 | for (i = 0; i < ftl->ingest_count && elmt != NULL; i++) { 374 | handle[i] = 0; 375 | data[i].ingest = elmt; 376 | data[i].ftl = ftl; 377 | os_create_thread(&handle[i], NULL, _ingest_get_rtt, &data[i]); 378 | sleep_ms(5); //space out the pings 379 | elmt = elmt->next; 380 | } 381 | 382 | /*wait for all the ingests to complete*/ 383 | elmt = ftl->ingest_list; 384 | for (i = 0; i < ftl->ingest_count && elmt != NULL; i++) { 385 | 386 | if (handle[i] != 0) { 387 | os_wait_thread(handle[i]); 388 | } 389 | 390 | if (best == NULL || elmt->rtt < best->rtt) { 391 | best = elmt; 392 | } 393 | 394 | elmt = elmt->next; 395 | } 396 | 397 | gettimeofday(&stop, NULL); 398 | timeval_subtract(&delta, &stop, &start); 399 | int ms = (int)timeval_to_ms(&delta); 400 | 401 | FTL_LOG(ftl, FTL_LOG_INFO, "It took %d ms to query all ingests\n", ms); 402 | 403 | elmt = ftl->ingest_list; 404 | for (i = 0; i < ftl->ingest_count && elmt != NULL; i++) { 405 | if (handle[i] != 0) { 406 | os_destroy_thread(handle[i]); 407 | } 408 | 409 | elmt = elmt->next; 410 | } 411 | 412 | free(handle); 413 | free(data); 414 | 415 | if (best){ 416 | FTL_LOG(ftl, FTL_LOG_INFO, "%s had the shortest RTT of %d ms\n", best->name, best->rtt); 417 | return _strdup(best->name); 418 | } 419 | return NULL; 420 | } 421 | #endif 422 | 423 | void ingest_release(ftl_stream_configuration_private_t *ftl) { 424 | 425 | ftl_ingest_t *elmt, *tmp; 426 | 427 | elmt = ftl->ingest_list; 428 | 429 | while (elmt != NULL) { 430 | tmp = elmt->next; 431 | free(elmt); 432 | elmt = tmp; 433 | } 434 | } 435 | -------------------------------------------------------------------------------- /libftl/init.c: -------------------------------------------------------------------------------- 1 | /** 2 | * init.c - Library initialization functions 3 | * 4 | * Copyright (c) 2015 Michael Casadevall 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #define __FTL_INTERNAL 26 | #include "ftl.h" 27 | 28 | char error_message[1000]; 29 | FTL_API const int FTL_VERSION_MAJOR = 0; 30 | FTL_API const int FTL_VERSION_MINOR = 2; 31 | FTL_API const int FTL_VERSION_MAINTENANCE = 3; 32 | 33 | // Initializes all sublibraries used by FTL 34 | ftl_status_t ftl_init() { 35 | ftl_init_sockets(); 36 | ftl_logging_init(); 37 | return FTL_SUCCESS; 38 | } 39 | -------------------------------------------------------------------------------- /libftl/logging.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file logging.c - Contains debug log functions 3 | * 4 | * Copyright (c) 2015 Michael Casadevall 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #include "ftl.h" 26 | #include "ftl_private.h" 27 | 28 | void ftl_log_msg(ftl_stream_configuration_private_t *ftl, ftl_log_severity_t log_level, const char * file, int lineno, const char * fmt, ...) { 29 | va_list args; 30 | ftl_status_msg_t m; 31 | m.type = FTL_STATUS_LOG; 32 | 33 | m.msg.log.log_level = log_level; 34 | va_start(args, fmt); 35 | vsnprintf(m.msg.log.string, sizeof(m.msg.log.string), fmt, args); 36 | va_end(args); 37 | 38 | enqueue_status_msg(ftl, &m); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /libftl/posix/socket.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file socket.c - Windows Socket Abstractions 3 | * 4 | * Copyright (c) 2015 Michael Casadevall 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #include "ftl.h" 26 | #include "ftl_private.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | void init_sockets() { 36 | //BSD sockets are smarter and don't need silly init 37 | } 38 | 39 | int close_socket(SOCKET sock) { 40 | return close(sock); 41 | } 42 | 43 | int shutdown_socket(SOCKET sock, int how) { 44 | return shutdown(sock, how); 45 | } 46 | 47 | char * get_socket_error() { 48 | return strerror(errno); 49 | } 50 | 51 | int set_socket_recv_timeout(SOCKET socket, int ms_timeout){ 52 | struct timeval tv = {0}; 53 | 54 | while (ms_timeout >= 1000) { 55 | tv.tv_sec++; 56 | ms_timeout -= 1000; 57 | } 58 | tv.tv_usec = ms_timeout * 1000; 59 | 60 | return setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv)); 61 | } 62 | 63 | int set_socket_send_timeout(SOCKET socket, int ms_timeout){ 64 | struct timeval tv = {0}; 65 | 66 | while (ms_timeout >= 1000) { 67 | tv.tv_sec++; 68 | ms_timeout -= 1000; 69 | } 70 | tv.tv_usec = ms_timeout * 1000; 71 | 72 | return setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv)); 73 | } 74 | 75 | int set_socket_enable_keepalive(SOCKET socket){ 76 | int keep_alive = 1; 77 | return setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&keep_alive, sizeof(keep_alive)); 78 | } 79 | 80 | int get_socket_send_buf(SOCKET socket, int *buffer_space) { 81 | int len = sizeof(*buffer_space); 82 | return getsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char*)buffer_space, &len); 83 | } 84 | 85 | int set_socket_send_buf(SOCKET socket, int buffer_space) { 86 | return setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char*)&buffer_space, sizeof(buffer_space)); 87 | } 88 | 89 | int get_socket_bytes_available(SOCKET socket, unsigned long *bytes_available) { 90 | return ioctl(socket, FIONREAD, bytes_available); 91 | } 92 | 93 | int poll_socket_for_receive(SOCKET socket, int timeoutMs) 94 | { 95 | // timeoutMs behavior 96 | // > 0 time in ms to wait 97 | // = 0 return instantly 98 | // < 0 wait forever 99 | 100 | struct pollfd fd; 101 | fd.fd = socket; 102 | fd.events = POLLIN; 103 | 104 | int ret = poll(&fd, 1, timeoutMs); 105 | 106 | // Function return values 107 | // = 0 timeout reached 108 | // = 1 data received 109 | // = -1 socket error 110 | if (ret == 0) 111 | { 112 | return 0; 113 | } 114 | else if (ret == 1 && fd.revents == POLLIN) 115 | { 116 | return 1; 117 | } 118 | else 119 | { 120 | return SOCKET_ERROR; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /libftl/posix/socket.h: -------------------------------------------------------------------------------- 1 | typedef int SOCKET; 2 | 3 | #define SD_RECEIVE SHUT_RD 4 | #define SD_SEND SHUT_WR 5 | #define SD_BOTH SHUT_RDWR 6 | 7 | void init_sockets(); 8 | int close_socket(SOCKET sock); 9 | char * get_socket_error(); 10 | int set_socket_recv_timeout(SOCKET socket, int ms_timeout); 11 | int set_socket_send_timeout(SOCKET socket, int ms_timeout); 12 | int set_socket_enable_keepalive(SOCKET socket); 13 | int get_socket_send_buf(SOCKET socket, int *buffer_space); 14 | int set_socket_send_buf(SOCKET socket, int buffer_space); 15 | int poll_socket_for_receive(SOCKET socket, int ms_timeout); 16 | int get_socket_bytes_available(SOCKET socket, unsigned long *bytes_available); 17 | int shutdown_socket(SOCKET sock, int how); 18 | -------------------------------------------------------------------------------- /libftl/posix/threads.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file threads.c - Posix Threads Abstractions 3 | * 4 | * Copyright (c) 2015 Stefan Slivinski 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #include "threads.h" 26 | 27 | pthread_mutexattr_t ftl_default_mutexattr; 28 | 29 | int os_create_thread(OS_THREAD_HANDLE *handle, OS_THREAD_ATTRIBS *attibs, OS_THREAD_START_ROUTINE func, void *args) { 30 | 31 | return pthread_create(handle, NULL, func, args); 32 | } 33 | 34 | int os_destroy_thread(OS_THREAD_HANDLE handle) { 35 | return 0; 36 | } 37 | 38 | int os_wait_thread(OS_THREAD_HANDLE handle) { 39 | return pthread_join(handle, NULL); 40 | } 41 | 42 | int os_init() { 43 | pthread_mutexattr_init(&ftl_default_mutexattr); 44 | // Set pthread mutexes to recursive to mirror Windows mutex behavior 45 | return pthread_mutexattr_settype(&ftl_default_mutexattr, PTHREAD_MUTEX_RECURSIVE); 46 | } 47 | 48 | int os_init_mutex(OS_MUTEX *mutex) { 49 | return pthread_mutex_init(mutex, &ftl_default_mutexattr); 50 | } 51 | 52 | int os_lock_mutex(OS_MUTEX *mutex) { 53 | return pthread_mutex_lock(mutex); 54 | } 55 | 56 | int os_trylock_mutex(OS_MUTEX *mutex) { 57 | int ret = pthread_mutex_trylock(mutex); 58 | return (ret == 0) ? 1 : 0; 59 | } 60 | 61 | int os_unlock_mutex(OS_MUTEX *mutex) { 62 | return pthread_mutex_unlock(mutex); 63 | } 64 | 65 | int os_delete_mutex(OS_MUTEX *mutex) { 66 | return 0; 67 | } 68 | 69 | int os_semaphore_create(OS_SEMAPHORE *sem, const char *name, int oflag, unsigned int value) { 70 | 71 | int retval = 0; 72 | 73 | if (pthread_mutex_init(&sem->mutex, NULL)) 74 | return -2; 75 | 76 | if (pthread_cond_init(&sem->cond, NULL)) { 77 | pthread_mutex_destroy(&sem->mutex); 78 | return -3; 79 | } 80 | 81 | sem->value = value; 82 | 83 | return 0; 84 | } 85 | 86 | int os_semaphore_pend(OS_SEMAPHORE *sem, int ms_timeout) { 87 | 88 | int retval = 0; 89 | 90 | if (pthread_mutex_lock(&sem->mutex)) 91 | return -1; 92 | 93 | while (1) { 94 | if (sem->value > 0) { 95 | sem->value--; 96 | break; 97 | } else { 98 | if (ms_timeout < 0) { 99 | if (pthread_cond_wait(&sem->cond, &sem->mutex)) { 100 | retval = -2; 101 | break; 102 | } 103 | } else { 104 | struct timespec ts; 105 | if (clock_gettime(CLOCK_REALTIME, &ts)) { 106 | retval = -3; 107 | break; 108 | } 109 | timespec_add_ms(&ts, ms_timeout); 110 | if (pthread_cond_timedwait(&sem->cond, &sem->mutex, &ts)) { 111 | retval = -4; 112 | break; 113 | } 114 | } 115 | continue; 116 | } 117 | } 118 | 119 | pthread_mutex_unlock(&sem->mutex); 120 | return retval; 121 | } 122 | 123 | int os_semaphore_post(OS_SEMAPHORE *sem) { 124 | int retval = 0; 125 | 126 | if (pthread_mutex_lock(&sem->mutex)) 127 | return -1; 128 | 129 | sem->value++; 130 | if (pthread_cond_broadcast(&sem->cond)) 131 | retval = -2; 132 | 133 | pthread_mutex_unlock(&sem->mutex); 134 | return retval; 135 | } 136 | 137 | int os_semaphore_delete(OS_SEMAPHORE *sem) { 138 | pthread_mutex_destroy(&sem->mutex); 139 | pthread_cond_destroy(&sem->cond); 140 | return 0; 141 | } 142 | 143 | void sleep_ms(int ms) 144 | { 145 | usleep(ms * 1000); 146 | } 147 | 148 | 149 | -------------------------------------------------------------------------------- /libftl/posix/threads.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file threads.h - Posix Threads Abstractions 3 | * 4 | * Copyright (c) 2015 Stefan Slivinski 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "gettimeofday/gettimeofday.h" 35 | 36 | typedef pthread_mutex_t OS_MUTEX; 37 | typedef bool BOOL; 38 | 39 | typedef pthread_t OS_THREAD_HANDLE; 40 | typedef void* OS_THREAD_TYPE; 41 | typedef void* OS_THREAD_ROUTINE; 42 | typedef void* OS_THREAD_START_ROUTINE; 43 | typedef void OS_THREAD_ATTRIBS; //todo implement attributes 44 | 45 | typedef struct { 46 | pthread_mutex_t mutex; 47 | pthread_cond_t cond; 48 | unsigned int value; 49 | }OS_SEMAPHORE; 50 | 51 | int os_init(); 52 | 53 | int os_create_thread(OS_THREAD_HANDLE *handle, OS_THREAD_ATTRIBS *attibs, OS_THREAD_START_ROUTINE func, void *args); 54 | int os_destroy_thread(OS_THREAD_HANDLE handle); 55 | int os_wait_thread(OS_THREAD_HANDLE handle); 56 | 57 | int os_init_mutex(OS_MUTEX *mutex); 58 | int os_lock_mutex(OS_MUTEX *mutex); 59 | int os_trylock_mutex(OS_MUTEX *mutex); 60 | int os_unlock_mutex(OS_MUTEX *mutex); 61 | int os_delete_mutex(OS_MUTEX *mutex); 62 | 63 | int os_semaphore_create(OS_SEMAPHORE *sem, const char *name, int oflag, unsigned int value); 64 | int os_semaphore_pend(OS_SEMAPHORE *sem, int ms_timeout); 65 | int os_semaphore_post(OS_SEMAPHORE *sem); 66 | int os_semaphore_delete(OS_SEMAPHORE *sem); 67 | 68 | void sleep_ms(int ms); 69 | 70 | 71 | -------------------------------------------------------------------------------- /libftl/win32/socket.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file socket.c - Windows Socket Abstractions 3 | * 4 | * Copyright (c) 2015 Michael Casadevall 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #include "ftl.h" 26 | #include "ftl_private.h" 27 | 28 | void init_sockets() { 29 | WSADATA wsaData; 30 | WSAStartup(MAKEWORD(2, 2), &wsaData); 31 | } 32 | 33 | int close_socket(SOCKET sock) { 34 | return closesocket(sock); 35 | } 36 | 37 | int shutdown_socket(SOCKET sock, int how) { 38 | return shutdown(sock, how); 39 | } 40 | 41 | char * get_socket_error() { 42 | int error_code = WSAGetLastError(); 43 | 44 | if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 45 | NULL, 46 | error_code, 47 | 0, 48 | (LPTSTR)&error_message, 49 | 1000, 50 | NULL) == 0) { 51 | // Err, oops, formatmessage failed -_-; 52 | return "FormatMessage() failed to process error!"; 53 | } 54 | 55 | return error_message; 56 | } 57 | 58 | int set_socket_recv_timeout(SOCKET socket, int ms_timeout){ 59 | return setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&ms_timeout, sizeof(ms_timeout)); 60 | } 61 | 62 | int set_socket_send_timeout(SOCKET socket, int ms_timeout){ 63 | return setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&ms_timeout, sizeof(ms_timeout)); 64 | } 65 | 66 | int set_socket_enable_keepalive(SOCKET socket){ 67 | int keep_alive = 1; 68 | return setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&keep_alive, sizeof(keep_alive)); 69 | } 70 | 71 | int get_socket_send_buf(SOCKET socket, int *buffer_space) { 72 | int len = sizeof(*buffer_space); 73 | return getsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char*)buffer_space, &len); 74 | } 75 | 76 | int set_socket_send_buf(SOCKET socket, int buffer_space) { 77 | return setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char*)&buffer_space, sizeof(buffer_space)); 78 | } 79 | 80 | int get_socket_bytes_available(SOCKET socket, unsigned long *bytes_available) { 81 | return ioctlsocket(socket, FIONREAD, bytes_available); 82 | } 83 | 84 | int poll_socket_for_receive(SOCKET socket, int timeoutMs) 85 | { 86 | // timeoutMs behavior 87 | // > 0 time in ms to wait 88 | // = 0 return instantly 89 | // < 0 wait forever 90 | 91 | WSAPOLLFD fd; 92 | fd.fd = socket; 93 | fd.events = POLLRDNORM; 94 | int ret = WSAPoll(&fd, 1, timeoutMs); 95 | 96 | // Function return values 97 | // = 0 timeout reached 98 | // = 1 data received 99 | // = -1 socket error 100 | if (ret == 0) 101 | { 102 | return 0; 103 | } 104 | else if (ret == 1 && fd.revents == POLLRDNORM) 105 | { 106 | return 1; 107 | } 108 | else 109 | { 110 | return SOCKET_ERROR; 111 | } 112 | } -------------------------------------------------------------------------------- /libftl/win32/socket.h: -------------------------------------------------------------------------------- 1 | void init_sockets(); 2 | int close_socket(SOCKET sock); 3 | char * get_socket_error(); 4 | int set_socket_recv_timeout(SOCKET socket, int ms_timeout); 5 | int set_socket_send_timeout(SOCKET socket, int ms_timeout); 6 | int set_socket_enable_keepalive(SOCKET socket); 7 | int get_socket_send_buf(SOCKET socket, int *buffer_space); 8 | int set_socket_send_buf(SOCKET socket, int buffer_space); 9 | int poll_socket_for_receive(SOCKET socket, int ms_timeout); 10 | int get_socket_bytes_available(SOCKET socket, unsigned long *bytes_available); 11 | int shutdown_socket(SOCKET sock, int how); 12 | -------------------------------------------------------------------------------- /libftl/win32/threads.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file threads.c - Windows Threads Abstractions 3 | * 4 | * Copyright (c) 2015 Stefan Slivinski 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #include 26 | #include "threads.h" 27 | 28 | int os_init(){ 29 | return 0; 30 | } 31 | 32 | int os_create_thread(OS_THREAD_HANDLE *handle, OS_THREAD_ATTRIBS *attibs, OS_THREAD_START_ROUTINE func, void *args) { 33 | HANDLE thread; 34 | thread = CreateThread(NULL, 0, func, args, 0, NULL); 35 | 36 | if (thread == NULL) { 37 | return -1; 38 | } 39 | 40 | *handle = thread; 41 | 42 | return 0; 43 | } 44 | 45 | int os_destroy_thread(OS_THREAD_HANDLE handle) { 46 | CloseHandle(handle); 47 | 48 | return 0; 49 | } 50 | 51 | int os_wait_thread(OS_THREAD_HANDLE handle) { 52 | WaitForSingleObject(handle, INFINITE); 53 | 54 | return 0; 55 | } 56 | 57 | int os_init_mutex(OS_MUTEX *mutex) { 58 | 59 | InitializeCriticalSection(mutex); 60 | 61 | return 0; 62 | } 63 | 64 | int os_lock_mutex(OS_MUTEX *mutex) { 65 | 66 | EnterCriticalSection(mutex); 67 | 68 | return 0; 69 | } 70 | 71 | int os_trylock_mutex(OS_MUTEX *mutex) { 72 | 73 | return TryEnterCriticalSection(mutex); 74 | } 75 | 76 | int os_unlock_mutex(OS_MUTEX *mutex) { 77 | 78 | LeaveCriticalSection(mutex); 79 | 80 | return 0; 81 | } 82 | 83 | int os_delete_mutex(OS_MUTEX *mutex) { 84 | 85 | DeleteCriticalSection(mutex); 86 | 87 | return 0; 88 | } 89 | 90 | int os_semaphore_create(OS_SEMAPHORE *sem, const char *name, int oflag, unsigned int value) { 91 | 92 | char *internal_name = NULL; 93 | int retval = 0; 94 | 95 | do { 96 | if (name == NULL) { 97 | retval = -1; 98 | break; 99 | } 100 | 101 | //if the semaphore is intended to only be used by the same process and not across processes, give it unique name 102 | size_t name_len = strlen(name); 103 | size_t max_name = name_len + 20; 104 | 105 | if ((internal_name = (char*)malloc(max_name * sizeof(char))) == NULL) { 106 | retval = -2; 107 | break; 108 | } 109 | 110 | sprintf_s(internal_name, max_name, "%s_%d", name, (unsigned int)rand()); 111 | 112 | if ( (*sem = CreateSemaphoreA(NULL, value, MAX_SEM_COUNT, (LPCSTR)internal_name)) == NULL){ 113 | retval = -3; 114 | break; 115 | } 116 | }while(0); 117 | 118 | if(internal_name != NULL){ 119 | free(internal_name); 120 | } 121 | 122 | return retval; 123 | } 124 | 125 | int os_semaphore_pend(OS_SEMAPHORE *sem, int ms_timeout) { 126 | 127 | if (WaitForSingleObject(*sem, ms_timeout) != WAIT_OBJECT_0) { 128 | return -1; 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | int os_semaphore_post(OS_SEMAPHORE *sem) { 135 | if (ReleaseSemaphore(*sem, 1, NULL)) { 136 | return 0; 137 | } 138 | 139 | return -1; 140 | } 141 | 142 | int os_semaphore_delete(OS_SEMAPHORE *sem) { 143 | if (CloseHandle(*sem)) { 144 | return 0; 145 | } 146 | 147 | return -1; 148 | } 149 | 150 | void sleep_ms(int ms) 151 | { 152 | Sleep(ms); 153 | } 154 | 155 | 156 | -------------------------------------------------------------------------------- /libftl/win32/threads.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file threads.h - Windows Threads Abstractions 3 | * 4 | * Copyright (c) 2015 Stefan Slivinski 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | **/ 24 | 25 | #include 26 | #include 27 | 28 | typedef CRITICAL_SECTION OS_MUTEX; 29 | 30 | typedef HANDLE OS_SEMAPHORE; 31 | #define MAX_SEM_COUNT 0x7FFFFFFF 32 | #define O_CREAT 0 33 | 34 | typedef HANDLE OS_THREAD_HANDLE; 35 | typedef DWORD OS_THREAD_TYPE; 36 | #define OS_THREAD_ROUTINE OS_THREAD_TYPE WINAPI 37 | typedef LPTHREAD_START_ROUTINE OS_THREAD_START_ROUTINE; 38 | typedef void OS_THREAD_ATTRIBS; //todo implement attributes 39 | 40 | 41 | #define OS_FOREVER INFINITE 42 | 43 | int os_init(); 44 | 45 | int os_create_thread(OS_THREAD_HANDLE *handle, OS_THREAD_ATTRIBS *attibs, OS_THREAD_START_ROUTINE func, void *args); 46 | int os_destroy_thread(OS_THREAD_HANDLE handle); 47 | int os_wait_thread(OS_THREAD_HANDLE handle); 48 | 49 | int os_init_mutex(OS_MUTEX *mutex); 50 | int os_lock_mutex(OS_MUTEX *mutex); 51 | int os_trylock_mutex(OS_MUTEX *mutex); 52 | int os_unlock_mutex(OS_MUTEX *mutex); 53 | int os_delete_mutex(OS_MUTEX *mutex); 54 | 55 | int os_semaphore_create(OS_SEMAPHORE *sem, const char *name, int oflag, unsigned int value); 56 | int os_semaphore_pend(OS_SEMAPHORE *sem, int ms_timeout); 57 | int os_semaphore_post(OS_SEMAPHORE *sem); 58 | int os_semaphore_delete(OS_SEMAPHORE *sem); 59 | 60 | void sleep_ms(int ms); 61 | -------------------------------------------------------------------------------- /make-deployment-yml: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu 3 | 4 | TOTAL_CPU=150 5 | TOTAL_MEM=50 6 | 7 | CPU_LIMIT=$((TOTAL_CPU)) 8 | MEM_LIMIT=$((TOTAL_MEM)) 9 | 10 | cat < docker create $DOCKER_TAG" 7 | CONTAINER_ID=$(docker create "$DOCKER_TAG") 8 | 9 | rm -rf publish 10 | 11 | echo "> docker cp ${CONTAINER_ID}:/opt/ftl-sdk/publish ." 12 | docker cp "${CONTAINER_ID}:/opt/ftl-sdk/publish" . 13 | 14 | echo "> docker rm $CONTAINER_ID" 15 | docker rm "$CONTAINER_ID" 16 | 17 | echo '> tar czvpf publish.tar.gz publish' 18 | tar czvpf publish.tar.gz publish 19 | 20 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ue 3 | 4 | fail () { 5 | local error_message="$1" 6 | local exit_code=$2 7 | echo "$error_message" >&2 8 | exit "$exit_code" 9 | } 10 | 11 | check_dependency () { 12 | local cmd="$1" 13 | command -v "$cmd" > /dev/null || fail "please install $cmd" 2 14 | } 15 | 16 | check_dependencies () { 17 | check_dependency 'curl' \ 18 | && check_dependency 'jq' 19 | } 20 | 21 | check_ingest () { 22 | local ingest="$1" 23 | [ "$ingest" = 'auto' ] \ 24 | || ping -c 1 "$ingest" \ 25 | || fail "failed to ping ingest '${ingest}'" 5 26 | } 27 | 28 | channel_online_status () { 29 | local channel="$1" 30 | curl -sL "https://mixer.com/api/v1/channels/${channel}" \ 31 | | jq .online \ 32 | | grep -E '(true)|(false)' 33 | } 34 | 35 | channel_online () { 36 | local channel="$1" 37 | channel_online_status "$channel" | grep -q 'true' && echo "${channel} online!" 38 | } 39 | 40 | channel_offline () { 41 | local channel="$1" 42 | channel_online_status "$channel" | grep -q 'false' && echo "${channel} offline!" 43 | } 44 | 45 | main () { 46 | check_dependencies 47 | check_ingest "$INGEST" 48 | 49 | local max_wait=300 50 | local sleep_duration=5 51 | local i=0 52 | echo "wait for channel ${CHANNEL:?} to go offline" 53 | until channel_offline "$CHANNEL"; do 54 | echo . 55 | sleep $sleep_duration 56 | (( i += sleep_duration )) 57 | if [ "$i" -gt $max_wait ]; then 58 | echo "channel ${CHANNEL} still online after ${max_wait} sec." 59 | echo "exiting." 60 | exit 42 61 | fi 62 | done 63 | 64 | echo "starting stream" 65 | 66 | export MIXER_STREAMER_KEY 67 | export INGEST 68 | export FPS 69 | 70 | ./start-stream & 71 | local pid="$!" 72 | 73 | local j=0 74 | echo "wait for channel ${CHANNEL} to come online" 75 | until channel_online "$CHANNEL"; do 76 | echo . 77 | sleep $sleep_duration 78 | (( j += sleep_duration )) 79 | if [ "$j" -gt $max_wait ]; then 80 | echo "channel ${CHANNEL} still offline after ${max_wait} sec." 81 | echo "exiting." 82 | exit 43 83 | fi 84 | 85 | ps -p "$pid" > /dev/null \ 86 | || fail "ftl_app (pid ${pid}) exited unexpectedly. exiting" 44 87 | done 88 | 89 | echo "channel ${CHANNEL} is online!" 90 | 91 | kill -INT "$pid" 92 | wait "$pid" 93 | } 94 | 95 | main 96 | -------------------------------------------------------------------------------- /start-stream: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ue 3 | 4 | : "${INGEST:=auto}" 5 | : "${FPS:=24000:1001}" 6 | 7 | echo "streaming to $INGEST" 8 | echo "FPS = $FPS" 9 | 10 | exec ./publish/ftl_app \ 11 | -i "$INGEST" \ 12 | -s "$MIXER_STREAMER_KEY" \ 13 | -v vid/sintel.h264 \ 14 | -a vid/sintel.opus \ 15 | -f "$FPS" 16 | 17 | --------------------------------------------------------------------------------