├── .gitignore
├── README.md
├── build
├── CMakeLists.txt
├── Triplet.cmake
├── build.sh
├── build_linux_dependencies.sh
├── build_mac_dependencies.sh
├── clean.sh
├── debug.sh
└── release.sh
├── html
├── signaling.js
├── style.css
├── test_sending.html
└── test_signaling.html
├── include
├── dtls
│ ├── Context.h
│ └── Parser.h
├── ice
│ ├── Agent.h
│ ├── Candidate.h
│ ├── Stream.h
│ └── Utils.h
├── rtc
│ └── Connection.h
├── rtp
│ ├── PacketVP8.h
│ ├── ReaderVP8.h
│ └── WriterVP8.h
├── sdp
│ ├── Reader.h
│ ├── SDP.h
│ ├── Types.h
│ ├── Utils.h
│ └── Writer.h
├── signaling
│ ├── Room.h
│ ├── Signaling.h
│ └── SignalingSettings.h
├── srtp
│ └── ParserSRTP.h
├── stun
│ ├── Attribute.h
│ ├── Message.h
│ ├── Reader.h
│ ├── Types.h
│ ├── Utils.h
│ └── Writer.h
└── video
│ ├── AggregatorVP8.h
│ ├── DecoderVP8.h
│ ├── EncoderSettings.h
│ ├── EncoderVP8.h
│ └── WriterIVF.h
└── src
├── dtls
├── Context.cpp
└── Parser.cpp
├── ice
├── Agent.cpp
├── Candidate.cpp
├── Stream.cpp
└── Utils.cpp
├── rtc
└── Connection.cpp
├── rtp
├── PacketVP8.cpp
├── ReaderVP8.cpp
└── WriterVP8.cpp
├── sdp
├── Reader.cpp
├── SDP.cpp
├── Types.cpp
├── Utils.cpp
└── Writer.cpp
├── signaling
├── Room.cpp
├── Signaling.cpp
└── SignalingSettings.cpp
├── srtp
└── ParserSRTP.cpp
├── stun
├── Attribute.cpp
├── Message.cpp
├── Reader.cpp
├── Types.cpp
├── Utils.cpp
└── Writer.cpp
├── test_webrtc_dtls.cpp
├── test_webrtc_extract_keying_info_for_srtp.cpp
├── test_webrtc_hmac_sha1.cpp
├── test_webrtc_ice.cpp
├── test_webrtc_ice_agent.cpp
├── test_webrtc_libwebsockets.cpp
├── test_webrtc_mongoose.cpp
├── test_webrtc_openssl_load_key_and_cert.cpp
├── test_webrtc_signaling.cpp
├── test_webrtc_ssl_fingerprint.cpp
├── test_webrtc_stun_message_fingerprint.cpp
├── test_webrtc_stun_message_integrity.cpp
├── test_webrtc_video_encoder.cpp
├── test_webrtc_zlib_crc32.cpp
└── video
├── AggregatorVP8.cpp
├── DecoderVP8.cpp
├── EncoderSettings.cpp
├── EncoderVP8.cpp
└── WriterIVF.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | install
2 | extern
3 | build
4 | shared
5 |
6 | ###########################################################################################
7 |
8 | *.com
9 | *.class
10 | *.dll
11 | *.exe
12 | *.o
13 | *.so
14 | *.7z
15 | *.dmg
16 | *.gz
17 | *.iso
18 | *.jar
19 | *.rar
20 | *.tar
21 | *.zip
22 | *.log
23 | *.sql
24 | *.sqlite
25 | .DS_Store
26 | .DS_Store?
27 | ._*
28 | .Spotlight-V100
29 | .Trashes
30 | ehthumbs.db
31 | Thumbs.db
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ````text
2 |
3 | +---------------+
4 | | |
5 | +--> + dtls::Context |
6 | | | |
7 | | +---------------+
8 | |
9 | +---------+ | +---------+
10 | | | | | |
11 | | Agent + ---+--> + Stream +
12 | | | | |
13 | +---------+ +----+----+
14 | |
15 | |
16 | V
17 | +------+------+
18 | | |
19 | | Candidate +
20 | | |
21 | +------+------+ +---------------+
22 | | | |
23 | +------------------------------> + dtls::Parser |
24 | | | |
25 | | +---------------+
26 | |
27 | | +---------------------+
28 | | | |
29 | +------------------------------> + rtc::ConnectionUDP |
30 | | | |
31 | | +---------------------+
32 | |
33 | | +---------------------+
34 | | | |
35 | +------------------------------> + stun::Reader |
36 | | |
37 | +---------------------+
38 |
39 |
40 |
41 | ````
--------------------------------------------------------------------------------
/build/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8)
2 |
3 | include(${CMAKE_CURRENT_LIST_DIR}/Triplet.cmake)
4 |
5 | set(app webrtc)
6 | set(lib webrtc)
7 | set(sd ${CMAKE_CURRENT_LIST_DIR}/../src/)
8 | set(id ${CMAKE_CURRENT_LIST_DIR}/../include/)
9 | set(debug "")
10 |
11 | if(CMAKE_BUILD_TYPE STREQUAL Debug)
12 | set(app "${app}_debug")
13 | set(debug "debug")
14 | endif()
15 |
16 | #add_definitions("-fsanitize-address-zero-base-shadow")
17 | #add_definitions("-fsanitize=address")
18 |
19 | add_definitions(-DUSE_WEBSOCKET)
20 |
21 | if (UNIX AND NOT APPLE)
22 | add_definitions("-std=c++0x")
23 | endif()
24 |
25 | include_directories(
26 | ${CMAKE_CURRENT_LIST_DIR}/../src
27 | ${CMAKE_CURRENT_LIST_DIR}/../include
28 | ${extern_include_dir}
29 | ${CMAKE_CURRENT_LIST_DIR}/../extern/tinylib/src
30 | )
31 |
32 | set(lib_sources
33 | ${sd}/sdp/SDP.cpp
34 | ${sd}/sdp/Utils.cpp
35 | ${sd}/sdp/Types.cpp
36 | ${sd}/sdp/Reader.cpp
37 | ${sd}/sdp/Writer.cpp
38 | ${sd}/stun/Reader.cpp
39 | ${sd}/stun/Writer.cpp
40 | ${sd}/stun/Message.cpp
41 | ${sd}/stun/Attribute.cpp
42 | ${sd}/stun/Types.cpp
43 | ${sd}/stun/Utils.cpp
44 | ${sd}/ice/Utils.cpp
45 | ${sd}/ice/Candidate.cpp
46 | ${sd}/ice/Agent.cpp
47 | ${sd}/ice/Stream.cpp
48 | ${sd}/dtls/Context.cpp
49 | ${sd}/dtls/Parser.cpp
50 | ${sd}/rtc/Connection.cpp
51 | ${sd}/srtp/ParserSRTP.cpp
52 | ${sd}/rtp/ReaderVP8.cpp
53 | ${sd}/rtp/WriterVP8.cpp
54 | ${sd}/rtp/PacketVP8.cpp
55 | ${sd}/video/AggregatorVP8.cpp
56 | ${sd}/video/WriterIVF.cpp
57 | ${sd}/video/EncoderVP8.cpp
58 | ${sd}/video/DecoderVP8.cpp
59 | ${sd}/video/EncoderSettings.cpp
60 |
61 | # signaling
62 | ${sd}/signaling/Signaling.cpp
63 | ${sd}/signaling/Room.cpp
64 | ${sd}/signaling/SignalingSettings.cpp
65 | ${extern_source_dir}/mongoose.c
66 | ${extern_source_dir}/net_skeleton.c
67 | ${extern_source_dir}/ssl_wrapper.c
68 | )
69 |
70 | set(lib_headers
71 | ${id}
72 | )
73 |
74 | set(app_libs
75 | ${extern_lib_dir}/libssl.a # for hmac/sha/ssl
76 | ${extern_lib_dir}/libcrypto.a # for hmac/sha/ssl
77 | ${extern_lib_dir}/libuv.a # for networking
78 | ${extern_lib_dir}/libz.a # for crc32
79 | ${extern_lib_dir}/libsrtp.a # used to handle SRTP packets.
80 | ${extern_lib_dir}/libvpx.a # encoding/decoding vp8
81 | ${extern_lib_dir}/libvideogenerator.a # used for test purposes;
82 | )
83 |
84 | if (UNIX AND NOT APPLE)
85 | list(APPEND app_libs
86 | pthread
87 | dl
88 | )
89 | endif()
90 |
91 | set(webrtc_sources ${lib_sources})
92 | set(webrtc_libs ${app_libs})
93 |
94 | macro(create_test name)
95 | add_executable("test_webrtc_${name}${debug}" ${sd}/test_webrtc_${name}.cpp)
96 | target_link_libraries("test_webrtc_${name}${debug}" ${lib} ${app_libs})
97 | install(TARGETS "test_webrtc_${name}${debug}" DESTINATION bin)
98 | endmacro()
99 |
100 | add_library(${lib} STATIC ${lib_sources})
101 | install(TARGETS ${lib} ARCHIVE DESTINATION lib)
102 | install(DIRECTORY ${lib_headers} DESTINATION include)
103 |
104 | # temporary disable the webrtc tests
105 | create_test(ice)
106 | create_test(ssl_fingerprint)
107 | create_test(hmac_sha1)
108 | create_test(stun_message_integrity)
109 | create_test(zlib_crc32)
110 | create_test(stun_message_fingerprint)
111 | create_test(openssl_load_key_and_cert)
112 | create_test(ice_agent)
113 | create_test(dtls)
114 | create_test(extract_keying_info_for_srtp)
115 | create_test(video_encoder)
116 | create_test(mongoose)
117 | create_test(signaling)
118 |
--------------------------------------------------------------------------------
/build/Triplet.cmake:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8)
2 |
3 | # ${tri_arch} - x86_64, i386
4 | # ${tri_compiler} - clang, gcc, vs2010, vs2012
5 | # ${tri_platform} - mac, win, unix
6 | # ${tri_triplet} - ${tri_platform}-${tri_compiler}-${tri_arch}
7 | # ${install_dir} - the install prefix
8 | # ${extern_source_dir} - path to the source directory inside extern/${tri_triplet}/
9 | # ${extern_lib_dir} - path to the extern libraries (root)
10 | # ${extern_include_dir} - include directories in for the extern libraries
11 |
12 | if(NOT ROXLU_USE_32BIT)
13 | set(tri_arch "x86_64")
14 | else()
15 | set(tri_arch "i386")
16 | endif()
17 |
18 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
19 | set(tri_compiler "clang")
20 | elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
21 | set(tri_compiler "gcc")
22 | elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
23 | if(MSVC10)
24 | set(tri_compiler "vs2010")
25 | else()
26 | set(tri_compiler "vs2012")
27 | endif()
28 | endif()
29 |
30 | if(APPLE)
31 | set(tri_platform "mac")
32 | elseif(WIN32)
33 | set(tri_platform "win")
34 | else()
35 | set(tri_platform "linux")
36 | endif()
37 |
38 | set(tri_triplet "${tri_platform}-${tri_compiler}-${tri_arch}")
39 |
40 | if(CMAKE_BUILD_TYPE STREQUAL "Debug")
41 | set(tri_triplet "${tri_triplet}d")
42 | endif()
43 |
44 | set(install_dir ${CMAKE_CURRENT_LIST_DIR}/../install/${tri_triplet})
45 | set(extern_source_dir ${CMAKE_CURRENT_LIST_DIR}/../extern/${tri_triplet}/src/)
46 | set(extern_lib_dir ${CMAKE_CURRENT_LIST_DIR}/../extern/${tri_triplet}/lib/)
47 | set(extern_include_dir ${CMAKE_CURRENT_LIST_DIR}/../extern/${tri_triplet}/include)
48 |
49 | if (NOT CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
50 | set(CMAKE_INSTALL_PREFIX ${install_dir})
51 | endif()
52 |
53 | message(STATUS "Building for ${tri_triplet}")
54 | message(STATUS "Extern include dir: ${extern_include_dir}")
55 |
--------------------------------------------------------------------------------
/build/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ ! -d build.release ] ; then
4 | mkdir build.release
5 | fi
6 |
7 | cd build.release
8 | cmake -DCMAKE_BUILD_TYPE=Release ../
9 | cmake --build . --target install
10 |
11 |
--------------------------------------------------------------------------------
/build/build_linux_dependencies.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -x
3 | d=${PWD}
4 | sd=${d}/linux-sources
5 | bd=${d}/../extern/linux-gcc-x86_64
6 |
7 | export PATH=${PATH}:${bd}/bin/
8 | export CFLAGS="-I\"${bd}/include\""
9 | export LDFLAGS="-L\"${bd}/lib\""
10 |
11 | # ----------------------------------------------------------------------- #
12 | # D O W N L O A D D E P E N D E N C I E S
13 | # ----------------------------------------------------------------------- #
14 |
15 | # Create source dir
16 | if [ ! -d ${sd} ] ; then
17 | mkdir -p ${sd}
18 | fi
19 |
20 | if [ ! -d ${bd}/src ] ; then
21 | mkdir -p ${bd}/src
22 | fi
23 |
24 | # Download openssl
25 | if [ ! -d ${sd}/openssl ] ; then
26 | cd ${sd}
27 | if [ ! -f openssl.tar.gz ] ; then
28 | curl -o openssl.tar.gz http://www.openssl.org/source/openssl-1.0.1i.tar.gz
29 | tar -zxvf openssl.tar.gz
30 | fi
31 | mv openssl-1.0.1i openssl
32 | fi
33 |
34 | # Download libuv
35 | if [ ! -d ${sd}/libuv ] ; then
36 | cd ${sd}
37 | git clone https://github.com/joyent/libuv.git libuv
38 | fi
39 |
40 | # Download libz
41 | if [ ! -d ${sd}/zlib ] ; then
42 | cd ${sd}
43 | if [ ! -f libz.tar.gz ] ; then
44 | curl -o libz.tar.gz http://zlib.net/zlib-1.2.8.tar.gz
45 | tar -zxvf libz.tar.gz
46 | fi
47 | mv zlib-1.2.8 zlib
48 | fi
49 |
50 | # Download libsrtp
51 | if [ ! -d ${sd}/libsrtp ] ; then
52 | cd ${sd}
53 | git clone https://github.com/cisco/libsrtp.git libsrtp
54 | fi
55 |
56 | # Download libvpx
57 | if [ ! -d ${sd}/libvpx ] ; then
58 | cd ${sd}
59 | git clone https://chromium.googlesource.com/webm/libvpx libvpx
60 | fi
61 |
62 | # Download libvideogenerator
63 | if [ ! -d ${sd}/libvideogenerator ] ; then
64 | cd ${sd}
65 | git clone git@github.com:roxlu/video_generator.git libvideogenerator
66 | fi
67 |
68 | # Download yasm, needed for libvpx
69 | if [ ! -d ${sd}/yasm ] ; then
70 | cd ${sd}
71 | curl -o yasm.tar.gz http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
72 | tar -zxvf yasm.tar.gz
73 | mv yasm-1.3.0 yasm
74 | fi
75 |
76 | # Download mongoose (signaling)
77 | if [ ! -d ${sd}/mongoose ] ; then
78 | cd ${sd}
79 | git clone https://github.com/cesanta/mongoose.git mongoose
80 | fi
81 |
82 | if [ ! -f ${bd}/src/mongoose.c ] ; then
83 | cp ${sd}/mongoose/mongoose.c ${bd}/src/
84 | cp ${sd}/mongoose/mongoose.h ${bd}/include/
85 | fi
86 |
87 | # Download net_skeleton (signaling)
88 | if [ ! -d ${sd}/net_skeleton ] ; then
89 | cd ${sd}
90 | git clone https://github.com/cesanta/net_skeleton.git net_skeleton
91 | fi
92 |
93 | if [ ! -f ${bd}/src/net_skeleton.c ] ; then
94 | cp ${sd}/net_skeleton/net_skeleton.c ${bd}/src/
95 | cp ${sd}/net_skeleton/net_skeleton.h ${bd}/include/
96 | fi
97 |
98 | # Download ssl_wrapper (signaling)
99 | if [ ! -d ${sd}/ssl_wrapper ] ; then
100 | cd ${sd}
101 | git clone https://github.com/cesanta/ssl_wrapper.git ssl_wrapper
102 | fi
103 |
104 | if [ ! -f ${bd}/src/ssl_wrapper.c ] ; then
105 | cp ${sd}/ssl_wrapper/ssl_wrapper.c ${bd}/src/
106 | cp ${sd}/ssl_wrapper/ssl_wrapper.h ${bd}/include/
107 | fi
108 |
109 | # ----------------------------------------------------------------------- #
110 | # C O M P I L E D E P E N D E N C I E S
111 | # ----------------------------------------------------------------------- #
112 |
113 | # Compile openSSL
114 | if [ ! -f ${bd}/lib/libssl.a ] ; then
115 | cd ${sd}/openssl
116 | ./Configure --prefix=${bd} linux-x86_64
117 | make clean
118 | make
119 | make install
120 | fi
121 |
122 | # Compile libuv
123 | if [ ! -f ${bd}/lib/libuv.a ] ; then
124 | cd ${sd}/libuv
125 | ./autogen.sh
126 | ./configure --prefix=${bd} --enable-static
127 | make
128 | make install
129 | fi
130 |
131 | # Compile zlib
132 | if [ ! -f ${bd}/lib/libz.a ] ; then
133 | cd ${sd}/zlib
134 | ./configure --prefix=${bd} --static --64
135 | make
136 | make install
137 | fi
138 |
139 | # Compile libsrtp
140 | if [ ! -f ${bd}/lib/libsrtp.a ] ; then
141 | cd ${sd}/libsrtp
142 | ./configure --prefix=${bd}
143 | make
144 | make install
145 | fi
146 |
147 | # Compile yasm
148 | if [ ! -f ${bd}/bin/yasm ] ; then
149 | cd ${sd}/yasm
150 | ./configure --prefix=${bd}
151 | make
152 | make install
153 | fi
154 |
155 | # Compile libvpx
156 | if [ ! -f ${bd}/lib/libvpx.a ] ; then
157 | cd ${sd}/libvpx
158 | ./configure --prefix=${bd} --as=yasm --disable-shared --enable-static
159 | make
160 | make install
161 | fi
162 |
163 | # Compile libvideogenerator
164 | if [ ! -f ${bd}/lib/libvideogenerator.a ] ; then
165 | cd ${sd}/libvideogenerator/build
166 | cmake -DCMAKE_INSTALL_PREFIX=${bd}
167 | cmake --build . --target install
168 | fi
169 |
170 |
171 |
--------------------------------------------------------------------------------
/build/build_mac_dependencies.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -x
3 | d=${PWD}
4 | sd=${d}/mac-sources
5 | bd=${d}/../extern/mac-clang-x86_64
6 |
7 | export PATH=${PATH}:${bd}/bin/:${sd}/gyp/
8 | export CFLAGS="-I\"${bd}/include\""
9 | export LDFLAGS="-L\"${bd}/lib\""
10 |
11 | # ----------------------------------------------------------------------- #
12 | # D O W N L O A D D E P E N D E N C I E S
13 | # ----------------------------------------------------------------------- #
14 | if [ ! -d ${sd} ] ; then
15 | mkdir -p ${sd}
16 | fi
17 |
18 | if [ ! -d ${bd}/src ] ; then
19 | mkdir ${bd}/src
20 | fi
21 |
22 | # Download autoconf and friends (for libuv)
23 | if [ ! -d ${sd}/autoconf ] ; then
24 | cd ${sd}
25 | curl -o autoconf.tar.gz http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz
26 | tar -zxvf autoconf.tar.gz
27 | mv autoconf-2.69 autoconf
28 | fi
29 |
30 | # Download libtool
31 | if [ ! -d ${sd}/libtool ] ; then
32 | cd ${sd}
33 | curl -o libtool.tar.gz http://ftp.gnu.org/gnu/libtool/libtool-2.4.2.tar.gz
34 | tar -zxvf libtool.tar.gz
35 | mv libtool-2.4.2 libtool
36 | fi
37 |
38 | # Download automake
39 | if [ ! -d ${sd}/automake ] ; then
40 | cd ${sd}
41 | curl -o automake.tar.gz http://ftp.gnu.org/gnu/automake/automake-1.14.tar.gz
42 | tar -zxvf automake.tar.gz
43 | mv automake-1.14 automake
44 | fi
45 |
46 | # Download openssl
47 | if [ ! -d ${sd}/openssl ] ; then
48 | cd ${sd}
49 | if [ ! -f openssl.tar.gz ] ; then
50 | curl -o openssl.tar.gz http://www.openssl.org/source/openssl-1.0.1i.tar.gz
51 | tar -zxvf openssl.tar.gz
52 | fi
53 | mv openssl-1.0.1i openssl
54 | fi
55 |
56 | # Download libuv
57 | if [ ! -d ${sd}/libuv ] ; then
58 | cd ${sd}
59 | git clone https://github.com/joyent/libuv.git libuv
60 | fi
61 |
62 | # Download gyp for libuv
63 | if [ ! -d ${sd}/libuv/build/gyp ] ; then
64 | cd ${sd}/libuv
65 | git clone https://git.chromium.org/external/gyp.git build/gyp
66 | fi
67 |
68 | # Download libz
69 | if [ ! -d ${sd}/zlib ] ; then
70 | cd ${sd}
71 | if [ ! -f libz.tar.gz ] ; then
72 | curl -o libz.tar.gz http://zlib.net/zlib-1.2.8.tar.gz
73 | tar -zxvf libz.tar.gz
74 | fi
75 | mv zlib-1.2.8 zlib
76 | fi
77 |
78 | # Download libsrtp
79 | if [ ! -d ${sd}/libsrtp ] ; then
80 | cd ${sd}
81 | git clone https://github.com/cisco/libsrtp.git libsrtp
82 | fi
83 |
84 | # Download libvpx
85 | if [ ! -d ${sd}/libvpx ] ; then
86 | cd ${sd}
87 | git clone https://chromium.googlesource.com/webm/libvpx libvpx
88 | fi
89 |
90 | # Download libvideogenerator
91 | if [ ! -d ${sd}/libvideogenerator ] ; then
92 | cd ${sd}
93 | git clone git@github.com:roxlu/video_generator.git libvideogenerator
94 | fi
95 |
96 | # Download yasm, needed for libvpx
97 | if [ ! -d ${sd}/yasm ] ; then
98 | cd ${sd}
99 | curl -o yasm.tar.gz http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
100 | tar -zxvf yasm.tar.gz
101 | mv yasm-1.3.0 yasm
102 | fi
103 |
104 | # Download mongoose (signaling)
105 | if [ ! -d ${sd}/mongoose ] ; then
106 | cd ${sd}
107 | git clone https://github.com/cesanta/mongoose.git mongoose
108 | fi
109 |
110 | if [ ! -f ${bd}/src/mongoose.c ] ; then
111 | cp ${sd}/mongoose/mongoose.c ${bd}/src/
112 | cp ${sd}/mongoose/mongoose.h ${bd}/include/
113 | fi
114 |
115 | # Download net_skeleton (signaling)
116 | if [ ! -d ${sd}/net_skeleton ] ; then
117 | cd ${sd}
118 | git clone https://github.com/cesanta/net_skeleton.git net_skeleton
119 | fi
120 |
121 | if [ ! -f ${bd}/src/net_skeleton.c ] ; then
122 | cp ${sd}/net_skeleton/net_skeleton.c ${bd}/src/
123 | cp ${sd}/net_skeleton/net_skeleton.h ${bd}/include/
124 | fi
125 |
126 | # Download ssl_wrapper (signaling)
127 | if [ ! -d ${sd}/ssl_wrapper ] ; then
128 | cd ${sd}
129 | git clone https://github.com/cesanta/ssl_wrapper.git ssl_wrapper
130 | fi
131 |
132 | if [ ! -f ${bd}/src/ssl_wrapper.c ] ; then
133 | cp ${sd}/ssl_wrapper/ssl_wrapper.c ${bd}/src/
134 | cp ${sd}/ssl_wrapper/ssl_wrapper.h ${bd}/include/
135 | fi
136 |
137 | # Cleanup some files we don't need anymore.
138 | if [ -f ${sd}/autoconf.tar.gz ] ; then
139 | rm ${sd}/autoconf.tar.gz
140 | fi
141 | if [ -f ${sd}/automake.tar.gz ] ; then
142 | rm ${sd}/automake.tar.gz
143 | fi
144 | if [ -f ${sd}/libtool.tar.gz ] ; then
145 | rm ${sd}/libtool.tar.gz
146 | fi
147 | if [ -f ${sd}/libz.tar.gz ] ; then
148 | rm ${sd}/libz.tar.gz
149 | fi
150 | if [ -f ${sd}/openssl.tar.gz ] ; then
151 | rm ${sd}/openssl.tar.gz
152 | fi
153 | if [ -f ${sd}/yasm.tar.gz ] ; then
154 | rm ${sd}/yasm.tar.gz
155 | fi
156 |
157 | # ----------------------------------------------------------------------- #
158 | # C O M P I L E D E P E N D E N C I E S
159 | # ----------------------------------------------------------------------- #
160 |
161 | # Compile autoconf
162 | if [ ! -f ${bd}/bin/autoconf ] ; then
163 | cd ${sd}/autoconf
164 | ./configure --prefix=${bd}
165 | make
166 | make install
167 | fi
168 |
169 | # Compile libtool
170 | if [ ! -f ${bd}/bin/libtool ] ; then
171 | cd ${sd}/libtool
172 | ./configure --prefix=${bd}
173 | make
174 | make install
175 | fi
176 |
177 | if [ ! -f ${bd}/bin/automake ] ; then
178 | cd ${sd}/automake
179 | ./configure --prefix=${bd}
180 | make
181 | make install
182 | fi
183 |
184 | # Compile openSSL
185 | if [ ! -f ${bd}/lib/libssl.a ] ; then
186 | cd ${sd}/openssl
187 | ./Configure --prefix=${bd} darwin64-x86_64-cc
188 | make clean
189 | make
190 | make install
191 | fi
192 |
193 | # Compile libuv
194 | if [ ! -f ${bd}/lib/libuv.a ] ; then
195 | cd ${sd}/libuv
196 | ./gyp_uv.py -f xcode
197 | xcodebuild -ARCHS="x86_64" -project uv.xcodeproj -configuration Release -target All
198 | cp ${sd}/libuv/build/Release/libuv.a ${bd}/lib/
199 | cp ${sd}/libuv/include/*.h ${bd}/include/
200 | fi
201 |
202 | # Compile zlib
203 | if [ ! -f ${bd}/lib/libz.a ] ; then
204 | cd ${sd}/zlib
205 | ./configure --prefix=${bd} --static --64
206 | make
207 | make install
208 | fi
209 |
210 | # Compile libsrtp
211 | if [ ! -f ${bd}/lib/libsrtp.a ] ; then
212 | cd ${sd}/libsrtp
213 | ./configure --prefix=${bd}
214 | make
215 | make install
216 | fi
217 |
218 | # Compile yasm
219 | if [ ! -f ${bd}/bin/yasm ] ; then
220 | cd ${sd}/yasm
221 | ./configure --prefix=${bd}
222 | make
223 | make install
224 | fi
225 |
226 | # Compile libvpx
227 | if [ ! -f ${bd}/lib/libvpx.a ] ; then
228 | cd ${sd}/libvpx
229 | ./configure --prefix=${bd} --as=yasm --disable-shared --enable-static
230 | make
231 | make install
232 | fi
233 |
234 | # Compile libvideogenerator
235 | if [ ! -f ${bd}/lib/libvideogenerator.a ] ; then
236 | cd ${sd}/libvideogenerator/build
237 | cmake -DCMAKE_INSTALL_PREFIX=${bd}
238 | cmake --build . --target install
239 | fi
240 |
241 |
--------------------------------------------------------------------------------
/build/clean.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ -d build ] ; then
4 | rm -r build
5 | fi
6 |
7 | if [ -d build.release ] ; then
8 | rm -r build.release
9 | fi
10 |
11 | if [ -d build.debug ] ; then
12 | rm -r build.debug
13 | fi
14 |
--------------------------------------------------------------------------------
/build/debug.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ ! -d build.debug ] ; then
4 | mkdir build.debug
5 | fi
6 |
7 | cd build.debug
8 | cmake -DCMAKE_BUILD_TYPE=Debug ../
9 | cmake --build . --target install
10 | cd ./../../install/mac-clang-x86_64d/bin/
11 | lldb ./test_extract_keying_info_for_srtpdebug
12 |
--------------------------------------------------------------------------------
/build/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | d=${PWD}
4 |
5 | if [ ! -d build.release ] ; then
6 | mkdir build.release
7 | fi
8 |
9 | if [ "$(uname)" == "Darwin" ] ; then
10 | if [ ! -d ${d}/../extern/mac-clang-x86_64 ] ; then
11 | ./build_mac_dependencies.sh
12 | cd build.release
13 | fi
14 | else
15 | if [ ! -d ${d}/../extern/linux-gcc-x86_x64 ] ; then
16 | ./build_linux_dependencies.sh
17 | fi
18 | fi
19 |
20 | cd build.release
21 | cmake -DCMAKE_BUILD_TYPE=Release ../
22 | cmake --build . --target install
23 |
24 | if [ "$(uname)" == "Darwin" ] ; then
25 | export PATH=${d}/../install/mac-clang-x86_64/bin/:${PATH}
26 | cd ./../../install/mac-clang-x86_64/bin/
27 | if [ ! -f server-key.pem ] ; then
28 | openssl req -x509 -newkey rsa:2048 -days 3650 -nodes -subj "/C=/ST=/L=/O=/CN=roxlu.com" -keyout server-key.pem -out server-cert.pem
29 | fi
30 | else
31 | export PATH=${d}/../install/linux-gcc-x86_64/bin/:${PATH}
32 | cd ./../../install/linux-gcc-x86_64/bin/
33 | if [ ! -f server-key.pem ] ; then
34 | openssl req -x509 -newkey rsa:2048 -days 3650 -nodes -subj "/C=/ST=/L=/O=/CN=roxlu.com" -keyout server-key.pem -out server-cert.pem
35 | fi
36 | fi
37 |
38 | #./test_ice
39 | #./test_ssl_fingerprint
40 | #./test_zlib_crc32
41 | #./test_hmac_sha1
42 | #./test_stun_message_integrity
43 | #./test_stun_message_fingerprint
44 | #./test_openssl_load_key_and_cert
45 | #./test_libwebsockets
46 | ./test_ice_agent
47 | #./test_extract_keying_info_for_srtp
48 | #./test_video_encoder
49 | #./test_mongoose
50 | #./test_signaling
51 |
--------------------------------------------------------------------------------
/html/signaling.js:
--------------------------------------------------------------------------------
1 | /*
2 | Signaling
3 | ---------
4 |
5 | Connects to the signaling server, tries to find the room on the signaling server and
6 | receives the SDP for this room.
7 |
8 |
9 |
10 |
11 | sig.onjoin = function(room, sdp) {
12 | console.log(room, sdp);
13 | }
14 |
15 | sig.onconnect = function() {
16 | sig.join("party");
17 | }
18 |
19 | sig.connect("ws://127.0.0.1:9001");
20 |
21 |
22 |
23 | */
24 | var Signaling = function() {
25 |
26 | var me = this;
27 | this.is_connected = false; /* used to keep state of our connection */
28 | this.onjoin = null; /* gets called when you joined a room; SDP is given back. */
29 | this.onconnect = null; /* gets called when connected. */
30 |
31 | this.connect = function(url) {
32 |
33 | this.url = url;
34 |
35 | this.conn = new WebSocket(url);
36 |
37 | this.conn.onopen = function(ev) {
38 | me.is_connected = true
39 |
40 | if (me.onconnect) {
41 | me.onconnect();
42 | }
43 | }
44 |
45 | this.conn.onclose = function(ev) {
46 | me.is_connected = false;
47 | }
48 |
49 | this.conn.onerror = function(ev) {
50 | console.error("onerror", ev);
51 | }
52 |
53 | this.conn.onmessage = function(ev) {
54 |
55 | var el = ev.data.split(' ');
56 | if (null != me.onjoin && el[0] == "sdp") {
57 | me.onjoin(el[1], el.slice(2, el.length).join(" ") );
58 | }
59 | else {
60 | console.warn("Signaling - unhandled message.");
61 | }
62 | }
63 | }
64 |
65 | this.join = function(room) {
66 |
67 | if (!room) {
68 | return false;
69 | }
70 |
71 | if (!me.is_connected) {
72 | console.error("Not connected yet; cannot join.");
73 | return false;
74 | }
75 |
76 | this.conn.send("join " +room);
77 | }
78 | };
79 |
--------------------------------------------------------------------------------
/html/style.css:
--------------------------------------------------------------------------------
1 |
2 | * {
3 | font-family: Arial, sans-serif;
4 | font-family: 'Roboto', sans-serif;
5 | font-weight: 300;
6 | }
7 |
8 | body {
9 | background-color: whitesmoke;
10 | }
11 |
12 | button {
13 | background-color: #d84a38;
14 | border: none;
15 | border-radius: 2px;
16 | color: white;
17 | font-family: 'Roboto', sans-serif;
18 | font-size: 0.8em;
19 | margin: 0 0 1em 0;
20 | padding: 0.5em 0.7em 0.6em 0.7em;
21 | }
22 |
23 | button:active {
24 | background-color: #cf402f;
25 | }
26 |
27 | button:hover {
28 | background-color: #cf402f;
29 | }
30 |
31 | button[disabled] {
32 | color: #ccc;
33 | }
34 |
35 | button[disabled]:hover {
36 | background-color: #d84a38;
37 | }
38 |
39 | h1 {
40 | border-bottom: 1px solid #ccc;
41 | font-family: 'Roboto', sans-serif;
42 | font-weight: 500;
43 | margin: 0 0 0.8em 0;
44 | padding: 0 0 0.2em 0;
45 | }
46 |
47 | h2 {
48 | color: #444;
49 | font-size: 1em;
50 | font-weight: 500;
51 | line-height: 1.2em;
52 | margin: 0 0 0.8em 0;
53 | }
54 |
55 | h3 {
56 | border-top: 1px solid #eee;
57 | color: #666;
58 | font-size: 0.9em;
59 | font-weight: 500;
60 | margin: 20px 0 10px 0;
61 | padding: 10px 0 0 0;
62 | white-space: nowrap;
63 | }
64 |
65 | label {
66 | display: inline-block;
67 | }
68 |
69 | .form label {
70 | width: 100px;
71 | }
72 |
73 | input {
74 | border: 1px solid #ccc;
75 | padding: 5px;
76 | margin: 2px;
77 | }
78 |
79 | input.big {
80 | width: 400px;
81 | }
82 |
83 | /* ------------------------------ */
84 | #container {
85 | margin: 0 auto 0 auto;
86 | max-width: 40em;
87 | padding: 1em 1.5em 1.3em 1.5em;
88 | }
89 |
90 | textarea {
91 | font-family:Courier, mono-space;
92 | font-size:10px;
93 | width: 100%;
94 | height:200px;
95 | margin-top:10px;
96 | border: 1px solid #ccc;
97 | padding: 5px;
98 | }
99 |
100 | #start_streaming {
101 | position:absolute;
102 | left:350px;
103 | top:440px;
104 | }
105 |
106 | #output_container {
107 | position:absolute;
108 | left:auto;
109 | right:0;
110 | }
111 |
112 | .video_container video {
113 | margin: 0 auto;
114 | display: block;
115 | background-color: #ccc;
116 | }
117 |
118 | .form_row {
119 | margin-bottom: 10px;
120 | }
121 |
--------------------------------------------------------------------------------
/html/test_signaling.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WebRTC
6 |
7 |
8 |
9 |
10 |
11 |
35 |
36 |
37 |
38 |
39 |
libwebrtc signaling example
40 |
Send data
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/include/dtls/Context.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 |
4 | dtls::Context
5 | -------------
6 |
7 | Context for openSSL DTLS features. You can use this to craete a SSL_CTX and from
8 | that instantiate SSL* objects that can be used to encrypt/decrypt your data. This
9 | is created for the WebRTC DTLS part. This class allows you to automatically generate
10 | a certificate and key or load them from file.
11 |
12 | ** NOTE **
13 | @todo - update dtls::Context.h info when we added support for passwords.
14 | ** NOTE **
15 |
16 | Create server/client self-signed certificate/key (self signed, DONT ADD PASSWORD)
17 | --
18 | openssl req -x509 -newkey rsa:2048 -days 3650 -nodes -keyout client-key.pem -out client-cert.pem
19 | openssl req -x509 -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-cert.pem
20 | --
21 |
22 | */
23 | #ifndef DTLS_CONTEXT_H
24 | #define DTLS_CONTEXT_H
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | namespace dtls {
36 |
37 | class Context {
38 | public:
39 | Context();
40 | ~Context();
41 | bool init(); /* generates a certificate + private key on the fly */
42 | bool init(std::string certfile, std::string keyfile); /* loads the given certificate + private key */
43 | bool getFingerprint(std::string& result); /* returns the fingerprint for the certificate */
44 | SSL* createSSL(); /* creates a new SSL* object with support with DTLS, giving ownership to the caller. */
45 |
46 | private:
47 | bool createKey(); /* creates a EVP_PKEY that is used to store private keys */
48 | bool createKeyAndCertificate(); /* creates a self signed certificate and private key */
49 | bool createCertificate(); /* creates the X509 certificate using EVP_PKEY member */
50 | bool createContext(); /* creates the SSL_CTX instance; only after the certificate and key have been created. */
51 | bool loadPrivateKeyFile(std::string filepath); /* loads the private key from a file. */
52 | bool loadCertificateFile(std::string filepath); /* loads the certificate from a file. */
53 |
54 | public:
55 | X509* cert; /* the certificate */
56 | EVP_PKEY* pkey; /* the private key. */
57 | SSL_CTX* ctx; /* the SSL_CTX */
58 | };
59 |
60 | } /* namespace dtls */
61 |
62 | #endif
63 |
--------------------------------------------------------------------------------
/include/dtls/Parser.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | dtls::Parser
4 | -------------
5 | Used to parse incoming DTLS data and keeps track of the current state
6 | of the SSL* member. It will call the `on_data()` callback whenever you need
7 | to send some data back to the end point for which you're using this parser.
8 |
9 | */
10 | #ifndef DTLS_PARSER_H
11 | #define DTLS_PARSER_H
12 |
13 | #include
14 | #include
15 | #include
16 |
17 | #define DTLS_BUFFER_SIZE (1024 * 96) /* used to copy data from the mem bio */
18 |
19 | /* SSL debug */
20 | #define SSL_WHERE_INFO(ssl, w, flag, msg) { \
21 | if(w & flag) { \
22 | printf("----- "); \
23 | printf("%20.20s", msg); \
24 | printf(" - %30.30s ", SSL_state_string_long(ssl)); \
25 | printf(" - %5.10s ", SSL_state_string(ssl)); \
26 | printf("\n"); \
27 | } \
28 | }
29 |
30 | /* SRTP keying material sizes. */
31 | #define DTLS_SRTP_MASTER_KEY_LEN 16
32 | #define DTLS_SRTP_MASTER_SALT_LEN 14
33 | #define DTLS_SRTP_MASTER_LEN (DTLS_SRTP_MASTER_KEY_LEN + DTLS_SRTP_MASTER_SALT_LEN)
34 |
35 | namespace dtls {
36 |
37 | typedef void (*dtls_parser_on_data_callback)(uint8_t* data, uint32_t nbytes, void* user); /* gets called when the parse has data ready that needs to be send back to the other party. */
38 |
39 | enum ParserState {
40 | DTLS_STATE_NONE,
41 | };
42 |
43 | enum ParserMode {
44 | DTLS_MODE_NONE,
45 | DTLS_MODE_CLIENT,
46 | DTLS_MODE_SERVER
47 | };
48 |
49 | class Parser {
50 | public:
51 | Parser();
52 | ~Parser();
53 | bool init();
54 | void process(uint8_t* data, uint32_t nbytes); /* process some encrypted data */
55 | bool isHandshakeFinished();
56 | bool extractKeyingMaterial(); /* only when the SSL handshake has finsihed, this will extract the keying material that is used by srtp. */
57 | const char* getCipherSuite(); /* returns the selected cipher suite, of < 0 on error. we set the given suite parameter to the one that we use. */
58 |
59 | private:
60 | void checkOutputBuffer(); /* checks is there is data in our out_bio and that we need to send something to the other party. */
61 |
62 | public:
63 | SSL* ssl; /* the SSL object that tracks state. must be set by user, we take ownership and free it in the d'tor. */
64 | BIO* in_bio; /* we use memory read bios. */
65 | BIO* out_bio; /* we use memory write bios. */
66 | ParserState state;/* @todo - check if we can't use the ssl member to tack state. */ /* used to state and makes sure the on_data callback is called at the right time. */
67 | ParserMode mode; /* is this a client or server implementation */
68 | uint8_t* buffer; /* is used to copy data out our out_bio/in_bio */
69 | dtls_parser_on_data_callback on_data; /* is called when there is data that needs to be send to the other party */
70 | void* user; /* gets passed into the callbacks */
71 | uint8_t keying_material[DTLS_SRTP_MASTER_LEN * 2]; /* contains the keying material. */
72 | uint8_t* remote_key; /* remote key, used by srtp, points into keying_material */
73 | uint8_t* remote_salt; /* remote salt, used by srtp, points into keying_material */
74 | uint8_t* local_key; /* local key, used by srtp, points into keying_material */
75 | uint8_t* local_salt; /* local salt, used by srtp, points into keying_material */
76 | };
77 |
78 | } /* namespace dtls */
79 |
80 | #endif
81 |
--------------------------------------------------------------------------------
/include/ice/Agent.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Agent
4 | -----
5 |
6 | This is an experimental class to keep track of the state of an agent
7 | and it's candidates. We only support ICE-LITE atm.
8 |
9 | Should be used with a (server) sdp, with a=ice-lite, e.g:
10 |
11 |
12 | v=0
13 | o=- 5372151867866539221 2 IN IP4 127.0.0.1
14 | s=-
15 | t=0 0
16 | a=ice-lite
17 | m=video 1 RTP/SAVPF 100
18 | c=IN IP4 192.168.0.193
19 | a=mid:video
20 | a=recvonly
21 | a=rtcp-mux
22 | a=rtpmap:100 VP8/90000
23 | a=ice-ufrag:5PN2qmWqBl
24 | a=ice-pwd:Q9wQj99nsQzldVI5ZuGXbEWRK5RhRXdC
25 | a=candidate:4252876256 1 udp 2122260223 192.168.0.193 59976 typ host
26 | a=candidate:4252876256 2 udp 2122260223 192.168.0.193 59976 typ host
27 | a=fingerprint:sha-256 3C:A8:D2:9B:34:9C:F1:94:F5:FD:AD:61:1D:79:21:4D:75:32:23:BB:ED:2E:85:02:79:C9:80:1D:A8:BB:A9:8A
28 | a=setup:passive
29 |
30 |
31 |
32 | References:
33 | -----------
34 | - Agent states: http://docs.webplatform.org/wiki/apis/webrtc/RTCPeerConnection/iceState
35 |
36 | */
37 |
38 | #ifndef ICE_AGENT_H
39 | #define ICE_AGENT_H
40 |
41 | #include
42 | #include
43 | #include
44 | #include
45 | #include
46 | #include
47 |
48 | namespace ice {
49 |
50 | class Agent {
51 | public:
52 | Agent();
53 | ~Agent();
54 | bool init(); /* After adding streams (and candidates to streams), call init to kick off everythign */
55 | void update(); /* This must be called often as it fetches new data from the socket and parses any incoming data */
56 | void addStream(Stream* stream); /* Add a new stream, this class takes ownership */
57 | void setCredentials(std::string ufrag, std::string pwd); /* set the credentials (ice-ufrag, ice-pwd) for all streams. */
58 | void handleStunMessage(Stream* stream, stun::Message* msg, std::string rip, uint16_t rport, std::string lip, uint16_t lport); /* Handles incoming stun messages for the given stream and candidates. It will make sure the correct action will be taken. */
59 | void handleStreamData(Stream* stream, std::string rip, uint16_t rport, std::string lip, uint16_t lport, uint8_t* data, uint32_t nbytes) ;
60 | std::string getSDP(); /* Experimental: based on the added streams / candidates, this will return an SDP that can be shared the other agents. */
61 |
62 | public:
63 | std::vector streams;
64 | dtls::Context dtls_ctx; /* The dtls::Context is used to handle the dtls communication */
65 | stun::Reader stun; /* Used to parse incoming data and detect stun messages */
66 | bool is_lite; /* At this moment we only support ice-lite. */
67 | };
68 | } /* namespace ice */
69 |
70 | #endif
71 |
--------------------------------------------------------------------------------
/include/ice/Candidate.h:
--------------------------------------------------------------------------------
1 | #ifndef ICE_CANDIDATE_H
2 | #define ICE_CANDIDATE_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | namespace ice {
12 |
13 | /* -------------------------------------------------- */
14 |
15 | class Candidate {
16 | public:
17 | Candidate(std::string ip, uint16_t port);
18 | bool init(connection_on_data_callback cb, void* user); /* pass in the function which will receive the data from the socket. */
19 | void update(); /* read data from the socket + process */
20 |
21 | public:
22 | std::string ip; /* the ip to which we can send data */
23 | uint16_t port; /* the port to which we can send data */
24 | uint8_t component_id; /* compoment id */
25 | rtc::ConnectionUDP conn; /* the (udp for now) connection on which we receive data; later we can decouple this if necessary. */
26 | connection_on_data_callback on_data; /* will be called whenever we receive data from the socket. */
27 | void* user; /* user data */
28 |
29 | /* only local candidates initialize these contexts */
30 | dtls::Parser dtls; /* a local candidate sets up dtls context */
31 | srtp::ParserSRTP srtp_out; /* used to protect outgoing data. */
32 | srtp::ParserSRTP srtp_in; /* used to unprotect incoming data. */
33 | };
34 |
35 | /* -------------------------------------------------- */
36 |
37 | class CandidatePair {
38 | public:
39 | CandidatePair();
40 | CandidatePair(Candidate* local, Candidate* remote);
41 | ~CandidatePair();
42 |
43 | public:
44 | Candidate* local; /* local candidate; which has a socket (ConnectionUDP) */
45 | Candidate* remote; /* the remote party from which we receive data and send data towards. */
46 | };
47 |
48 | } /* namespace ice */
49 |
50 | #endif
51 |
--------------------------------------------------------------------------------
/include/ice/Stream.h:
--------------------------------------------------------------------------------
1 | #ifndef ICE_STREAM_H
2 | #define ICE_STREAM_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | /* flags, used to control the way the stream works */
10 | #define STREAM_FLAG_NONE 0x0000
11 | #define STREAM_FLAG_VP8 0x0001
12 | #define STREAM_FLAG_RTCP_MUX 0x0002
13 | #define STREAM_FLAG_SENDRECV 0x0004
14 | #define STREAM_FLAG_RECVONLY 0x0008
15 |
16 | namespace ice {
17 |
18 | class Stream;
19 |
20 | /* gets called when a stream received data, for which we haven't found a candidate pair yet. */
21 | typedef void(*stream_data_callback)(Stream* stream,
22 | std::string rip, uint16_t rport,
23 | std::string lip, uint16_t lport,
24 | uint8_t* data, uint32_t nbytes, void* user);
25 |
26 | /* gets called when we have MEDIA data from a valid candidate (RTP, DTLS, RTCP), e.g. similar to stream_data_callback, only we have a valid candidate pair now. */
27 | typedef void(*stream_media_callback)(Stream* stream, CandidatePair* pair,
28 | uint8_t* data, uint32_t nbytes, void* user);
29 |
30 |
31 | class Stream {
32 | public:
33 | Stream(uint32_t flags = STREAM_FLAG_NONE);
34 | ~Stream();
35 | bool init(); /* initialize, must be called once after all local candidates have been added */
36 | void update(); /* must be called often, which flush any pending buffers */
37 | void addLocalCandidate(Candidate* c); /* add a candidate; we take ownership of the candidate and free it in the d'tor. */
38 | void addRemoteCandidate(Candidate* c); /* add a remote candidate; is done whenever we recieve data from a ip:port for which no CandidatePair exists. */
39 | void addCandidatePair(CandidatePair* p); /* add a candidate pair; local -> remote data flow */
40 | void setCredentials(std::string ufrag, std::string pwd); /* set the credentials (ice-ufrag, ice-pwd) for all candidates. */
41 | CandidatePair* createPair(std::string rip, uint16_t rport, std::string lip, uint16_t lport);/* creates a new candidate pair for the given IPs, ofc. when the local stream exists */
42 | CandidatePair* findPair(std::string rip, uint16_t rport, std::string lip, uint16_t lport); /* used internally to find a pair on which data flows */
43 | Candidate* findLocalCandidate(std::string ip, uint16_t port); /* find a local candidate for the given local ip and port. */
44 | Candidate* findRemoteCandidate(std::string ip, uint16_t port); /* find a remote candidate for the given remote ip and port. */
45 | int sendRTP(uint8_t* data, uint32_t nbytes); /* send unprotected RTP data; we will make sure it's protected. */
46 |
47 | public:
48 | std::vector local_candidates; /* our local candidates */
49 | std::vector remote_candidates; /* our remote candidates */
50 | std::vector pairs; /* the candidate pairs */
51 | stream_data_callback on_data; /* the stream data callback; is called whenever one of the transports receives data; the Agent handles incoming data. */
52 | stream_media_callback on_rtp; /* is called whenever there is decoded rtp data; it's up to the user to call this at the right time, e.g. see Agent.cpp */
53 | void* user_data; /* user data that is passed to the on_data handler. */
54 | void* user_rtp; /* user data that is passed to the on_rtp handler. */
55 | std::string ice_ufrag; /* the ice_ufrag from the sdp */
56 | std::string ice_pwd; /* the ice-pwd value from the sdp, used when adding the message-integrity element to the responses. */
57 | uint32_t flags; /* bitflags, defines the featues of the stream; e.g. is it VP8, does it use RTCP-MUX, etc.. */
58 | };
59 |
60 | } /* namespace ice */
61 |
62 | #endif
63 |
--------------------------------------------------------------------------------
/include/ice/Utils.h:
--------------------------------------------------------------------------------
1 | #ifndef ICE_UTILS_H
2 | #define ICE_UTILS_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | namespace ice {
9 |
10 | std::string gen_random_string(const int len); /* generates a random alpha-num string with the given len. */
11 | std::vector get_interface_addresses(); /* retrieve interface addresses, can be used to create a SDP.*/
12 |
13 | } /* namespace ice */
14 | #endif
15 |
--------------------------------------------------------------------------------
/include/rtc/Connection.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Connection
4 | -----------
5 | This code is still experimental and the API will/may change. At this
6 | moment I'm not sure how to abstract the network I/O (udp/tcp) and the
7 | coupling with DTLS.
8 |
9 | At this moment there is a base Connection and an ConnectionUDP class.
10 |
11 | */
12 | #ifndef RTC_CONNECTION_H
13 | #define RTC_CONNECTION_H
14 |
15 | extern "C" {
16 | # include
17 | }
18 |
19 | #include
20 | #include
21 | #include
22 |
23 | typedef void(*connection_on_data_callback)(std::string rip, uint16_t rport, /* local ip and port */
24 | std::string lip, uint16_t lport, /* remote ip and port */
25 | uint8_t* data, uint32_t nbytes, void* user); /* gets called when a connection receives some data. */
26 |
27 | namespace rtc {
28 |
29 | class Connnection {
30 | };
31 |
32 | class ConnectionUDP {
33 |
34 | public:
35 | ConnectionUDP();
36 | bool bind(std::string ip, uint16_t port);
37 | void update();
38 | // void send(uint8_t* data, uint32_t nbytes); /* @todo - deprecated, use sendTo */
39 | void sendTo(std::string rip, uint16_t rport, uint8_t* data, uint32_t nbytes);
40 | public:
41 | std::string ip;
42 | uint16_t port;
43 | struct sockaddr_in raddr; /* receive */
44 | // struct sockaddr* saddr; /* send (will not be necessary anymore! @todo remove when ice things are working) */
45 | uv_udp_t sock;
46 | uv_loop_t* loop;
47 |
48 | /* callbacks */
49 | connection_on_data_callback on_data;
50 | void* user;
51 | };
52 |
53 | } /* namespace rtc */
54 |
55 | #endif
56 |
--------------------------------------------------------------------------------
/include/rtp/PacketVP8.h:
--------------------------------------------------------------------------------
1 | #ifndef RTP_PACKET_VP8_H
2 | #define RTP_PACKET_VP8_H
3 |
4 | #include
5 |
6 | namespace rtp {
7 |
8 | class PacketVP8 {
9 | public:
10 | PacketVP8();
11 | ~PacketVP8();
12 | void reset();
13 |
14 | public:
15 |
16 | /* rtp header */
17 | uint8_t version;
18 | uint8_t padding;
19 | uint8_t extension;
20 | uint8_t csrc_count;
21 | uint8_t marker; /* set for the very last packet of each encoded frame in line with the normal use of the M bit in video formats. For VP8 this will be set to 1 when the last packet for a frame is received. */
22 | uint8_t payload_type;
23 | uint16_t sequence_number;
24 | uint32_t timestamp;
25 | uint32_t ssrc;
26 |
27 | /* required */
28 | uint8_t X; /* extended controlbits present */
29 | uint8_t N; /* (non-reference frame) when set to 1 this frame can be discarded */
30 | uint8_t S; /* start of VP8 partition */
31 | uint8_t PID; /* partition index */
32 |
33 | /* 2nd second row Payload Descriptor (is optional) */
34 | uint8_t I; /* 1 if PictureID is present */
35 | uint8_t L; /* 1 if TL0PICIDX is present */
36 | uint8_t T; /* 1 if TID is present */
37 | uint8_t K; /* 1 if KEYIDX is present */
38 | uint16_t PictureID; /* 8 or 16 bits, picture ID */
39 | uint8_t TL0PICIDX; /* 8 bits temporal level zero index */
40 |
41 | /* 3rd row Payload Descriptor */
42 | uint8_t M; /* Extension flag; must be present if I bit == 1. If set, the PictureID field must contains 16 bits, else 8*/
43 |
44 | /* payload header */
45 | uint8_t P; /* 0 if current frame is a key frame, otherwise 1 */
46 |
47 | /* the actual frame/partition data */
48 | uint8_t* payload; /* points to the start of the partition data that can be fed into the decoder (once a frame has been constructed.). We do not copy the data! */
49 | uint32_t nbytes; /* number of bytes in the partition */
50 | };
51 |
52 | } /* namespace rtp */
53 |
54 | #endif
55 |
--------------------------------------------------------------------------------
/include/rtp/ReaderVP8.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | RTP VP8 extension (writer)
4 | --------------------------
5 |
6 | See:
7 | - RFC: http://tools.ietf.org/html/draft-ietf-payload-vp8-11#section-4.2
8 | - Example code: https://gist.github.com/roxlu/df0a786a8bf81e75ef0e
9 |
10 | */
11 | #ifndef RTP_READER_VP8
12 | #define RTP_READER_VP8
13 |
14 | #include
15 | #include
16 |
17 | namespace rtp {
18 |
19 | int rtp_vp8_decode(uint8_t* data, uint32_t nbytes, PacketVP8* pkt);
20 |
21 | } /* namespace rtp */
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/include/rtp/WriterVP8.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | RTP VP8 extension (writer)
4 | --------------------------
5 |
6 | */
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | namespace rtp {
15 |
16 | typedef void(*rtp_vp8_on_packet)(PacketVP8* pkt, void* user); /* gets called whenever a new RTP-VP8 packet is created; one vpx_codec_cx_pkt_t can result in multiple RTP-VP8 packets. */
17 |
18 | class WriterVP8 {
19 |
20 | public:
21 | WriterVP8();
22 | ~WriterVP8();
23 | int packetize(const vpx_codec_cx_pkt_t* pkt); /* create a RTP-VP8 packet. */
24 |
25 | public:
26 | uint32_t ssrc; /* RTP ssrc */
27 | uint16_t seqnum; /* RTP sequence number, starts with a random value. */
28 | uint16_t picture_id; /* RTP-VP8 picture id, starts with a random value. */
29 | rtp_vp8_on_packet on_packet; /* must be set by user; will receive a RTP packet. */
30 | void* user; /* gets passed into the callback */
31 |
32 | private:
33 | uint32_t capacity; /* the capacity of our buffer */
34 | uint8_t* buffer; /* the buffer that will hold the VP8 data. */
35 | };
36 |
37 |
38 | } /* namespace rtp */
39 |
--------------------------------------------------------------------------------
/include/sdp/Reader.h:
--------------------------------------------------------------------------------
1 | #ifndef SDP_READER_H
2 | #define SDP_READER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | namespace sdp {
12 |
13 | /* our parse exception */
14 | struct ParseException : public std::exception {
15 | std::string s;
16 | ParseException(std::string s):s(s) {}
17 | ~ParseException() throw() {}
18 | const char* what() const throw() { return s.c_str(); };
19 | };
20 |
21 | /* one element of a Line */
22 | class Token {
23 | public:
24 | Token();
25 | Token(std::string value);
26 | bool isNumeric();
27 | size_t size();
28 |
29 | /* conversion functions */
30 | int toInt();
31 | uint64_t toU64();
32 | std::string toString();
33 | AddrType toAddrType();
34 | NetType toNetType();
35 | MediaType toMediaType();
36 | MediaProto toMediaProto();
37 | CandType toCandType();
38 | SetupType toSetupType();
39 |
40 | public:
41 | std::string value;
42 | };
43 |
44 | /* a sdp line, e.g "a=rtcp:59976 IN IP4 192.168.0.194" */
45 | class Line {
46 | public:
47 | Line();
48 | Line(std::string src);
49 |
50 | /* generic parse functions */
51 | void skip(char until); /* skip some characters until you find the given character. */
52 | void ltrim(); /* trim whitespace from left from current index. */
53 | Token getToken(char until = ' '); /* read part of a sdp line until the given character. */
54 | char operator[](unsigned int);
55 |
56 | /* read the next token as a specific type */
57 | bool readType(char type); /* read until the type element (e.g. o=, v=, a=) and return true when the line is the given type. */
58 | std::string readString(char until = ' '); /* read a string from the next token */
59 | int readInt(char until = ' '); /* read an integer value from the next token */
60 | uint64_t readU64(char until = ' '); /* read an integer (u64). */
61 | AddrType readAddrType(char until = ' '); /* read an AddrType */
62 | NetType readNetType(char until = ' '); /* read a NetType */
63 | MediaType readMediaType(char until = ' '); /* read a MediaType */
64 | MediaProto readMediaProto(char until = ' '); /* read a MediaProto */
65 | CandType readCandType(char until = ' '); /* read a CandType */
66 | SetupType readSetupType(char until = ' '); /* read a SetupType */
67 |
68 | public:
69 | std::string value;
70 | size_t index; /* used to keep track until which character one has read. */
71 | };
72 |
73 | /* parses an SDP */
74 | class Reader {
75 | public:
76 | int parse(std::string source, SDP* result);
77 |
78 | private:
79 | Node* parseLine(Line& line);
80 | Version* parseVersion(Line& line); /* v= */
81 | Origin* parseOrigin(Line& line); /* o= */
82 | SessionName* parseSessionName(Line& line); /* s= */
83 | SessionInformation* parseSessionInformation(Line& line); /* i= */
84 | URI* parseURI(Line& line); /* u= */
85 | EmailAddress* parseEmailAddress(Line& line); /* e= */
86 | PhoneNumber* parsePhoneNumber(Line& line); /* p= */
87 | ConnectionData* parseConnectionData(Line& line); /* c= */
88 | Timing* parseTiming(Line& line); /* t= */
89 | Media* parseMedia(Line& line); /* m= */
90 | Attribute* parseAttribute(Line& line); /* a= */
91 | };
92 |
93 | }
94 |
95 | #endif
96 |
--------------------------------------------------------------------------------
/include/sdp/SDP.h:
--------------------------------------------------------------------------------
1 | #ifndef SDP_H
2 | #define SDP_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | namespace sdp {
9 |
10 | class SDP : public Node {
11 | public:
12 | SDP();
13 |
14 | };
15 |
16 |
17 | } /* namespace sdp */
18 |
19 | #endif
20 |
--------------------------------------------------------------------------------
/include/sdp/Types.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Types
4 | -----
5 |
6 | This file contains the structs/nodes that make up a SDP and is based on RFC4566.
7 | We follow the same naming as the elements as described in RFC4566.
8 | We implement the nodes in the same order as described in the "SDP specification"
9 | chapter in http://tools.ietf.org/html/rfc4566.html
10 |
11 | */
12 |
13 | #ifndef SDP_TYPES_H
14 | #define SDP_TYPES_H
15 |
16 | #include
17 | #include
18 | #include
19 |
20 | namespace sdp {
21 |
22 | enum Type {
23 | SDP_NONE,
24 | SDP_SESSION, /* a full SDP session */
25 | SDP_ORIGIN,
26 | SDP_VERSION,
27 | SDP_SESSION_NAME,
28 | SDP_SESSION_INFORMATION,
29 | SDP_URI,
30 | SDP_EMAIL_ADDRESS,
31 | SDP_PHONE_NUMBER,
32 | SDP_CONNECTION_DATA,
33 | SDP_TIMING,
34 | SDP_MEDIA,
35 | SDP_CANDIDATE,
36 | SDP_ATTRIBUTE
37 | };
38 |
39 | enum NetType {
40 | SDP_NETTYPE_NONE,
41 | SDP_IN
42 | };
43 |
44 | enum AddrType {
45 | SDP_ADDRTYPE_NONE,
46 | SDP_IP4,
47 | SDP_IP6
48 | };
49 |
50 | enum MediaType {
51 | SDP_MEDIATYPE_NONE,
52 | SDP_VIDEO,
53 | SDP_AUDIO,
54 | SDP_TEXT,
55 | SDP_APPLICATION,
56 | SDP_MESSAGE
57 | };
58 |
59 | enum MediaProto {
60 | SDP_MEDIAPROTO_NONE,
61 | SDP_UDP,
62 | SDP_RTP_AVP,
63 | SDP_RTP_SAVP,
64 | SDP_RTP_SAVPF /* http://tools.ietf.org/html/rfc5124 */
65 | };
66 |
67 | enum AttrType {
68 | SDP_ATTRTYPE_NONE,
69 | SDP_ATTR_RTCP,
70 | SDP_ATTR_KEYWDS,
71 | SDP_ATTR_TOOL,
72 | SDP_ATTR_PTIME,
73 | SDP_ATTR_MAXPTIME,
74 | SDP_ATTR_RTPMAP,
75 | SDP_ATTR_RECVONLY,
76 | SDP_ATTR_SENDRECV,
77 | SDP_ATTR_SENDONLY,
78 | SDP_ATTR_INACTIVE,
79 | SDP_ATTR_ORIENT,
80 | SDP_ATTR_TYPE,
81 | SDP_ATTR_CHARSET,
82 | SDP_ATTR_SDPLANG,
83 | SDP_ATTR_LANG,
84 | SDP_ATTR_CANDIDATE,
85 | SDP_ATTR_ICE_UFRAG,
86 | SDP_ATTR_ICE_PWD,
87 | SDP_ATTR_ICE_OPTIONS,
88 | SDP_ATTR_FINGERPRINT,
89 | SDP_ATTR_SETUP,
90 |
91 | /* etc... etc.. */
92 | SDP_ATTR_UNKNOWN /* an generic attribute. different from SDP_ATTRTYPE_NONE as this one has been explicitly set by the user */
93 | };
94 |
95 | /* a=candidate: */
96 | enum CandType {
97 | SDP_CANDTYPE_NONE,
98 | SDP_HOST,
99 | SDP_SRFLX,
100 | SDP_PRFLX,
101 | SDP_RELAY
102 | };
103 |
104 | /* a=setup: */
105 | enum SetupType {
106 | SDP_SETUPTYPE_NONE,
107 | SDP_ACTIVE,
108 | SDP_PASSIVE,
109 | SDP_ACTPASS,
110 | SDP_HOLDCONN
111 | };
112 |
113 | /* forward declared for Node::find() */
114 | struct Version;
115 | struct Origin;
116 | struct SessionName;
117 | struct SessionInformation;
118 | struct Timing;
119 | struct ConnectionData;
120 | struct Media;
121 | struct Attribute;
122 | struct AttributeRTCP;
123 | struct AttributeCandidate;
124 |
125 | struct Node {
126 | public:
127 | Node(Type t);
128 | virtual ~Node();
129 | void add(Node* n);
130 | bool find(Type t, std::vector& result); /* will try to find a child node for the given type */
131 | bool find(MediaType t, Media** result); /* will set result to the first found media element of the given media type. */
132 | bool find(AttrType t, Attribute** result); /* will set result to the first occurence of the attribute type. */
133 | bool find(AttrType t, std::vector& result); /* find all attributes for the given type. */
134 | void remove(Type t); /* remove all nodes of the given type. */
135 | void remove(AttrType t); /* remove attributes of this type. */
136 |
137 | public:
138 | Type type;
139 | std::vector nodes;
140 | };
141 |
142 | /* v= */
143 | struct Version : public Node {
144 | Version();
145 | int version;
146 | };
147 |
148 | /* o= */
149 | struct Origin : public Node {
150 | Origin();
151 |
152 | std::string username; /* users login, or "-" if you dont support user ids. */
153 | std::string sess_id; /* numeric string that is used as unique identifier, e.g. timestamp, e.g. "621762799816690644" */
154 | uint64_t sess_version; /* version number of this SDP, e.g. "1" */
155 | NetType net_type; /* SDP_IN */
156 | AddrType addr_type; /* SDP_IP4, SDP_IP6 */
157 | std::string unicast_address; /* address of the machine from which the session was created, e.g. 127.0.0.1 */
158 | };
159 |
160 | /* s= */
161 | struct SessionName : public Node {
162 | SessionName();
163 | std::string session_name;
164 | };
165 |
166 | /* i= */
167 | struct SessionInformation : public Node {
168 | SessionInformation();
169 | std::string session_description;
170 | };
171 |
172 | /* u= */
173 | struct URI : public Node {
174 | URI();
175 | std::string uri;
176 | };
177 |
178 | /* e= */
179 | struct EmailAddress : public Node {
180 | EmailAddress();
181 | std::string email_address;
182 | };
183 |
184 | /* p= */
185 | struct PhoneNumber : public Node {
186 | PhoneNumber();
187 | std::string phone_number;
188 | };
189 |
190 | /* t= */
191 | struct Timing : public Node {
192 | Timing();
193 | uint64_t start_time;
194 | uint64_t stop_time;
195 | };
196 |
197 | /* c= */
198 | struct ConnectionData : public Node {
199 | ConnectionData();
200 | NetType net_type;
201 | AddrType addr_type;
202 | std::string connection_address;
203 | };
204 |
205 | /* m= */
206 | struct Media : public Node {
207 | Media();
208 | MediaType media;
209 | uint16_t port;
210 | MediaProto proto;
211 | int fmt;
212 | };
213 |
214 | /*
215 |
216 | Because the list of attribute types is huge, we create a generic Attribute
217 | struct which contains some members that are meant for common types. So in general
218 | not all members of this sturct are always used. The reader will set the members
219 | base on the AttrType member.
220 |
221 | a=
222 | */
223 | struct Attribute : public Node {
224 | Attribute();
225 | Attribute(std::string name, std::string value, AttrType atype = SDP_ATTR_UNKNOWN);
226 | AttrType attr_type;
227 | std::string name;
228 | std::string value;
229 | };
230 |
231 | /* a=rtcp:59976 IN IP4 192.168.0.194 */
232 | struct AttributeRTCP : public Attribute {
233 | AttributeRTCP();
234 | uint16_t port;
235 | NetType net_type;
236 | AddrType addr_type;
237 | std::string connection_address;
238 | };
239 |
240 | /* a=candidate:4252876256 1 udp 2122260223 192.168.0.194 59976 typ host generation 0 */
241 | struct AttributeCandidate : public Attribute {
242 | AttributeCandidate();
243 |
244 | std::string foundation;
245 | uint64_t component_id;
246 | std::string transport;
247 | uint64_t priority;
248 | std::string connection_address;
249 | int port;
250 | CandType cand_type;
251 | std::string rel_addr;
252 | uint16_t rel_port;
253 | };
254 |
255 | /* a=fingerprint:sha-256 EA:A3:3E:F1:7F:36:62:AA:AE:31:ED:9E:B5:B6:FA:CE:56:8A:29:4C:A3:C5:F7:28:3D:1B:72:5A:68:9F:FE:33 */
256 | /* see: http://www.rfc-editor.org/rfc/rfc4572.txt, section 5 */
257 | struct AttributeFingerprint : public Attribute {
258 | AttributeFingerprint();
259 | AttributeFingerprint(std::string hfunc, std::string fprint);
260 |
261 | std::string hash_func;
262 | std::string fingerprint;
263 | };
264 |
265 | /* a=setup: ... */
266 | struct AttributeSetup : public Attribute {
267 | AttributeSetup();
268 | AttributeSetup(SetupType role);
269 | void makeActive();
270 | void makePassive();
271 | SetupType role;
272 | };
273 |
274 | };
275 |
276 | #endif
277 |
--------------------------------------------------------------------------------
/include/sdp/Utils.h:
--------------------------------------------------------------------------------
1 | #ifndef SDP_UTILS_H
2 | #define SDP_UTILS_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | namespace sdp {
13 |
14 | bool string_to_net_type(std::string& input, NetType& result); /* convert a string to a NetType */
15 | bool string_to_addr_type(std::string& input, AddrType& result); /* convert a string to an AddrType */
16 | bool string_to_media_type(std::string& input, MediaType& result); /* convert a string to a MediaType */
17 | bool string_to_media_proto(std::string& input, MediaProto& result); /* convert a string to a MediaType */
18 | bool string_to_cand_type(std::string& input, CandType& result); /* convert a string to a CandType */
19 | bool string_to_setup_type(std::string& input, SetupType& result); /* convert a string to a SetupType for the a=setup: attribute */
20 | std::string net_type_to_string(NetType type);
21 | std::string addr_type_to_string(AddrType type);
22 | std::string media_type_to_string(MediaType type);
23 | std::string media_proto_to_string(MediaProto proto);
24 | std::string cand_type_to_string(CandType type);
25 | std::string setup_type_to_string(SetupType type);
26 |
27 |
28 | /* trim from right */
29 | inline static std::string rtrim(const std::string &source , const std::string& t = " " ) {
30 | std::string str = source;
31 | return str.erase (str.find_last_not_of(t) + 1);
32 | }
33 |
34 | /* trim from left */
35 | inline static std::string ltrim(const std::string& source, const std::string& t = " ") {
36 | std::string str = source;
37 | return str.erase (0 , source.find_first_not_of(t));
38 | }
39 |
40 | /* trim from both left and right */
41 | inline static std::string trim ( const std::string& source, const std::string& t = " ") {
42 | std::string str = source;
43 | return ltrim(rtrim (str, t) , t);
44 | }
45 |
46 | /* tokenizes the input on the given character. */
47 | inline int tokenize(std::string input, char delim, std::vector& output) {
48 |
49 | std::stringstream ss(input);
50 | std::string line;
51 |
52 | while (std::getline(ss, line, delim)) {
53 | output.push_back(line);
54 | }
55 |
56 | if (!output.size()) {
57 | return -1;
58 | }
59 |
60 | return 0;
61 |
62 | };
63 |
64 | /* check if the given value is numeric */
65 | inline bool is_numeric(std::string s) {
66 | std::string::const_iterator it = s.begin();
67 | while (it != s.end() && std::isdigit(*it)) {
68 | ++it;
69 | }
70 | return !s.empty() && it == s.end();
71 | }
72 |
73 | /* converts the given string to another type */
74 | template T convert(std::string value) {
75 | T result;
76 | std::stringstream ss(value);
77 | ss >> result;
78 | return result;
79 | }
80 | };
81 |
82 | #endif
83 |
--------------------------------------------------------------------------------
/include/sdp/Writer.h:
--------------------------------------------------------------------------------
1 | #ifndef SDP_WRITER_H
2 | #define SDP_WRITER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | namespace sdp {
12 |
13 | class Writer {
14 | public:
15 | std::string toString(SDP* sdp);
16 | std::string toString(Node* sdp);
17 | std::string toString(Version* v);
18 | std::string toString(Origin* o);
19 | std::string toString(SessionName* s);
20 | std::string toString(Timing* t);
21 | std::string toString(Media* m);
22 | std::string toString(Attribute* a);
23 | std::string toString(AttributeCandidate* c);
24 | std::string toString(AttributeFingerprint* f);
25 | std::string toString(AttributeSetup* f);
26 | };
27 |
28 | } /* namesapce sdp */
29 | #endif
30 |
--------------------------------------------------------------------------------
/include/signaling/Room.h:
--------------------------------------------------------------------------------
1 | #ifndef WEBRTC_SIGNALING_ROOM_H
2 | #define WEBRTC_SIGNALING_ROOM_H
3 |
4 | #include
5 |
6 | namespace sig {
7 |
8 | class Room {
9 | public:
10 | Room(std::string name, std::string sdp);
11 |
12 | public:
13 | std::string name;
14 | std::string sdp;
15 | };
16 |
17 |
18 | } /* namespace sig */
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/include/signaling/Signaling.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Signaling
4 | ---------
5 |
6 | Experimental and basic implementation of a signaling websocket API. We're
7 | starting this signal server with the idea that it will be embedded in a
8 | custom application which wants to allow users to connect to it using WebRTC.
9 |
10 | The signaling server uses 'rooms' keep state. Each room has things like
11 | the SDP of the server, name, etc.
12 |
13 | */
14 | #ifndef WEBRTC_SIGNALING_H
15 | #define WEBRTC_SIGNALING_H
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | namespace sig {
24 |
25 | class Signaling {
26 |
27 | public:
28 | Signaling();
29 | ~Signaling();
30 | int init(SignalingSettings cfg);
31 | int addRoom(Room* room);
32 | int start();
33 | int stop();
34 |
35 | Room* findRoom(std::string name);
36 |
37 | /* API */
38 | void handleJoin(struct mg_connection* conn);
39 |
40 | public:
41 | SignalingSettings settings;
42 | std::vector rooms;
43 | struct mg_server* server;
44 | bool must_stop;
45 | };
46 |
47 | } /* namespace sig */
48 | #endif
49 |
--------------------------------------------------------------------------------
/include/signaling/SignalingSettings.h:
--------------------------------------------------------------------------------
1 | #ifndef WEBRTC_SIGNALING_SETTINGS_H
2 | #define WEBRTC_SIGNALING_SETTINGS_H
3 |
4 | #include
5 |
6 | namespace sig {
7 |
8 | class SignalingSettings {
9 | public:
10 | SignalingSettings();
11 | ~SignalingSettings();
12 |
13 | public:
14 | std::string port;
15 | };
16 |
17 | } /* namespace sig */
18 |
19 | #endif
20 |
--------------------------------------------------------------------------------
/include/srtp/ParserSRTP.h:
--------------------------------------------------------------------------------
1 | #ifndef SRTP_PARSER_H
2 | #define SRTP_PARSER_H
3 |
4 | #include
5 | #include
6 |
7 | #define SRTP_PARSER_MASTER_KEY_LEN 16
8 | #define SRTP_PARSER_MASTER_SALT_LEN 14
9 | #define SRTP_PARSER_MASTER_LEN (SRTP_PARSER_MASTER_KEY_LEN + SRTP_PARSER_MASTER_SALT_LEN)
10 |
11 |
12 | namespace srtp {
13 |
14 | class ParserSRTP {
15 |
16 | public:
17 | ParserSRTP();
18 | ~ParserSRTP();
19 | int init(const char* cipher, bool inbound, const uint8_t* key, const uint8_t* salt);
20 | int protectRTP(void* in, uint32_t nbytes);
21 | int protectRTCP(void* in, uint32_t nbytes);
22 | int unprotectRTP(void* in, uint32_t nbytes);
23 | int unprotectRTCP(void* in, uint32_t nbytes);
24 |
25 | public:
26 | static bool is_lib_init;
27 | bool is_init;
28 | srtp_t session;
29 | srtp_policy_t policy;
30 | };
31 |
32 | } /* namespace srtp */
33 |
34 | #endif
35 |
--------------------------------------------------------------------------------
/include/stun/Attribute.h:
--------------------------------------------------------------------------------
1 | #ifndef STUN_ATTRIBUTE_H
2 | #define STUN_ATTRIBUTE_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | namespace stun {
10 |
11 | /* --------------------------------------------------------------------- */
12 |
13 | class Attribute {
14 | public:
15 | Attribute(uint16_t type = STUN_ATTR_TYPE_NONE);
16 |
17 | public:
18 | uint16_t type;
19 | uint16_t length; /* The number of bytes of the attribute data. This is the size w/o the padded bytes that are added when the the attribute is not padded to 32 bits. */
20 | uint16_t nbytes; /* The number of bytes the attribute takes inside the buffer. Because the STUN message has to be padded on 32 bits the length may be different from the nbytes. Also, this nbytes includes the bytes of the type field and length field. */
21 | uint32_t offset; /* Byte offset where the header of the attribute starts in the Message::buffer. */
22 | };
23 |
24 | /* --------------------------------------------------------------------- */
25 |
26 | class StringValue {
27 | public:
28 | StringValue(){}
29 | StringValue(std::string v) { std::copy(v.begin(), v.end(), std::back_inserter(buffer)); }
30 | std::vector buffer;
31 | };
32 |
33 | /* --------------------------------------------------------------------- */
34 |
35 | class Username : public Attribute {
36 | public:
37 | Username():Attribute(STUN_ATTR_USERNAME){ }
38 | Username(std::string name):value(name),Attribute(STUN_ATTR_USERNAME) { }
39 | StringValue value;
40 | };
41 |
42 | /* --------------------------------------------------------------------- */
43 |
44 | class Software : public Attribute {
45 | public:
46 | Software():Attribute(STUN_ATTR_SOFTWARE) {}
47 | Software(std::string name):value(name),Attribute(STUN_ATTR_SOFTWARE) {}
48 | StringValue value;
49 | };
50 |
51 | /* --------------------------------------------------------------------- */
52 |
53 | class XorMappedAddress : public Attribute {
54 | public:
55 | XorMappedAddress();
56 | XorMappedAddress(std::string addr, uint16_t p, uint8_t fam = STUN_IP4);
57 | uint8_t family;
58 | uint16_t port;
59 | std::string address; /* IP address in string notation: 192.168.0.1 */
60 | };
61 |
62 | /* --------------------------------------------------------------------- */
63 |
64 | class Fingerprint : public Attribute {
65 | public:
66 | Fingerprint();
67 | uint32_t crc;
68 | };
69 |
70 | /* --------------------------------------------------------------------- */
71 |
72 | class IceControlled : public Attribute {
73 | public:
74 | IceControlled();
75 | uint64_t tie_breaker;
76 | };
77 |
78 | /* --------------------------------------------------------------------- */
79 |
80 | class IceControlling : public Attribute {
81 | public:
82 | IceControlling();
83 | uint64_t tie_breaker;
84 | };
85 |
86 | /* --------------------------------------------------------------------- */
87 |
88 | class Priority : public Attribute {
89 | public:
90 | Priority();
91 | uint32_t value;
92 | };
93 |
94 | /* --------------------------------------------------------------------- */
95 |
96 | class MessageIntegrity : public Attribute {
97 | public:
98 | MessageIntegrity();
99 | uint8_t sha1[20];
100 | };
101 |
102 | } /* namespace stun */
103 |
104 | #endif
105 |
--------------------------------------------------------------------------------
/include/stun/Message.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Message
4 | --------
5 |
6 | Represents a stun::Message. When you're using Message-Integrity and Fingerprint
7 | attributes, make sure to call computeMessageIntegrity(), before you call computeFingerprint().
8 | The fingerprint (crc32) is computer over the buffer including the message-integrity value, whne
9 | you don't compute this first, the crc will be incorrect.
10 |
11 | */
12 | #ifndef STUN_MESSAGE_H
13 | #define STUN_MESSAGE_H
14 |
15 | #include
16 | #include
17 |
18 | namespace stun {
19 |
20 | class Message {
21 | public:
22 | Message(uint16_t type = STUN_MSG_TYPE_NONE);
23 | ~Message();
24 | void addAttribute(Attribute* attr); /* Add an attribute to the message who takes ownership (will delete all attributes in the d'tor. */
25 | void copyTransactionID(Message* from); /* Copy the transaction ID from the given messsage. */
26 | void setTransactionID(uint32_t a, uint32_t b, uint32_t c); /* Set the transaction ID from the given values. */
27 | bool hasAttribute(AttributeType atype); /* Check if the given attribute is found in one of the attributes */
28 | bool find(MessageIntegrity** result); /* Find a message integrity attribute. */
29 | bool find(XorMappedAddress** result); /* Find a xor-mapped-address attribute.*/
30 | bool find(Fingerprint** result); /* Find a fingerprint attrbiute. */
31 | bool computeMessageIntegrity(std::string key); /* When the message contains a MessageIntegrity element, this will compute the HMAC-SHA1 message integrity. */
32 | bool computeFingerprint(); /* When the message contains a Fingerprint attriute (must be added after the MessageInterity attribute), this will calculate and set CRC value. Important: make sure that you computer the fingerprint AFTER you've computed the message-integrity */
33 |
34 | template bool find(uint16_t atype, T** result) {
35 | *result = NULL;
36 | for (size_t i = 0; i < attributes.size(); ++i) {
37 | if (attributes[i]->type == atype) {
38 | *result = static_cast(attributes[i]);
39 | return true;
40 | }
41 | }
42 | return false;
43 | }
44 |
45 | public:
46 | uint16_t type;
47 | uint16_t length;
48 | uint32_t cookie;
49 | uint32_t transaction[3];
50 | std::vector attributes;
51 | std::vector buffer;
52 | };
53 |
54 | } /* namespace stun */
55 |
56 | #endif
57 |
--------------------------------------------------------------------------------
/include/stun/Reader.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | STUN
4 | ----
5 |
6 | Experimental code that parses stun.
7 |
8 | References:
9 | ----------
10 | - libjingle: https://gist.github.com/roxlu/511544ea07610f70a900
11 | - own test code: https://gist.github.com/roxlu/57fe6c590f6267e0a209
12 | - C++, each attribute has its own class: https://github.com/husman/Development-Sample-Side-Projects/blob/713b052afd2344e95b7e1b33405713460d1b2d95/Computer_Science/My_Hobby_Projects/Live_Streaming/P2P_Live_Streaming/3rdparty/StunUsernameAttribute.h
13 | - C++, each clean example, a couple of generic attribute values: https://github.com/sourcey/libsourcey/blob/f126fdaa26fdcc3f44ebd2d70f2915ee0b6a1b23/src/stun/tests/stuntests.cpp
14 | - Stun attribute values: http://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml
15 | - Some good info on the attributes and their meaning: http://www.3cx.com/blog/voip-howto/stun-details/
16 | - Test data for message-integrity checks: http://tools.ietf.org/html/rfc5769
17 | */
18 |
19 | #ifndef STUN_READER_H
20 | #define STUN_READER_H
21 |
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 |
30 | namespace stun {
31 |
32 | class Reader {
33 |
34 | public:
35 | Reader();
36 | int process(uint8_t* data, uint32_t nbytes, Message* msg); /* parses the incoming data and fills msg if the data contains a valid stun message, if so it returns 0, when other data is passed into this function it will return 1, on error it returns -1 */
37 |
38 | private:
39 | uint8_t readU8(); /* read one uint8_t from buffer and increment the index. */
40 | uint16_t readU16(); /* read an uint16_t from the buffer, expecting the buffer to hold Big Endian data and moving the dx member. */
41 | uint32_t readU32(); /* read an uint32_t from the buffer, expecting the buffer to hold Big Endian data and moving the dx member. */
42 | uint64_t readU64(); /* read an uint64_t from the buffer, expecting the buffer to hold Big Endian data and moving the dx member. */
43 | StringValue readString(uint16_t len); /* read a StringValue from the current buffer */
44 | XorMappedAddress* readXorMappedAddress(); /* reads a XorMappedAddress */
45 | void skip(uint32_t nbytes); /* skip the next nbytes. */
46 | uint32_t bytesLeft(); /* returns the number of bytes that still need to be parsed, this is not the same as the size of the buffer! */
47 | uint8_t* ptr(); /* returns a pointer to the current read index of the buffer. */
48 |
49 | public:
50 | std::vector buffer;
51 | size_t dx;
52 | };
53 |
54 | } /* namespace stun */
55 |
56 | #endif
57 |
--------------------------------------------------------------------------------
/include/stun/Types.h:
--------------------------------------------------------------------------------
1 | #ifndef STUN_TYPES_H
2 | #define STUN_TYPES_H
3 |
4 | #include
5 | #include
6 |
7 | #define STUN_IP4 0x01
8 | #define STUN_IP6 0x02
9 |
10 | namespace stun {
11 |
12 | enum MessageType {
13 | STUN_MSG_TYPE_NONE = 0x0000,
14 | STUN_BINDING_REQUEST = 0x0001,
15 | STUN_BINDING_RESPONSE = 0x0101,
16 | STUN_BINDING_ERROR_RESPONSE = 0x0111,
17 | STUN_BINDING_INDICATION = 0x0011
18 | };
19 |
20 | enum AttributeType {
21 | STUN_ATTR_TYPE_NONE = 0x0000,
22 | STUN_ATTR_MAPPED_ADDR = 0x0001,
23 | STUN_ATTR_CHANGE_REQ = 0x0003,
24 | STUN_ATTR_USERNAME = 0x0006,
25 | STUN_ATTR_MESSAGE_INTEGRITY = 0x0008, /* See: http://tools.ietf.org/html/rfc5389#section-15.4 + http://tools.ietf.org/html/rfc4013, SHA1, 20 bytes*/
26 | STUN_ATTR_ERR_CODE = 0x0009,
27 | STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000a,
28 | STUN_ATTR_CHANNEL_NUMBER = 0x000c,
29 | STUN_ATTR_LIFETIME = 0x000d,
30 | STUN_ATTR_XOR_PEER_ADDR = 0x0012,
31 | STUN_ATTR_DATA = 0x0013,
32 | STUN_ATTR_REALM = 0x0014,
33 | STUN_ATTR_NONCE = 0x0015,
34 | STUN_ATTR_XOR_RELAY_ADDRESS = 0x0016,
35 | STUN_ATTR_REQ_ADDRESS_FAMILY = 0x0017,
36 | STUN_ATTR_EVEN_PORT = 0x0018,
37 | STUN_ATTR_REQUESTED_TRANSPORT = 0x0019,
38 | STUN_ATTR_DONT_FRAGMENT = 0x001a,
39 | STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020,
40 | STUN_ATTR_RESERVATION_TOKEN = 0x0022,
41 | STUN_ATTR_PRIORITY = 0x0024, /* See: http://tools.ietf.org/html/rfc5245#section-7.1.2.1 */
42 | STUN_ATTR_USE_CANDIDATE = 0x0025, /* See: http://tools.ietf.org/html/rfc5245#section-7.1.2.1 */
43 | STUN_ATTR_PADDING = 0x0026,
44 | STUN_ATTR_RESPONSE_PORT = 0x0027,
45 | STUN_ATTR_SOFTWARE = 0x8022,
46 | STUN_ATTR_ALTERNATE_SERVER = 0x8023,
47 | STUN_ATTR_FINGERPRINT = 0x8028, /* See: http://tools.ietf.org/html/rfc5389#section-8 + http://tools.ietf.org/html/rfc5389#section-15.5 */
48 | STUN_ATTR_ICE_CONTROLLED = 0x8029, /* See: http://tools.ietf.org/html/rfc5245#section-19.1 */
49 | STUN_ATTR_ICE_CONTROLLING = 0x802a, /* See: http://tools.ietf.org/html/rfc5245#section-19.1 */
50 | STUN_ATTR_RESPONSE_ORIGIN = 0x802b,
51 | STUN_ATTR_OTHER_ADDRESS = 0x802c,
52 | };
53 |
54 | /* --------------------------------------------------------------------- */
55 |
56 | std::string attribute_type_to_string(uint32_t t);
57 | std::string message_type_to_string(uint32_t t);
58 |
59 | /* --------------------------------------------------------------------- */
60 |
61 | } /* namespace stun */
62 |
63 | #endif
64 |
--------------------------------------------------------------------------------
/include/stun/Utils.h:
--------------------------------------------------------------------------------
1 | #ifndef STUN_UTILS_H
2 | #define STUN_UTILS_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | namespace stun {
9 |
10 | /*
11 | Compute the hmac-sha1 over message.
12 |
13 | uint8_t* message: the data over which we compute the hmac sha
14 | uint32_t nbytes: the number of bytse in message
15 | std::string key: key to use for hmac
16 | uint8_t* output: we write the sha1 into this buffer.
17 | */
18 | bool compute_hmac_sha1(uint8_t* message, uint32_t nbytes, std::string key, uint8_t* output);
19 |
20 | /*
21 | Compute the Message-Integrity of a stun message.
22 | This will not change the given buffer.
23 |
24 | std::vector& buffer: the buffer that contains a valid stun message
25 | std::string key: key to use for hmac
26 | uint8_t* output: will be filled with the correct hmac-sha1 of that represents the integrity message value.
27 | */
28 | bool compute_message_integrity(std::vector& buffer, std::string key, uint8_t* output);
29 |
30 | /*
31 | Compute the fingerprint value for the stun message.
32 | This will not change the given buffer.
33 |
34 | std::vector& buffer: the buffer that contains a valid stun message.
35 | uint32_t& result: will be set to the calculated crc value.
36 | */
37 | bool compute_fingerprint(std::vector& buffer, uint32_t& result);
38 |
39 |
40 | } /* namespace stun */
41 |
42 | #endif
43 |
--------------------------------------------------------------------------------
/include/stun/Writer.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | stun::Writer
4 | -------------
5 |
6 | This will fill the `buffer` member with the binary representation
7 | of the values in the Message. It will also compute the message integrity
8 | and fingerprint CRC32 value when they're found in the attributes of the
9 | Message that is passed into Writer::writeMessage().
10 |
11 | */
12 | #ifndef STUN_WRITER_H
13 | #define STUN_WRITER_H
14 |
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | namespace stun {
23 |
24 | class Writer {
25 | public:
26 | void writeMessage(Message* msg);
27 | void writeMessage(Message* msg, std::string messageIntegrityPassword); /* When you call this, we assume that the message contains a MessageIntegrity attribute. We calculate the hmac-sha and rewrite our internal buffer. This also checks for a Fingerprint attribute; and computers + writes this crc32-value. */
28 |
29 | private:
30 | void writeAttribute(Attribute* attr);
31 | void writeUsername(Username* u);
32 | void writeSoftware(Software* s);
33 | void writePriority(Priority* p);
34 | void writeIceControlled(IceControlled* ic);
35 | void writeIceControlling(IceControlling* ic);
36 | void writeMessageIntegrity(MessageIntegrity* integ);
37 | void writeFingerprint(Fingerprint* fp);
38 | void writeXorMappedAddress(XorMappedAddress* xma);
39 | void writeU8(uint8_t v);
40 | void writeU16(uint16_t v);
41 | void writeU32(uint32_t v);
42 | void writeU64(uint64_t v);
43 | void writeBytes(uint8_t* buf, uint32_t nbytes);
44 | void writeString(StringValue v);
45 | void rewriteU16(size_t index, uint16_t v);
46 | void rewriteU32(size_t index, uint32_t v);
47 |
48 | public:
49 | std::vector buffer;
50 | };
51 |
52 | } /* namespace stun */
53 | #endif
54 |
--------------------------------------------------------------------------------
/include/video/AggregatorVP8.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | AggregatorVP8
4 | -------------
5 |
6 | Used to reconstruct VP8 frames from multiple RTP-VP8 packets. You
7 | call addPacket() with a initialize PacketVP8. We collect data from the
8 | same frame, from multiple packets into one buffer that can be fed into
9 | the VP8 decoder.
10 |
11 | * this is experimental code *
12 |
13 | */
14 | #ifndef VIDEO_AGGREGATOR_VP8_H
15 | #define VIDEO_AGGREGATOR_VP8_H
16 |
17 | #include
18 | #include
19 | #include
20 |
21 | enum {
22 | AGGREGATOR_VP8_ERR_PACKET = -1,
23 | AGGREGATOR_VP8_ERR_PAYLOAD = -2,
24 | AGGREGATOR_VP8_ERR_SEQNUM = -3,
25 | AGGREGATOR_VP8_ERR_BUFLEN = -4,
26 | AGGREGATOR_VP8_WANTS_MORE = 1,
27 | AGGREGATOR_VP8_GOT_FRAME = 2
28 | };
29 |
30 | namespace video {
31 |
32 | std::string aggregator_vp8_result_to_string(int r);
33 |
34 | class AggregatorVP8 {
35 | public:
36 | AggregatorVP8(uint32_t capacity = (1024 * 1024 *2));
37 | ~AggregatorVP8();
38 | int addPacket(rtp::PacketVP8* pkt);
39 |
40 | public:
41 | uint32_t capacity; /* we collect data from multiple PacketVP8 into our 'buffer' this is the max size */
42 | uint8_t* buffer; /* buffer that contains the frame data */
43 | uint32_t pos; /* current write position */
44 | uint32_t nbytes; /* number of bytes currently written to the buffer. */
45 | uint16_t prev_seqnum; /* the previous sequence number that we handled. */
46 | };
47 |
48 | } /* namespace video */
49 |
50 | #endif
51 |
--------------------------------------------------------------------------------
/include/video/DecoderVP8.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | DecoderVP8
4 | ----------
5 | Decodes raw VP8 data. Used to decode the RTP payload data when it's
6 | encoded with VP8.
7 |
8 | */
9 | #ifndef VIDEO_DECODER_VP8_H
10 | #define VIDEO_DECODER_VP8_H
11 |
12 | #define VPX_CODEC_DISABLE_COMPAT 1
13 | #include
14 | #include
15 | #include
16 |
17 | #define vpx_dx_interface (vpx_codec_vp8_dx())
18 |
19 | namespace video {
20 |
21 | class DecoderVP8;
22 | typedef void(*decoder_vp8_on_image)(DecoderVP8* dec, const vpx_image_t* img); /* gets called when the decoder decodes a new image */
23 |
24 | class DecoderVP8 {
25 |
26 | public:
27 | DecoderVP8();
28 | ~DecoderVP8();
29 | int init();
30 | int decode(uint8_t* data, size_t nbytes);
31 |
32 | public:
33 | vpx_codec_ctx_t ctx;
34 | vpx_image_t* img;
35 | bool is_init;
36 |
37 | /* callback */
38 | decoder_vp8_on_image on_image;
39 | void* user;
40 | };
41 |
42 | } /* namespace video */
43 |
44 | #endif
45 |
--------------------------------------------------------------------------------
/include/video/EncoderSettings.h:
--------------------------------------------------------------------------------
1 | #ifndef VIDEO_ENCODER_SETTINGS_H
2 | #define VIDEO_ENCODER_SETTINGS_H
3 |
4 | namespace video {
5 |
6 | class EncoderSettings {
7 |
8 | public:
9 | EncoderSettings();
10 | ~EncoderSettings();
11 | void reset();
12 |
13 | public:
14 | int width;
15 | int height;
16 | int fps_num;
17 | int fps_den;
18 | };
19 |
20 | } /* namespace video */
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/include/video/EncoderVP8.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | EncoderVP8
4 | -----------
5 |
6 | Basic implementation of the VP8 encoder. Used to encode YUV420P data that
7 | one can stream to a WebRTC capable agent.
8 |
9 | Todo
10 | ----
11 |
12 | Optimize for low latency:
13 | - Discussion: https://groups.google.com/a/webmproject.org/forum/#!topic/webm-discuss/TNPgd7Tf9jQ
14 | - Error resilient: https://gist.github.com/roxlu/ceb1e8c95aff5ba60f45#file-vp8_impl-cc-L225-L238
15 | - VP8 config: https://gist.github.com/roxlu/ceb1e8c95aff5ba60f45#file-vp8_impl-cc-L225-L238
16 |
17 |
18 | */
19 |
20 | #ifndef VIDEO_ENCODER_VP8_H
21 | #define VIDEO_ENCODER_VP8_H
22 |
23 | #include
24 | #include
25 | #include
26 | #define vpx_cx_interface (vpx_codec_vp8_cx())
27 |
28 | namespace video {
29 |
30 | class EncoderVP8;
31 | typedef void(*encoder_vp8_on_packet)(EncoderVP8* enc, const vpx_codec_cx_pkt_t* pkt, int64_t pts); /* gets called whenever we output a partition/packet */
32 |
33 | class EncoderVP8 {
34 |
35 | public:
36 | EncoderVP8();
37 | ~EncoderVP8();
38 | int init(EncoderSettings config);
39 | int encode(uint8_t* y, int ystride, uint8_t* u, int ustride, uint8_t* v, int vstride, int64_t pts);
40 | int encode(vpx_image_t* image, int64_t pts);
41 |
42 | public:
43 | EncoderSettings settings;
44 | vpx_image_t img;
45 | vpx_codec_enc_cfg_t cfg;
46 | vpx_codec_ctx_t ctx;
47 | unsigned long frame_duration;
48 | uint64_t nframes;
49 | uint32_t flags; /* Used to e.g. force keyframes */
50 |
51 | /* callback */
52 | encoder_vp8_on_packet on_packet;
53 | void* user;
54 | };
55 |
56 | } /* namespace video */
57 |
58 | #endif
59 |
--------------------------------------------------------------------------------
/include/video/WriterIVF.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | WriterIVF
4 | ---------
5 |
6 | Writes a .ivf file with VP8 data. You can use avconf to mux the
7 | vp8 data into a webm file.
8 |
9 |
10 | ````sh
11 | ./avconv -f ivf -i test.ivf -vcodec copy out.webm
12 | ````
13 |
14 | */
15 | #ifndef VIDEO_WRITER_IVF
16 | #define VIDEO_WRITER_IVF
17 |
18 | #include
19 | #include
20 | #include
21 |
22 | namespace video {
23 |
24 | class WriterIVF {
25 |
26 | public:
27 | WriterIVF();
28 | ~WriterIVF();
29 | int open(std::string filepath, uint16_t width, uint16_t height, uint32_t fpsnum, uint32_t fpsden);
30 | int write(uint8_t* data, uint32_t nbytes, uint64_t timestamp); /* timestamp is actually the frame number ^.^ */
31 | int close();
32 | private:
33 | void writeU16(uint16_t v);
34 | void writeU32(uint32_t v);
35 | void writeU64(uint64_t v);
36 |
37 | public:
38 | std::ofstream ofs;
39 | uint64_t nframes;
40 | };
41 |
42 | } /* namespace video */
43 |
44 |
45 | #endif
46 |
--------------------------------------------------------------------------------
/src/ice/Candidate.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | namespace ice {
7 |
8 | Candidate::Candidate(std::string ip, uint16_t port)
9 | :ip(ip)
10 | ,port(port)
11 | ,on_data(NULL)
12 | ,user(NULL)
13 | {
14 | }
15 |
16 | bool Candidate::init(connection_on_data_callback cb, void* user) {
17 |
18 | if (!ip.size()) {
19 | printf("ice::Candidate - error: candidate::setup(), invalid ip (empty).\n");
20 | return false;
21 | }
22 |
23 | if (!port || port < 1024) {
24 | printf("ice::Candidate - error: candidate::setup(), invalid port; make sure the port is > 1024, now %d\n", port);
25 | return false;
26 | }
27 |
28 | if (!conn.bind(ip, port)) {
29 | return false;
30 | }
31 |
32 | conn.on_data = cb;
33 | conn.user = user;
34 |
35 | return true;
36 | }
37 |
38 | void Candidate::update() {
39 | conn.update();
40 | }
41 |
42 | /* ------------------------------------------------------------- */
43 |
44 | CandidatePair::CandidatePair()
45 | :local(NULL)
46 | ,remote(NULL)
47 | {
48 | }
49 |
50 | CandidatePair::CandidatePair(Candidate* local, Candidate* remote)
51 | :local(local)
52 | ,remote(remote)
53 | {
54 | }
55 |
56 | CandidatePair::~CandidatePair() {
57 | local = NULL;
58 | remote = NULL;
59 | }
60 |
61 | } /* namespace ice */
62 |
--------------------------------------------------------------------------------
/src/ice/Stream.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | namespace ice {
4 |
5 | /* ------------------------------------------------------------------ */
6 |
7 | /* gets called when a candidate receives data. */
8 | static void stream_on_data(std::string rip, uint16_t rport, std::string lip, uint16_t lport, uint8_t* data, uint32_t nbytes, void* user);
9 |
10 | /* ------------------------------------------------------------------ */
11 |
12 | Stream::Stream(uint32_t flags)
13 | :on_data(NULL)
14 | ,user_data(NULL)
15 | ,on_rtp(NULL)
16 | ,user_rtp(NULL)
17 | ,flags(flags)
18 | {
19 |
20 | }
21 |
22 | Stream::~Stream() {
23 |
24 | {
25 | /* local candidates */
26 | std::vector::iterator it = local_candidates.begin();
27 | while (it != local_candidates.end()) {
28 | delete *it;
29 | it = local_candidates.erase(it);
30 | }
31 | }
32 |
33 | {
34 | /* remote candidates */
35 | std::vector::iterator it = remote_candidates.begin();
36 | while (it != remote_candidates.end()) {
37 | delete *it;
38 | it = remote_candidates.erase(it);
39 | }
40 | }
41 |
42 | {
43 | /* candidate pairs */
44 | std::vector::iterator it = pairs.begin();
45 | while (it != pairs.end()) {
46 | delete *it;
47 | it = pairs.erase(it);
48 | }
49 | }
50 | }
51 |
52 | bool Stream::init() {
53 |
54 | for (size_t i = 0; i < local_candidates.size(); ++i) {
55 | if (!local_candidates[i]->init(stream_on_data, this)) {
56 | return false;
57 | }
58 | }
59 |
60 | return true;
61 | }
62 |
63 | void Stream::update() {
64 | for (size_t i = 0; i < local_candidates.size(); ++i) {
65 | local_candidates[i]->update();
66 | }
67 | }
68 |
69 | void Stream::addLocalCandidate(Candidate* c) {
70 | local_candidates.push_back(c);
71 | }
72 |
73 | void Stream::addRemoteCandidate(Candidate* c) {
74 | remote_candidates.push_back(c);
75 | }
76 |
77 | void Stream::addCandidatePair(CandidatePair* p) {
78 | pairs.push_back(p);
79 | }
80 |
81 | void Stream::setCredentials(std::string ufrag, std::string pwd) {
82 | ice_ufrag = ufrag;
83 | ice_pwd = pwd;
84 | }
85 |
86 | CandidatePair* Stream::findPair(std::string rip, uint16_t rport, std::string lip, uint16_t lport) {
87 |
88 | if (pairs.size() == 0) {
89 | return NULL;
90 | }
91 |
92 | for (size_t i = 0; i < pairs.size(); ++i) {
93 | CandidatePair* p = pairs[i];
94 | if (p->local->port != lport) {
95 | continue;
96 | }
97 | if (p->remote->port != rport) {
98 | continue;
99 | }
100 | if (p->local->ip != lip) {
101 | continue;
102 | }
103 | if (p->remote->ip != rip) {
104 | continue;
105 | }
106 | return p;
107 | }
108 |
109 | return NULL;
110 | }
111 |
112 | CandidatePair* Stream::createPair(std::string rip, uint16_t rport, std::string lip, uint16_t lport) {
113 | ice::Candidate* remote_cand = NULL;
114 | ice::Candidate* local_cand = NULL;
115 | ice::CandidatePair* pair = NULL;
116 |
117 | /* We shouldn't find this pair */
118 | pair = findPair(rip, rport, lip, lport);
119 | if (NULL != pair) {
120 | printf("ice::Stream::createPair() - pair already exists. %s:%u <-> %s:%u\n", lip.c_str(), lport, rip.c_str(), rport);
121 | return pair;
122 | }
123 |
124 | local_cand = findLocalCandidate(lip, lport);
125 | if (NULL == local_cand) {
126 | printf("ice::Stream::createPair() - error: cannot find a local candidate; we can only create candidate when the local one has been added alread. (e.g. by the calling app.).\n");
127 | return NULL;
128 | }
129 |
130 | /* Create a new remote candidate or use the one that already exists. */
131 | remote_cand = findRemoteCandidate(rip, rport);
132 | if (NULL == remote_cand) {
133 | remote_cand = new Candidate(rip, rport);
134 | if (NULL == remote_cand) {
135 | printf("ice::Stream::createPair() - error: cannot allocate an ice::Candidate. \n");
136 | return NULL;
137 | }
138 | }
139 |
140 | /* Create a new pair of these local and remote candidates. */
141 | pair = new ice::CandidatePair(local_cand, remote_cand);
142 | if (NULL == pair) {
143 | printf("ice::Agent::handleStunMessage() - error: cannot allocate an ice::CandidatePair.\n");
144 | return NULL;
145 | }
146 |
147 | /* Make sure the stream keeps track of the allocate candidate/pairs. These will be freed by the stream. */
148 | addRemoteCandidate(remote_cand);
149 | addCandidatePair(pair);
150 |
151 | return pair;
152 | }
153 |
154 | Candidate* Stream::findLocalCandidate(std::string ip, uint16_t port) {
155 | for (size_t i = 0; i < local_candidates.size(); ++i) {
156 | Candidate* c = local_candidates[i];
157 | if (c->port != port) {
158 | continue;
159 | }
160 | if (c->ip != ip) {
161 | continue;
162 | }
163 | return c;
164 | }
165 | return NULL;
166 | }
167 |
168 | Candidate* Stream::findRemoteCandidate(std::string ip, uint16_t port) {
169 | for (size_t i = 0; i < remote_candidates.size(); ++i) {
170 | Candidate* c = remote_candidates[i];
171 | if (c->port != port) {
172 | continue;
173 | }
174 | if (c->ip != ip) {
175 | continue;
176 | }
177 | return c;
178 | }
179 | return NULL;
180 | }
181 |
182 | int Stream::sendRTP(uint8_t* data, uint32_t nbytes) {
183 |
184 | /* validate */
185 | if (!data) { return -1; }
186 | if (!nbytes) { return -2; }
187 | if (0 == pairs.size()) {
188 | printf("ice::Stream::sendRTP() - error: cannot send because we have not pairs yet.\n");
189 | return -3;
190 | }
191 |
192 | CandidatePair* pair = pairs[0];
193 | int len = pair->local->srtp_out.protectRTP(data, nbytes);
194 | if (len < 0) {
195 | printf("ice::Stream::sendRTP() - verbose: cannot protect the RTP data. Probably the srtp parser is not yet initialized.\n");
196 | return -4;
197 | }
198 |
199 | for (size_t i = 0; i < pairs.size(); ++i) {
200 | pair = pairs[i];
201 | pair->local->conn.sendTo(pair->remote->ip, pair->remote->port, data, len);
202 | }
203 |
204 | return 0;
205 | }
206 |
207 | /* ------------------------------------------------------------------ */
208 |
209 | /*
210 | This is called whenever a candidate receives data. Each stream (e.g. video, audio, or muxed),
211 | will check if there is an existing candidate pair for the transport. If it doesn't exist
212 | it will create a new one.
213 |
214 | It may happen that a candidate pair is created but never used because another pair is selected.
215 | At this moment the candidate pair is not free'd and simply ignored.
216 |
217 | @todo - stream_on_data, implement candidate/candidate-pair states, so we can free unused pairs.
218 |
219 | */
220 | static void stream_on_data(std::string rip, uint16_t rport,
221 | std::string lip, uint16_t lport,
222 | uint8_t* data, uint32_t nbytes, void* user)
223 | {
224 |
225 | Stream* stream = static_cast(user);
226 | if (stream->on_data) {
227 | stream->on_data(stream, rip, rport, lip, lport, data, nbytes, stream->user_data);
228 | }
229 | }
230 |
231 | } /* namespace ice */
232 |
233 |
--------------------------------------------------------------------------------
/src/ice/Utils.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | namespace ice {
5 |
6 | std::string gen_random_string(const int len) {
7 | std::string s;
8 | static const char alphanum[] =
9 | "0123456789"
10 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
11 | "abcdefghijklmnopqrstuvwxyz";
12 |
13 | for (int i = 0; i < len; ++i) {
14 | s.push_back(alphanum[rand() % (sizeof(alphanum) - 1)]);
15 | }
16 |
17 | return s;
18 | }
19 |
20 | std::vector get_interface_addresses() {
21 | std::vector result;
22 | char buf[512];
23 | int count, i;
24 | uv_interface_address_t* info;
25 | uv_interface_addresses(&info, &count);
26 |
27 | for (i = 0; i < count; ++i) {
28 |
29 | /* skip internal ones, e.g. loopback . */
30 | uv_interface_address_t iface = info[i];
31 | if (0 != iface.is_internal) {
32 | continue;
33 | }
34 |
35 | /* IP4 type addresses. */
36 | if (iface.address.address4.sin_family == AF_INET) {
37 | uv_ip4_name(&iface.address.address4, buf, sizeof(buf));
38 | result.push_back(buf);
39 | }
40 | }
41 |
42 | uv_free_interface_addresses(info, count);
43 |
44 | return result;
45 | }
46 |
47 | } /* namespace ice */
48 |
--------------------------------------------------------------------------------
/src/rtc/Connection.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | /* ----------------------------------------------------------------- */
5 |
6 | static void rtc_connection_udp_alloc_cb(uv_handle_t* handle, size_t nsize, uv_buf_t* buf);
7 | static void rtc_connection_udp_recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned int flags);
8 | static void rtc_connection_udp_send_cb(uv_udp_send_t* req, int status);
9 |
10 | /* ----------------------------------------------------------------- */
11 |
12 | namespace rtc {
13 |
14 | ConnectionUDP::ConnectionUDP()
15 | :loop(NULL)
16 | // ,saddr(NULL)
17 | ,user(NULL)
18 | ,on_data(NULL)
19 | ,port(0)
20 | {
21 |
22 | loop = uv_default_loop();
23 | if (!loop) {
24 | printf("rtc::ConnectionUDP - error: ConnectionUDP() cannot get the default uv loop.\n");
25 | ::exit(1);
26 | }
27 |
28 | }
29 |
30 | bool ConnectionUDP::bind(std::string sip, uint16_t sport) {
31 |
32 | int r;
33 | ip = sip;
34 | port = sport;
35 |
36 | /* create sockaddr */
37 | r = uv_ip4_addr(ip.c_str(), port, &raddr);
38 | if (r != 0) {
39 | printf("rtc::ConnectionUDP - error: cannot create sockaddr_in: %s\n", uv_strerror(r));
40 | return false;
41 | }
42 |
43 | /* initialize the socket */
44 | r = uv_udp_init(loop, &sock);
45 | if (r != 0) {
46 | printf("rtc::ConnectionUDP - error: cannot initialize the UDP socket in ConnectionUDP: %s\n", uv_strerror(r));
47 | return false;
48 | }
49 |
50 | /* bind */
51 | r = uv_udp_bind(&sock, (const struct sockaddr*)&raddr, 0);
52 | if (r != 0) {
53 | printf("rtc::ConnectionUDP - error: cannot bind the UDP socket in ConnectionUDP: %s\n", uv_strerror(r));
54 | return false;
55 | }
56 |
57 | sock.data = (void*) this;
58 |
59 | /* start receiving */
60 | r = uv_udp_recv_start(&sock,
61 | rtc_connection_udp_alloc_cb,
62 | rtc_connection_udp_recv_cb);
63 |
64 | if (r != 0) {
65 | printf("rtc::ConnectionUDP - error: cannot start receiving in ConnectionUDP: %s\n", uv_strerror(r));
66 | return false;
67 | }
68 |
69 | return true;
70 | }
71 |
72 | #if 0
73 | void ConnectionUDP::send(uint8_t* data, uint32_t nbytes) {
74 |
75 | if (!saddr) {
76 | printf("rtc::ConnectionUDP - error: cannot send data in ConnectionUDP(); no data received yet, so we don't know where to send to.\n");
77 | return;
78 | }
79 |
80 | uv_udp_send_t* req = (uv_udp_send_t*)malloc(sizeof(uv_udp_send_t));
81 | if (!req) {
82 | printf("rtc::ConnectionUDP - error: cannot allocate a send request in ConnectionUDP.\n");
83 | return;
84 | }
85 |
86 | printf("rtc::ConnectionUDP - verbose: sending the following data.\n");
87 | printf("-----------------------------------");
88 | int nl = 0;
89 | int c = 0;
90 | for(uint32_t i = 0; i < nbytes; ++i, ++nl) {
91 | if (nl == 32 || c == 0) {
92 | printf("\n\t%02D: ", c);
93 | c++;
94 | nl = 0;
95 | }
96 | printf("%02X ", data[i]);
97 | }
98 | printf("\n-----------------------------------\n");
99 |
100 | /* @todo check nbytes size in ConnectionUDP::send */
101 | /* @todo we def. don't want to allocate everytime when we need to sentin ConnectionUDP. */
102 |
103 | char* buffer_copy = new char[nbytes];
104 | if (!buffer_copy) {
105 | printf("rtc::ConnectionUDP - error: cannot allocate a copy for the send buffer in ConnectionUDP.\n");
106 | free(req);
107 | req = NULL;
108 | return;
109 | }
110 |
111 | memcpy(buffer_copy, data, nbytes);
112 | uv_buf_t buf = uv_buf_init(buffer_copy, nbytes);
113 |
114 | req->data = buffer_copy;
115 |
116 | int r = uv_udp_send(req,
117 | &sock,
118 | &buf,
119 | 1,
120 | (const struct sockaddr*)saddr,
121 | rtc_connection_udp_send_cb);
122 |
123 | if (r != 0) {
124 | printf("rtc:::ConnectionUDP - error: cannot send udp data in ConnectionUDP: %s.\n", uv_strerror(r));
125 | free(req);
126 | free(buffer_copy);
127 | req = NULL;
128 | buffer_copy = NULL;
129 | }
130 | }
131 | #endif
132 |
133 | void ConnectionUDP::sendTo(std::string rip, uint16_t rport, uint8_t* data, uint32_t nbytes) {
134 | uv_udp_send_t* req = (uv_udp_send_t*)malloc(sizeof(uv_udp_send_t));
135 | if (!req) {
136 | printf("rtc::ConnectionUDP - error: cannot allocate a send request in ConnectionUDP.\n");
137 | return;
138 | }
139 |
140 |
141 | printf("rtc::ConnectionUDP - verbose: sending the following data (%u bytes) form %s:%u to %s:%u.\n", nbytes, ip.c_str(), port, rip.c_str(), rport);
142 |
143 | #if 0
144 | printf("-----------------------------------");
145 |
146 | int c = 0;
147 | for(uint32_t i = 0; i < nbytes; ++i) {
148 | if (i == 0 || i % 16 == 0) {
149 | printf("\n\t");
150 | c++;
151 |
152 | }
153 | printf("%02X ", data[i]);
154 | }
155 | printf("\n-----------------------------------\n");
156 | #endif
157 |
158 | /* @todo check nbytes size in ConnectionUDP::send */
159 | /* @todo we def. don't want to allocate everytime when we need to sentin ConnectionUDP. */
160 |
161 | char* buffer_copy = new char[nbytes];
162 | if (!buffer_copy) {
163 | printf("rtc::ConnectionUDP - error: cannot allocate a copy for the send buffer in ConnectionUDP.\n");
164 | free(req);
165 | req = NULL;
166 | return;
167 | }
168 |
169 | memcpy(buffer_copy, data, nbytes);
170 | uv_buf_t buf = uv_buf_init(buffer_copy, nbytes);
171 |
172 | req->data = buffer_copy;
173 |
174 | struct sockaddr_in send_addr;
175 | uv_ip4_addr(rip.c_str(), rport, &send_addr);
176 | int r = uv_udp_send(req,
177 | &sock,
178 | &buf,
179 | 1,
180 | (const struct sockaddr*)&send_addr,
181 | rtc_connection_udp_send_cb);
182 |
183 | if (r != 0) {
184 | printf("rtc:::ConnectionUDP - error: cannot send udp data in ConnectionUDP: %s.\n", uv_strerror(r));
185 | free(req);
186 | free(buffer_copy);
187 | req = NULL;
188 | buffer_copy = NULL;
189 | }
190 | }
191 |
192 | void ConnectionUDP::update() {
193 | uv_run(loop, UV_RUN_NOWAIT);
194 | }
195 |
196 | } /* namespace rtc */
197 |
198 | /* ----------------------------------------------------------------- */
199 |
200 | static void rtc_connection_udp_recv_cb(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned int flags) {
201 |
202 | /* do nothing when we receive 0 as nread. */
203 | if (nread == 0) {
204 | printf("rtc::ConnectionUDP - verbose: received 0 bytes, flags: %d.\n", flags);
205 | return;
206 | }
207 |
208 | rtc::ConnectionUDP* udp = static_cast(handle->data);
209 | /*
210 | if (!udp->saddr) {
211 | udp->saddr = (struct sockaddr*)malloc(sizeof(struct sockaddr));
212 | if (!udp->saddr) {
213 | printf("rtc::ConnectionUDP - error: cannot allocate the `struct sockaddr*` in ConnectionUDP. Out of mem?\n");
214 | exit(1);
215 | }
216 | memcpy(udp->saddr, addr, sizeof(struct sockaddr));
217 | }
218 | */
219 |
220 | if (addr) {
221 | char src_ip[20];
222 | uint16_t port;
223 | const struct sockaddr_in* src_addr = (const struct sockaddr_in*)addr;
224 | uv_ip4_name(src_addr, src_ip, sizeof(src_ip));
225 | port = ntohs(src_addr->sin_port);
226 | printf("rtc::ConnectionUDP - verbose: received from: %s:%d, number of bytes: %ld\n", src_ip, port, nread);
227 | if (udp->on_data) {
228 | udp->on_data(src_ip, port, udp->ip, udp->port, (uint8_t*)buf->base, nread, udp->user);
229 | }
230 | }
231 | }
232 |
233 | static void rtc_connection_udp_alloc_cb(uv_handle_t* handle, size_t nsize, uv_buf_t* buf) {
234 | static char slab[65536];
235 |
236 | if (nsize > sizeof(slab)) {
237 | printf("rtc::ConnectionUDP - error: requested receiver size to large. @todo - this is just a quick implementation.\n");
238 | exit(1);
239 | }
240 |
241 | buf->base = slab;
242 | buf->len = sizeof(slab);
243 | }
244 |
245 | static void rtc_connection_udp_send_cb(uv_udp_send_t* req, int status) {
246 | printf("rtc::ConnectionUDP - ready sending some data, status: %d\n", status);
247 |
248 | /* @todo rtc_connection_udp_send_cb needs to handle the status value.*/
249 | char* ptr = (char*)req->data;
250 | delete[] ptr;
251 | delete req;
252 | req = NULL;
253 | ptr = NULL;
254 | }
255 |
--------------------------------------------------------------------------------
/src/rtp/PacketVP8.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | namespace rtp {
4 |
5 | PacketVP8::PacketVP8() {
6 | reset();
7 | }
8 |
9 | PacketVP8::~PacketVP8() {
10 | reset();
11 | }
12 |
13 | void PacketVP8::reset() {
14 |
15 | /* rtp header */
16 | version = 0;
17 | padding = 0;
18 | extension = 0;
19 | csrc_count = 0;
20 | marker = 0;
21 | payload_type = 0;
22 | sequence_number = 0;
23 | timestamp = 0;
24 |
25 | /* vp8 */
26 | X = 0;
27 | N = 0;
28 | S = 0;
29 | PID = 0;
30 |
31 | I = 0;
32 | L = 0;
33 | T = 0;
34 | K = 0;
35 | PictureID = 0;
36 | TL0PICIDX = 0;
37 | M = 0;
38 | P = 0;
39 | }
40 |
41 | } /* namespace rtp */
42 |
--------------------------------------------------------------------------------
/src/rtp/ReaderVP8.cpp:
--------------------------------------------------------------------------------
1 | #include // nthos
2 | #include
3 | #include
4 | #include
5 |
6 | namespace rtp {
7 |
8 | /* ----------------------------------------------------------- */
9 |
10 | int rtp_vp8_decode(uint8_t* data, uint32_t nbytes, PacketVP8* pkt) {
11 |
12 | uint8_t* buf = data;
13 | int64_t len = nbytes;
14 |
15 | if (!data) { return -1; }
16 | if (!nbytes) { return -2; }
17 | if (!pkt) { return -3; }
18 |
19 |
20 | /* RTP Header */
21 | pkt->version = (buf[0] & 0xC0) >> 6;
22 | pkt->padding = (buf[0] & 0x20) >> 4;
23 | pkt->extension = (buf[0] & 0x10) >> 3;
24 | pkt->csrc_count = (buf[0] & 0x0F);
25 | pkt->marker = (buf[1] & 0x80) >> 7;
26 | pkt->payload_type = (buf[1] & 0x7F);
27 | pkt->sequence_number = ntohs(*(uint16_t*)(buf + 2));
28 | pkt->timestamp = ntohl(*(uint32_t*)(buf + 4));
29 | pkt->ssrc = ntohl(*(uint32_t*)(buf + 8));
30 |
31 | if (pkt->csrc_count != 0) {
32 | printf("ReaderVP::process - error: the csrc_count != 0, we only implemented support for csrc_count === 0.\n");
33 | return -4;
34 | }
35 |
36 | /* VP8-Payload-Descriptor */
37 | pkt->X = (buf[12] & 0x80) >> 7; /* Extended control bits present */
38 | pkt->N = (buf[12] & 0x20) >> 5; /* None reference frame. (if 1, we can discard this frame). */
39 | pkt->S = (buf[12] & 0x10) >> 4; /* Start of VP8 partition */
40 | pkt->PID = (buf[12] & 0x07); /* Partition index */
41 | buf += 13;
42 | len -= 13;
43 |
44 | /* X: |I|L|T|K| RSV | (OPTIONAL) */
45 | if(pkt->X == 1) {
46 | pkt->I = (buf[0] & 0x80) >> 7; /* PictureID present */
47 | pkt->L = (buf[0] & 0x40) >> 6; /* TL0PICIDX present */
48 | pkt->T = (buf[0] & 0x20) >> 5; /* TID present */
49 | pkt->K = (buf[0] & 0x10) >> 4; /* KEYIDX present */
50 | buf++;
51 | len--;
52 | }
53 |
54 | if(pkt->I) {
55 | pkt->M = (buf[0] & 0x80) >> 7; /* M, PictureID extension flag. */
56 |
57 | if(pkt->M) { /* M, if M == 1, the picture ID takes 16 bits */
58 | pkt->PictureID = ntohs(*(uint16_t*)buf) & 0x7FFF;
59 | buf += 2;
60 | len -= 2;
61 | }
62 | else {
63 | buf++;
64 | len--;
65 | }
66 | }
67 |
68 | if (pkt->L) {
69 | buf++;
70 | len--;
71 | }
72 |
73 | if (pkt->T || pkt->K) {
74 | buf++;
75 | len--;
76 | }
77 |
78 | pkt->payload = buf;
79 | pkt->nbytes = len;
80 |
81 | #if 0
82 | printf("ReaderVP8::process - verbose: version: %d, "
83 | "padding: %d, extension: %d, csrc_count: %d, "
84 | "marker: %d, sequence: %u, timestamp: %u, ssrc: %u, payload_type: %u\n",
85 | pkt->version, pkt->padding, pkt->extension,
86 | pkt->csrc_count, pkt->marker, pkt->sequence_number,
87 | pkt->timestamp, pkt->ssrc, pkt->payload_type);
88 |
89 | printf("ReaderVP8::process - verbose: X: %d, N: %d, S: %d, PID: %d, "
90 | "I: %d, L: %d, T: %d, K: %d, M:%d, PictureID: %u, len: %u\n",
91 | pkt->X, pkt->N, pkt->S, pkt->PID,
92 | pkt->I, pkt->L, pkt->T, pkt->K, pkt->M, pkt->PictureID, pkt->nbytes
93 | );
94 | #endif
95 |
96 | return 0;
97 | }
98 |
99 |
100 | } /* namespace rtp */
101 |
--------------------------------------------------------------------------------
/src/rtp/WriterVP8.cpp:
--------------------------------------------------------------------------------
1 | #include /* @todo remove, tmp used to keep track of timestamp */
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | namespace rtp {
9 |
10 | /* ------------------------------------------------------------------------ */
11 |
12 | static uint32_t rtp_vp8_calc_packet_size(uint32_t framelen, uint32_t mtu);
13 |
14 | /* ------------------------------------------------------------------------ */
15 |
16 | WriterVP8::WriterVP8()
17 | :capacity(1024)
18 | ,buffer(NULL)
19 | ,on_packet(NULL)
20 | ,user(NULL)
21 | {
22 |
23 | /* allocate our buffer. */
24 | uint8_t* tmp = (uint8_t*)realloc(buffer, capacity);
25 | if (!tmp) {
26 | printf("WriterVP8 - error: cannot allocate buffer for RTP VP8 writer.\n");
27 | exit(1);
28 | }
29 | buffer = tmp;
30 |
31 | srand(time(NULL));
32 | picture_id = rand();
33 | seqnum = (rand() + 1) & 0x7FFFF; /* not 0 */
34 | ssrc = (rand() + 1) & 0x7FFFF; /* not 0 */
35 | }
36 |
37 | WriterVP8::~WriterVP8() {
38 | if (buffer) {
39 | free(buffer);
40 | }
41 | buffer = NULL;
42 | capacity = 0;
43 | }
44 |
45 | int WriterVP8::packetize(const vpx_codec_cx_pkt_t* pkt) {
46 | /* @todo tmp: hardcoding timestamp to test RTP stream */
47 | static uint64_t start_time = 0;
48 | uint64_t ts = 0;
49 | if (0 == start_time) {
50 | start_time = uv_hrtime();
51 | }
52 | ts = ((uv_hrtime() - start_time) / (1000llu * 1000llu)) * 90;
53 | /* @todo - end */
54 |
55 |
56 | if (!pkt) { return -1; }
57 | if (!buffer) { return -2; }
58 | if (!on_packet){ return -3; }
59 |
60 | uint32_t mtu = 900 - 14; /* the header size for rtp/rtp-vp8 is roughly 14 bytes, @todo we need better heuristics here ^.^ */
61 | uint32_t packet_size = 0;
62 | uint32_t packet_dx = 0;
63 | uint32_t bytes_left = 0;
64 | uint8_t* picid_ptr = (uint8_t*)&picture_id;
65 | uint8_t* tmp = NULL;
66 | PacketVP8 rtp;
67 |
68 | /* do we need to grow? */
69 | while (capacity < mtu) {
70 | capacity *= 2;
71 | tmp = (uint8_t*)realloc(buffer, capacity);
72 | if (NULL == tmp) {
73 | printf("WriterVP8 - error: cannot reallocate the buffer.\n");
74 | }
75 | buffer = tmp;
76 | }
77 |
78 | /* @todo the rtp.N (non-reference frame), should probably be set using a different flag, check out https://gist.github.com/roxlu/ceb1e8c95aff5ba60f45#file-vp8_impl-cc-L42 */
79 |
80 | /* update the given PacketVP8 so it fits the RFC */
81 | rtp.version = 2; /* RTP: version. */
82 | rtp.extension = 0; /* RTP: extension header? -> yes, VP8 */
83 | rtp.csrc_count = 0; /* RTP: num of csrc identifiers */
84 | rtp.sequence_number = seqnum; /* RTP: sequence number. */
85 | rtp.timestamp = pkt->data.frame.pts * 90; /* RTP: timestamp: 90hz. */
86 | rtp.ssrc = ssrc; /* RTP: ssrc */
87 | rtp.payload_type = 100; /* @todo extract the payload value from SDP! */
88 | rtp.PID = pkt->data.frame.partition_id; /* RTP VP8: partition index. */
89 | rtp.S = 1; /* RTP VP8: start of first VP8 partition */
90 | rtp.X = 1; /* RTP VP8: extended control bits are present. */
91 | rtp.N = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? 0 : 1; /* RTP VP8: non-reference frame */
92 | rtp.M = 1; /* RTP VP8: we use 15 bits for the picture_id */
93 | rtp.PictureID = picture_id; /* RTP VP8: picture id */
94 | rtp.payload = buffer;
95 |
96 | if (pkt->data.frame.flags & VPX_FRAME_IS_DROPPABLE) {
97 | exit(1);
98 | }
99 |
100 | /* create packets */
101 | bytes_left = pkt->data.frame.sz;
102 | while (bytes_left > 0) {
103 |
104 | /* calculate the size for this packet. */
105 | packet_size = rtp_vp8_calc_packet_size(bytes_left, mtu);
106 | bytes_left -= packet_size;
107 |
108 | /* set RTP packet properties */
109 | rtp.sequence_number = seqnum;
110 | rtp.marker = ((packet_size < mtu) && (pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) ? 1 : 0;
111 |
112 | /* RTP header */
113 | buffer[0] = (rtp.version & 0x02) << 6; /* RTP: version */
114 | buffer[0] |= (rtp.padding & 0x01) << 5; /* RTP: padding */
115 | buffer[0] |= (rtp.extension & 0x01) << 4; /* RTP: has extension header */
116 | buffer[0] |= (rtp.csrc_count & 0x0f) << 0; /* RTP: csrc count */
117 | buffer[1] = (rtp.marker & 0x01) << 7; /* RTP: marker bit, last packet of frame */
118 | buffer[1] |= (rtp.payload_type & 0x7f) << 0; /* RTP: payload type */
119 |
120 | *(uint16_t*)(buffer + 2) = htons(rtp.sequence_number); /* RTP: sequence number */
121 | *(uint32_t*)(buffer + 4) = htonl(rtp.timestamp); /* RTP: timestamp */
122 | *(uint32_t*)(buffer + 8) = htonl(rtp.ssrc); /* RTP: ssrc */
123 |
124 | /* RTP-VP8 required header */
125 | buffer[12] = (rtp.X & 0x01) << 7; /* RTP VP8: extended control bits set? */
126 | buffer[12] |= (rtp.N & 0x01) << 5; /* RTP VP8: non-reference frame. */
127 | buffer[12] |= (rtp.S & 0x01) << 4; /* RTP VP8: start of vp8-partition. */
128 | buffer[12] |= (rtp.PID & 0x07) << 0; /* RTP VP8: parition id. */
129 |
130 | /* RTP-VP8 extended control bits */
131 | buffer[13] = 0x80; /* RTP VP8: picture id present, all other bits are 0. */
132 | buffer[14] = 0x80 | (picid_ptr[1]); /* RTP VP8: first bit sequence of picture id */
133 | buffer[15] = picid_ptr[0]; /* RTP VP8: second bit sequence of picture id */
134 |
135 | /* copy the VP8 data */
136 | memcpy(buffer + 16, (uint8_t*)(pkt->data.frame.buf) + packet_dx, packet_size);
137 | packet_dx += packet_size;
138 | rtp.nbytes = 16 + packet_size;
139 |
140 | /* call the callback we have a new RTP packet. */
141 | on_packet(&rtp, user);
142 |
143 | #if 0
144 | printf("WriterVP8::packtize - verbose: Marker: %d, X: %d, N: %d, S: %d, PID: %d, payload_type: %d, SSRC: %u, "
145 | "I: %d, L: %d, T: %d, K: %d, M:%d, PictureID: %u, len: %u, timestamp: %u, seqnum: %u\n",
146 | rtp.marker,
147 | rtp.X, rtp.N, rtp.S, rtp.PID, rtp.payload_type, rtp.ssrc,
148 | rtp.I, rtp.L, rtp.T, rtp.K, rtp.M, rtp.PictureID, rtp.nbytes, rtp.timestamp,
149 | rtp.sequence_number
150 | );
151 | #endif
152 |
153 | rtp.S = 0; /* for all other the 'start of partition is 0', except first packet the S = 0. */
154 | seqnum++;
155 |
156 | if (1 == rtp.marker) {
157 | picture_id = (picture_id + 1) & 0x7FFF;
158 | }
159 | }
160 |
161 | picture_id = (picture_id + 1) & 0x7FFF;
162 | return 0;
163 | }
164 |
165 | /* ------------------------------------------------------------------------ */
166 |
167 | static uint32_t rtp_vp8_calc_packet_size(uint32_t framelen, uint32_t mtu) {
168 | if (framelen >= mtu) {
169 | return mtu;
170 | }
171 | else {
172 | return framelen;
173 | }
174 | }
175 |
176 | } /* namespace rtp */
177 |
--------------------------------------------------------------------------------
/src/sdp/SDP.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | namespace sdp {
4 |
5 | SDP::SDP()
6 | :Node(SDP_SESSION)
7 | {
8 | }
9 |
10 | } /* namespace sdp */
11 |
12 |
--------------------------------------------------------------------------------
/src/sdp/Types.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | namespace sdp {
4 |
5 | /* ----------------------------------------------------------- */
6 |
7 | /* generic sdp line */
8 | Node::Node(Type t)
9 | :type(t)
10 | {
11 | }
12 |
13 | Node::~Node() {
14 |
15 | std::vector::iterator it = nodes.begin();
16 | while (it != nodes.end()) {
17 | delete *it;
18 | it = nodes.erase(it);
19 | }
20 | }
21 |
22 | void Node::add(Node* n) {
23 | nodes.push_back(n);
24 | }
25 |
26 | bool Node::find(Type t, std::vector& result) {
27 | for (size_t i = 0; i < nodes.size(); ++i) {
28 | if (nodes[i]->type == t) {
29 | result.push_back(nodes[i]);
30 | }
31 | }
32 | return result.size();
33 | }
34 |
35 | bool Node::find(MediaType t, Media** m) {
36 | Media* media = NULL;
37 | *m = NULL;
38 | for (size_t i = 0; i < nodes.size(); ++i) {
39 | if (nodes[i]->type == SDP_MEDIA) {
40 | media = static_cast(nodes[i]);
41 | if (media->media == t) {
42 | *m = media;
43 | return true;
44 | }
45 | }
46 | }
47 | return false;
48 | }
49 |
50 | bool Node::find(AttrType t, Attribute** a) {
51 | Attribute* attr;
52 | *a = NULL;
53 | for (size_t i = 0; i < nodes.size(); ++i) {
54 | if (nodes[i]->type == SDP_ATTRIBUTE) {
55 | attr = static_cast(nodes[i]);
56 | if (attr->attr_type == t) {
57 | *a = static_cast(nodes[i]);
58 | return true;
59 | }
60 | }
61 | }
62 | return false;
63 | }
64 |
65 | bool Node::find(AttrType t, std::vector& result) {
66 | Attribute* attr;
67 | for (size_t i = 0; i < nodes.size(); ++i) {
68 | if (nodes[i]->type == SDP_ATTRIBUTE) {
69 | attr = static_cast(nodes[i]);
70 | if (attr->attr_type == t) {
71 | result.push_back(attr);
72 | }
73 | }
74 | }
75 | return result.size();
76 | }
77 |
78 | void Node::remove(Type t) {
79 | Node* node;
80 | std::vector::iterator it = nodes.begin();
81 | while (it != nodes.end()) {
82 | node = *it;
83 | if (node->type == t) {
84 | delete node;
85 | it = nodes.erase(it);
86 | continue;
87 | }
88 | ++it;
89 | }
90 | }
91 |
92 | void Node::remove(AttrType t) {
93 | Attribute* attr;
94 | Node* node;
95 | std::vector::iterator it = nodes.begin();
96 | while (it != nodes.end()) {
97 | node = *it;
98 | if (node->type == SDP_ATTRIBUTE) {
99 | attr = static_cast(node);
100 | if (attr->attr_type == t) {
101 | delete node;
102 | it = nodes.erase(it);
103 | continue;
104 | }
105 | }
106 | ++it;
107 | }
108 | }
109 |
110 | /* ----------------------------------------------------------- */
111 |
112 | /* v=0 */
113 | Version::Version()
114 | :version(0)
115 | ,Node(SDP_VERSION)
116 | {
117 | }
118 |
119 | /* o=- 621762799816690644 7 IN IP4 127.0.0.1 */
120 | Origin::Origin()
121 | :net_type(SDP_IN)
122 | ,addr_type(SDP_IP4)
123 | ,sess_version(1)
124 | ,Node(SDP_ORIGIN)
125 | {
126 | }
127 |
128 | /* s=- */
129 | SessionName::SessionName()
130 | :Node(SDP_SESSION_NAME)
131 | {
132 | }
133 |
134 | /* i= */
135 | SessionInformation::SessionInformation()
136 | :Node(SDP_SESSION_INFORMATION)
137 | {
138 | }
139 |
140 | /* u= */
141 | URI::URI()
142 | :Node(SDP_URI)
143 | {
144 | }
145 |
146 | /* e= */
147 | EmailAddress::EmailAddress()
148 | :Node(SDP_EMAIL_ADDRESS)
149 | {
150 | }
151 |
152 | /* p= */
153 | PhoneNumber::PhoneNumber()
154 | :Node(SDP_PHONE_NUMBER)
155 | {
156 | }
157 |
158 | /* c= */
159 | ConnectionData::ConnectionData()
160 | :net_type(SDP_IN)
161 | ,addr_type(SDP_IP4)
162 | ,Node(SDP_CONNECTION_DATA)
163 | {
164 | }
165 |
166 | /* t=0 0 */
167 | Timing::Timing()
168 | :start_time(0)
169 | ,stop_time(0)
170 | ,Node(SDP_TIMING)
171 | {
172 | }
173 |
174 | /* m= */
175 | Media::Media()
176 | :media(SDP_MEDIATYPE_NONE)
177 | ,port(0)
178 | ,proto(SDP_MEDIAPROTO_NONE)
179 | ,fmt(0)
180 | ,Node(SDP_MEDIA)
181 | {
182 | }
183 |
184 | /* a= */
185 | Attribute::Attribute()
186 | :attr_type(SDP_ATTRTYPE_NONE)
187 | ,Node(SDP_ATTRIBUTE)
188 | {
189 | }
190 |
191 | Attribute::Attribute(std::string name, std::string value, AttrType atype)
192 | :name(name)
193 | ,value(value)
194 | ,attr_type(atype)
195 | ,Node(SDP_ATTRIBUTE)
196 | {
197 | }
198 |
199 | /* a=rtcp: */
200 | AttributeRTCP::AttributeRTCP()
201 | :Attribute()
202 | {
203 | attr_type = SDP_ATTR_RTCP;
204 | }
205 |
206 |
207 | /* a=candidate: ... */
208 | AttributeCandidate::AttributeCandidate()
209 | :component_id(0)
210 | ,priority(0)
211 | ,port(0)
212 | ,rel_port(0)
213 | ,Attribute()
214 | {
215 | attr_type = SDP_ATTR_CANDIDATE;
216 | }
217 |
218 | /* a=fingerprint:... */
219 | AttributeFingerprint::AttributeFingerprint()
220 | :Attribute()
221 | {
222 | attr_type = SDP_ATTR_FINGERPRINT;
223 | }
224 |
225 | AttributeFingerprint::AttributeFingerprint(std::string hfunc, std::string fprint)
226 | :hash_func(hfunc)
227 | ,fingerprint(fprint)
228 | ,Attribute()
229 | {
230 | attr_type = SDP_ATTR_FINGERPRINT;
231 | }
232 |
233 | /* a=setup: */
234 | AttributeSetup::AttributeSetup()
235 | :role(SDP_SETUPTYPE_NONE)
236 | ,Attribute()
237 | {
238 | attr_type = SDP_ATTR_SETUP;
239 | }
240 |
241 | AttributeSetup::AttributeSetup(SetupType role)
242 | :role(role)
243 | ,Attribute()
244 | {
245 | attr_type = SDP_ATTR_SETUP;
246 | }
247 |
248 | void AttributeSetup::makeActive() {
249 | role = SDP_ACTIVE;
250 | }
251 |
252 | void AttributeSetup::makePassive() {
253 | role = SDP_PASSIVE;
254 | }
255 | };
256 |
--------------------------------------------------------------------------------
/src/sdp/Utils.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | namespace sdp {
4 |
5 | bool string_to_net_type(std::string& input, NetType& result) {
6 |
7 | result = SDP_NETTYPE_NONE;
8 |
9 | if (input.size() == 0) {
10 | return false;
11 | }
12 |
13 | if (input == "IN") {
14 | result = SDP_IN;
15 | return true;
16 | }
17 |
18 | return false;
19 | }
20 |
21 | /* convert a string to an AddrType */
22 | bool string_to_addr_type(std::string& input, AddrType& result) {
23 |
24 | result = SDP_ADDRTYPE_NONE;
25 |
26 | if (input.size() == 0) {
27 | return false;
28 | }
29 |
30 | if (input == "IP4") {
31 | result = SDP_IP4;
32 | }
33 | else if (input == "IP6") {
34 | result = SDP_IP6;
35 | }
36 |
37 | return result != SDP_ADDRTYPE_NONE;
38 | }
39 |
40 | /* convert a string to an MediaType */
41 | bool string_to_media_type(std::string& input, MediaType& result) {
42 |
43 | result = SDP_MEDIATYPE_NONE;
44 |
45 | if (input.size() == 0) {
46 | return false;
47 | }
48 |
49 | if (input == "video") {
50 | result = SDP_VIDEO;
51 | }
52 | else if (input == "audio") {
53 | result = SDP_AUDIO;
54 | }
55 | else if (input == "text") {
56 | result = SDP_TEXT;
57 | }
58 | else if (input == "message") {
59 | result = SDP_MESSAGE;
60 | }
61 | else if (input == "application") {
62 | result = SDP_APPLICATION;
63 | }
64 |
65 | return result != SDP_MEDIATYPE_NONE;
66 | }
67 |
68 | /* convert a string to an MediaProto */
69 | bool string_to_media_proto(std::string& input, MediaProto& result) {
70 |
71 | result = SDP_MEDIAPROTO_NONE;
72 |
73 | if (input.size() == 0) {
74 | return false;
75 | }
76 |
77 | if (input == "udp") {
78 | result = SDP_UDP;
79 | }
80 | else if (input == "RTP/AVP") {
81 | result = SDP_RTP_AVP;
82 | }
83 | else if (input == "RTP/SAVP") {
84 | result = SDP_RTP_SAVP;
85 | }
86 | else if (input == "RTP/SAVPF") {
87 | result = SDP_RTP_SAVPF;
88 | }
89 |
90 | return result != SDP_MEDIAPROTO_NONE;
91 | }
92 |
93 | /* convert a string to an MediaProto */
94 | bool string_to_cand_type(std::string& input, CandType& result) {
95 |
96 | result = SDP_CANDTYPE_NONE;
97 |
98 | if (input.size() == 0) {
99 | return false;
100 | }
101 |
102 | if (input == "host") {
103 | result = SDP_HOST;
104 | }
105 | else if (input == "host") {
106 | result = SDP_HOST;
107 | }
108 | else if (input == "srflx") {
109 | result = SDP_SRFLX;
110 | }
111 | else if (input == "prflx") {
112 | result = SDP_PRFLX;
113 | }
114 | else if (input == "relay") {
115 | result = SDP_RELAY;
116 | }
117 |
118 | return result != SDP_CANDTYPE_NONE;
119 | }
120 |
121 | bool string_to_setup_type(std::string& input, SetupType& result) {
122 |
123 | result = SDP_SETUPTYPE_NONE;
124 |
125 | if (input.size() == 0) {
126 | return false;
127 | }
128 |
129 | if (input == "active") {
130 | result = SDP_ACTIVE;
131 | }
132 | else if (input == "passive") {
133 | result = SDP_PASSIVE;
134 | }
135 | else if (input == "actpass") {
136 | result = SDP_ACTPASS;
137 | }
138 | else if (input == "holdconn") {
139 | result = SDP_HOLDCONN;
140 | }
141 |
142 | return result != SDP_SETUPTYPE_NONE;
143 | }
144 |
145 | std::string net_type_to_string(NetType type) {
146 | switch (type) {
147 | case SDP_IN: { return "IN"; }
148 | default: { return "unknown"; }
149 | };
150 | }
151 |
152 | std::string addr_type_to_string(AddrType type) {
153 | switch (type) {
154 | case SDP_IP4: { return "IP4"; }
155 | case SDP_IP6: { return "IP6"; }
156 | default: { return "unknown"; }
157 | };
158 | }
159 |
160 | std::string media_type_to_string(MediaType type) {
161 | switch (type) {
162 | case SDP_VIDEO: { return "video"; }
163 | case SDP_AUDIO: { return "audio"; }
164 | case SDP_TEXT: { return "text"; }
165 | case SDP_APPLICATION: { return "application"; }
166 | case SDP_MESSAGE: { return "message"; }
167 | default: { return "unknown"; }
168 | }
169 | }
170 |
171 | std::string media_proto_to_string(MediaProto proto) {
172 | switch (proto) {
173 | case SDP_UDP: { return "udp"; }
174 | case SDP_RTP_AVP: { return "RTP/AVP"; }
175 | case SDP_RTP_SAVP: { return "RTP/SAVP"; }
176 | case SDP_RTP_SAVPF: { return "RTP/SAVPF"; }
177 | default: { return "unknown"; }
178 | };
179 | }
180 |
181 | std::string cand_type_to_string(CandType type) {
182 | switch (type) {
183 | case SDP_HOST: { return "host"; }
184 | case SDP_SRFLX: { return "srflx"; }
185 | case SDP_PRFLX: { return "prflx"; }
186 | case SDP_RELAY: { return "relay"; }
187 | default: { return "unknown"; }
188 | }
189 | }
190 |
191 | std::string setup_type_to_string(SetupType type) {
192 | switch (type) {
193 | case SDP_ACTIVE: { return "active"; }
194 | case SDP_PASSIVE: { return "passive"; }
195 | case SDP_ACTPASS: { return "actpass"; }
196 | case SDP_HOLDCONN: { return "holdconn"; }
197 | default: { return "unknown"; }
198 | }
199 | }
200 |
201 | } /* namespace sdp */
202 |
--------------------------------------------------------------------------------
/src/sdp/Writer.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | namespace sdp {
5 |
6 | std::string Writer::toString(SDP* sdp) {
7 |
8 | std::string result;
9 |
10 | for (size_t i = 0; i < sdp->nodes.size(); ++i) {
11 | result += toString(sdp->nodes[i]);
12 | }
13 |
14 | return result;
15 | }
16 |
17 | std::string Writer::toString(Node* node) {
18 |
19 | std::string result;
20 |
21 | switch (node->type) {
22 | case SDP_VERSION: { result += toString(static_cast(node)); break; }
23 | case SDP_ORIGIN: { result += toString(static_cast(node)); break; }
24 | case SDP_SESSION_NAME: { result += toString(static_cast(node)); break; }
25 | case SDP_TIMING: { result += toString(static_cast(node)); break; }
26 | case SDP_MEDIA: { result += toString(static_cast(node)); break; }
27 | case SDP_ATTRIBUTE: { result += toString(static_cast(node)); break; }
28 | default: {
29 | printf("Error: cannot convert node type to string: %d \n", node->type);
30 | break;
31 | }
32 | }
33 |
34 | return result;
35 | }
36 |
37 | /* v=0 */
38 | std::string Writer::toString(Version* v) {
39 | std::stringstream ss;
40 | ss << "v=" << v->version << "\r\n";
41 | return ss.str();
42 | }
43 |
44 | /* o=- 621762799816690644 7 IN IP4 127.0.0.1 */
45 | std::string Writer::toString(Origin* o) {
46 | std::stringstream ss;
47 |
48 | ss << "o="
49 | << o->username << " "
50 | << o->sess_id << " "
51 | << o->sess_version << " "
52 | << sdp::net_type_to_string(o->net_type) << " "
53 | << sdp::addr_type_to_string(o->addr_type) << " "
54 | << o->unicast_address.c_str()
55 | << "\r\n";
56 |
57 | return ss.str();
58 | }
59 |
60 | /* s= */
61 | std::string Writer::toString(SessionName* s) {
62 | std::stringstream ss;
63 | ss << "s=" << s->session_name << "\r\n";
64 | return ss.str();
65 | }
66 |
67 | /* t= */
68 | std::string Writer::toString(Timing* t) {
69 | std::stringstream ss;
70 |
71 | ss << "t="
72 | << t->start_time << " "
73 | << t->stop_time
74 | << "\r\n";
75 |
76 | return ss.str();
77 | }
78 |
79 | /* m= */
80 | std::string Writer::toString(Media* m) {
81 |
82 | std::stringstream ss;
83 |
84 | ss << "m="
85 | << sdp::media_type_to_string(m->media) << " "
86 | << m->port << " "
87 | << sdp::media_proto_to_string(m->proto) << " "
88 | << m->fmt
89 | << "\r\n";
90 |
91 | /* @todo -> add RTPMAP attribute ids. */
92 |
93 | /* add the attributes of a media element. */
94 | for (size_t i = 0; i < m->nodes.size(); ++i) {
95 | ss << toString(m->nodes[i]);
96 | }
97 |
98 | return ss.str();
99 | }
100 |
101 | /* a= */
102 | std::string Writer::toString(Attribute* a) {
103 | std::stringstream ss;
104 | //printf(">> ATTR: %s\n", a->value.c_str());
105 | switch (a->attr_type) {
106 |
107 | /* generic name-value attributes */
108 | case SDP_ATTR_ICE_UFRAG:
109 | case SDP_ATTR_ICE_PWD:
110 | case SDP_ATTR_ICE_OPTIONS:
111 | case SDP_ATTR_UNKNOWN: {
112 | ss << "a=" << a->name ;
113 |
114 | if (a->value.size()) {
115 | ss << ":" << a->value;
116 | }
117 |
118 | ss << "\r\n";
119 |
120 | return ss.str();
121 | }
122 |
123 | case SDP_ATTR_SETUP: {
124 | return toString(static_cast(a));
125 | }
126 |
127 | case SDP_ATTR_FINGERPRINT: {
128 | return toString(static_cast(a));
129 | }
130 |
131 | case SDP_ATTR_CANDIDATE: {
132 | return toString(static_cast(a));
133 | }
134 |
135 | /* unknown/unhandled. */
136 | default: {
137 | printf("Error: cannot convert attribute type to a string: %d\n", a->attr_type);
138 | return "";
139 | }
140 | }
141 | }
142 |
143 | /* c= */
144 | std::string Writer::toString(AttributeCandidate* c) {
145 | std::stringstream ss;
146 |
147 | ss << "a=candidate:"
148 | << c->foundation << " "
149 | << c->component_id << " "
150 | << c->transport << " "
151 | << c->priority << " "
152 | << c->connection_address << " "
153 | << c->port << " "
154 | << "typ "
155 | << sdp::cand_type_to_string(c->cand_type)
156 | << "\r\n";
157 |
158 | return ss.str();
159 | }
160 |
161 | /* a=fingerprint: ... */
162 | std::string Writer::toString(AttributeFingerprint* f) {
163 | std::stringstream ss;
164 |
165 | ss << "a=fingerprint:"
166 | << f->hash_func << " "
167 | << f->fingerprint
168 | << "\r\n";
169 |
170 | return ss.str();
171 | }
172 |
173 | /* a=setup: */
174 | std::string Writer::toString(AttributeSetup* s) {
175 | std::stringstream ss;
176 |
177 | ss << "a=setup:"
178 | << sdp::setup_type_to_string(s->role)
179 | << "\r\n";
180 |
181 | return ss.str();
182 | }
183 |
184 | } /* namespace sdp */
185 |
--------------------------------------------------------------------------------
/src/signaling/Room.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | namespace sig {
4 |
5 | Room::Room(std::string name, std::string sdp)
6 | :sdp(sdp)
7 | ,name(name)
8 | {
9 |
10 | }
11 |
12 | } /* namespace sig */
13 |
--------------------------------------------------------------------------------
/src/signaling/Signaling.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | /* @todo - add SSL support to signaling */
7 |
8 | namespace sig {
9 |
10 | /* -------------------------------------------------------------- */
11 |
12 | static void* signaling_server_thread(void* user);
13 | static int signaling_event_handler(struct mg_connection* conn, enum mg_event ev);
14 |
15 | /* ---------------------------------------------------------------- */
16 |
17 | Signaling::Signaling()
18 | :server(NULL)
19 | ,must_stop(true)
20 | {
21 | }
22 |
23 | Signaling::~Signaling() {
24 |
25 | /* free the rooms */
26 | for (size_t i = 0; i < rooms.size(); ++i) {
27 | delete rooms[i];
28 | }
29 |
30 | rooms.clear();
31 |
32 | if (server) {
33 | mg_destroy_server(&server);
34 | server = NULL;
35 | }
36 |
37 | must_stop = true;
38 | }
39 |
40 |
41 | int Signaling::init(SignalingSettings cfg) {
42 |
43 | /* validate */
44 | if (0 == cfg.port.size()) {
45 | printf("Signaling::init() - error: invalid settings, no port given.\n");
46 | return -1;
47 | }
48 |
49 | /* initialize our mongoose based websocket server. */
50 | settings = cfg;
51 |
52 | return 0;
53 | }
54 |
55 | int Signaling::addRoom(Room* room) {
56 | if (!room) { return -1; }
57 |
58 | rooms.push_back(room);
59 |
60 | return 0;
61 | }
62 |
63 | int Signaling::start() {
64 |
65 | /* validate */
66 | if (0 == settings.port.size()) {
67 | printf("Signaling::start() - error: cannot start signaling server because no port was set in the settings. Did you call init()?\n");
68 | return -1;
69 | }
70 |
71 | if (0 == rooms.size()) {
72 | printf("Signaling::start() - error: makes no sense to start withouth any rooms.\n");
73 | return -2;
74 | }
75 |
76 | must_stop = false;
77 |
78 | /* init mongoose */
79 | server = mg_create_server(this, signaling_event_handler); /* this will be set to mg_connection::server_param */
80 | if (!server) {
81 | printf("Signaling::start() - error: cannot create the mongoose server.\n");
82 | must_stop = true;
83 | return -3;
84 | }
85 |
86 | mg_set_option(server, "listening_port", settings.port.c_str());
87 |
88 | mg_start_thread(signaling_server_thread, this);
89 |
90 | return 0;
91 | }
92 |
93 | int Signaling::stop() {
94 |
95 | if (NULL == server) {
96 | printf("Signaling::stop() - error: cannot stop the signaling server because it seems we're not started yet.\n");
97 | return -1;
98 | }
99 |
100 | must_stop = true;
101 |
102 | return 0;
103 | }
104 |
105 | Room* Signaling::findRoom(std::string name) {
106 | for (size_t i = 0; i < rooms.size(); ++i) {
107 | if (rooms[i]->name == name) {
108 | return rooms[i];
109 | }
110 | }
111 | return NULL;
112 | }
113 |
114 | void Signaling::handleJoin(struct mg_connection* conn) {
115 | if (NULL == conn) { return ; }
116 | if (conn->content_len < 5) { return ; }
117 |
118 | /* extract the name of the room. */
119 | std::string data(conn->content + 5, conn->content_len - 5);
120 | std::stringstream ss(data);
121 | std::string room_name;
122 | std::string response;
123 | ss >> room_name;
124 |
125 | if (0 == room_name.size()) {
126 | printf("Signaling::handleJoin() - error: invalid room name, empty.\n");
127 | return ;
128 | }
129 |
130 | Room* room = findRoom(room_name);
131 | if (NULL == room) {
132 | printf("Signaling::handleJoin() - error; cannot find the room: `%s`\n", room_name.c_str());
133 | return;
134 | }
135 |
136 | response = "sdp " +room_name +" " +room->sdp;
137 |
138 | mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, response.c_str(), response.size());
139 | }
140 |
141 | /* -------------------------------------------------------------- */
142 |
143 | static int signaling_event_handler(struct mg_connection* conn, enum mg_event ev) {
144 |
145 | if (MG_REQUEST == ev) {
146 |
147 | if (conn->is_websocket) {
148 |
149 | /* join command */
150 | if (conn->content_len >= 5 && 0 == memcmp(conn->content, "join", 4)) {
151 |
152 | /* @todo handle invalid joins. */
153 | Signaling* s = static_cast(conn->server_param);
154 | s->handleJoin(conn);
155 | }
156 |
157 | //return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ? MG_FALSE : MG_TRUE;
158 | return MG_TRUE;
159 | }
160 | }
161 | else if (MG_AUTH == ev) {
162 | return MG_TRUE;
163 | }
164 |
165 | return MG_FALSE;
166 | }
167 |
168 | static void* signaling_server_thread(void* user) {
169 |
170 | Signaling* s = static_cast(user);
171 |
172 | while (false == s->must_stop) {
173 | mg_poll_server(s->server, 100);
174 | }
175 |
176 | mg_destroy_server(&s->server);
177 | s->server = NULL;
178 |
179 | return 0;
180 | }
181 |
182 | } /* namespace sig */
183 |
--------------------------------------------------------------------------------
/src/signaling/SignalingSettings.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | namespace sig {
4 |
5 | SignalingSettings::SignalingSettings() {
6 | }
7 |
8 | SignalingSettings::~SignalingSettings() {
9 | }
10 |
11 | } /* namespace sig */
12 |
--------------------------------------------------------------------------------
/src/srtp/ParserSRTP.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include /* for the cipher suites */
5 |
6 | namespace srtp {
7 |
8 | bool ParserSRTP::is_lib_init = false;
9 |
10 | ParserSRTP::ParserSRTP()
11 | :is_init(false)
12 | {
13 |
14 | /* Initialize the srtp library. */
15 | if (false == is_lib_init) {
16 | err_status_t err = srtp_init();
17 | if (err != err_status_ok) {
18 | printf("ParserSRTP::ParserSRTP() - error: cannot initialize libsrtp(): %d\n", err);
19 | exit(1);
20 | }
21 | is_lib_init = true;
22 | }
23 | }
24 |
25 | ParserSRTP::~ParserSRTP() {
26 |
27 | if (policy.key) {
28 | delete[] policy.key;
29 | policy.key = NULL;
30 | }
31 |
32 | if (is_init) {
33 | srtp_dealloc(session);
34 | }
35 |
36 | is_init = false;
37 | }
38 |
39 | int ParserSRTP::init(const char* cipher, bool inbound, const uint8_t* key, const uint8_t* salt) {
40 | err_status_t err;
41 |
42 | if (!cipher) { return -1; }
43 | if (!key) { return -2; }
44 | if (!salt) { return -3; }
45 | if (true == is_init) { return -4; }
46 |
47 | if (0 == strcasecmp(cipher, "SRTP_AES128_CM_SHA1_80")) {
48 | crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
49 | crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
50 | }
51 | else if (0 == strcasecmp(cipher, "SRTP_AES128_CM_SHA1_32")) {
52 | crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);
53 | crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); /* see http://dxr.mozilla.org/mozilla-central/source/media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp#228 @todo check if this is correct! */
54 | printf("srtp::ParserSRTP::init() - warning, we're using crypto_policy_set_aes_cm_128_hmac_sha1_80() .. but didn't test this; \n");
55 | }
56 | else {
57 | printf("srtp::ParserSRTP::init() - error: invalid/unsupported cipher %s\n", cipher);
58 | return -5;
59 | }
60 |
61 | /* Allocate space for the key! */
62 | policy.key = new uint8_t[SRTP_PARSER_MASTER_LEN];
63 | if (!policy.key) {
64 | printf("srtp::ParserSRTP - error: cannot alloc the key for the policy.\n");
65 | return -6;
66 | }
67 |
68 | policy.ssrc.type = (true == inbound) ? ssrc_any_inbound : ssrc_any_outbound;
69 | policy.window_size = 128; /* @todo http://mxr.mozilla.org/mozilla-central/source/media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp */
70 | policy.allow_repeat_tx = 0;
71 | policy.next = NULL;
72 |
73 | /* Copy the key */
74 | memcpy(policy.key, key, SRTP_PARSER_MASTER_KEY_LEN);
75 | memcpy(policy.key + SRTP_PARSER_MASTER_KEY_LEN, salt, SRTP_PARSER_MASTER_SALT_LEN);
76 |
77 | err = srtp_create(&session, &policy);
78 | if (err != err_status_ok) {
79 | printf("srtp::ParserSRTP - error: cannot create a policy: %d\n", err);
80 | return -7;
81 | }
82 |
83 | is_init = true;
84 |
85 | return 0;
86 | }
87 |
88 | int ParserSRTP::protectRTP(void* in, uint32_t nbytes) {
89 | err_status_t err;
90 | int len = nbytes;
91 |
92 | if (!in) { return -1; }
93 | if (!nbytes) { return -2; }
94 |
95 | if (false == is_init) {
96 | printf("srtp::ParserSRTP::protectRTP() - error: trying to protect data, but we're not initialized.\n");
97 | return -3;
98 | }
99 |
100 | err = srtp_protect(session, in, &len);
101 | if (err != err_status_ok) {
102 | printf("srtp::ParserSRTP::protectRTP() - error: cannot protect the given srtp packet: %d\n", err);
103 | return -3;
104 | }
105 |
106 | return len;
107 | }
108 |
109 | int ParserSRTP::protectRTCP(void* in, uint32_t nbytes) {
110 | /* @todo implement */
111 | return 0;
112 | }
113 |
114 | int ParserSRTP::unprotectRTP(void* in, uint32_t nbytes) {
115 | err_status_t err;
116 | int len = nbytes;
117 |
118 | /* validate. */
119 | if (!in) { return -1; }
120 | if (!nbytes) { return -2; }
121 |
122 | if (false == is_init) {
123 | printf("srtp::ParserSRTP::unprotectRTP() - error: trying to unprotect data, but we're not initialized.\n");
124 | return -3;
125 | }
126 |
127 | err = srtp_unprotect(session, in, &len);
128 | if (err != err_status_ok) {
129 | printf("srtp::ParserSRTP::unprotectRTP() - error: cannot unprotect the given SRTP packet: %d\n", err);
130 | return -4;
131 | }
132 |
133 | printf("srtp::ParserSRTP::unprotectRTP() - verbose: successfully unprotected a SRTP packet! length: %d\n", len);
134 |
135 | return len;
136 | }
137 |
138 | int ParserSRTP::unprotectRTCP(void* in, uint32_t nbytes) {
139 | /* @todo implement */
140 | return 0;
141 | }
142 |
143 | } /* namespace srtp */
144 |
--------------------------------------------------------------------------------
/src/stun/Attribute.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | namespace stun {
5 |
6 | /* --------------------------------------------------------------------- */
7 |
8 | Attribute::Attribute(uint16_t type)
9 | :type(type)
10 | ,length(0)
11 | ,nbytes(0)
12 | ,offset(0)
13 | {
14 | }
15 |
16 | /* --------------------------------------------------------------------- */
17 |
18 | XorMappedAddress::XorMappedAddress()
19 | :Attribute(STUN_ATTR_XOR_MAPPED_ADDRESS)
20 | ,family(0)
21 | ,port(0)
22 | {
23 | }
24 |
25 | XorMappedAddress::XorMappedAddress(std::string addr, uint16_t port, uint8_t fam)
26 | :Attribute(STUN_ATTR_XOR_MAPPED_ADDRESS)
27 | ,address(addr)
28 | ,port(port)
29 | ,family(fam)
30 | {
31 | }
32 |
33 | /* --------------------------------------------------------------------- */
34 |
35 | Fingerprint::Fingerprint()
36 | :Attribute(STUN_ATTR_FINGERPRINT)
37 | ,crc(0)
38 | {
39 |
40 | }
41 |
42 | /* --------------------------------------------------------------------- */
43 |
44 | Priority::Priority()
45 | :Attribute(STUN_ATTR_PRIORITY)
46 | ,value(0)
47 | {
48 | }
49 |
50 | /* --------------------------------------------------------------------- */
51 |
52 | IceControlled::IceControlled()
53 | :Attribute(STUN_ATTR_ICE_CONTROLLED)
54 | ,tie_breaker(0)
55 | {
56 | }
57 |
58 | /* --------------------------------------------------------------------- */
59 |
60 | IceControlling::IceControlling()
61 | :Attribute(STUN_ATTR_ICE_CONTROLLING)
62 | ,tie_breaker(0)
63 | {
64 | }
65 |
66 | /* --------------------------------------------------------------------- */
67 |
68 | MessageIntegrity::MessageIntegrity()
69 | :Attribute(STUN_ATTR_MESSAGE_INTEGRITY)
70 | {
71 | memset(sha1, 0x00, 20);
72 | }
73 |
74 | /* --------------------------------------------------------------------- */
75 |
76 |
77 |
78 | } /* namespace stun */
79 |
--------------------------------------------------------------------------------
/src/stun/Message.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | namespace stun {
7 |
8 | /* --------------------------------------------------------------------- */
9 |
10 | Message::Message(uint16_t type)
11 | :type(type)
12 | ,length(0)
13 | ,cookie(0x2112a442)
14 | {
15 | }
16 |
17 | Message::~Message() {
18 | std::vector::iterator it = attributes.begin();
19 | while (it != attributes.end()) {
20 | delete *it;
21 | it = attributes.erase(it);
22 | }
23 | }
24 |
25 | void Message::addAttribute(Attribute* attr) {
26 | attributes.push_back(attr);
27 | }
28 |
29 | void Message::copyTransactionID(Message* from) {
30 |
31 | if (!from) {
32 | printf("Error: tryign to copy a transaction ID, but the message is invalid in stun::Message.\n");
33 | return;
34 | }
35 |
36 | transaction[0] = from->transaction[0];
37 | transaction[1] = from->transaction[1];
38 | transaction[2] = from->transaction[2];
39 | }
40 |
41 | void Message::setTransactionID(uint32_t a, uint32_t b, uint32_t c) {
42 | transaction[0] = a;
43 | transaction[1] = b;
44 | transaction[2] = c;
45 | }
46 |
47 | bool Message::find(MessageIntegrity** result) {
48 | return find(STUN_ATTR_MESSAGE_INTEGRITY, result);
49 | }
50 |
51 | bool Message::find(XorMappedAddress** result) {
52 | return find(STUN_ATTR_XOR_MAPPED_ADDRESS, result);
53 | }
54 |
55 | bool Message::find(Fingerprint** result) {
56 | return find(STUN_ATTR_FINGERPRINT, result);
57 | }
58 |
59 | bool Message::hasAttribute(AttributeType atype) {
60 | for (size_t i = 0; i < attributes.size(); ++i) {
61 | if (attributes[i]->type == atype) {
62 | return true;
63 | }
64 | }
65 | return false;
66 | }
67 |
68 | bool Message::computeMessageIntegrity(std::string key) {
69 |
70 | MessageIntegrity* integ = NULL;
71 | if (!key.size()) {
72 | printf("Error: cannot compute message integrity in stun::Message because the key is empty.\n");
73 | return false;
74 | }
75 |
76 | if (!attributes.size() || !find(&integ)) {
77 | printf("Error: cannot compute the message integrity in stun::Message because the message doesn't contain a MessageIntegrity attribute.\n");
78 | return false;
79 | }
80 |
81 | return compute_message_integrity(buffer, key, integ->sha1);
82 | }
83 |
84 |
85 | bool Message::computeFingerprint() {
86 |
87 | Fingerprint* finger = NULL;
88 | if (!attributes.size() || !find(&finger)) {
89 | printf("Error: cannot compute fingerprint because there is not fingerprint attribute.\n");
90 | return false;
91 | }
92 |
93 | return compute_fingerprint(buffer, finger->crc);
94 | }
95 |
96 | } /* namespace stun */
97 |
--------------------------------------------------------------------------------
/src/stun/Types.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | namespace stun {
4 |
5 | /* --------------------------------------------------------------------- */
6 |
7 | std::string attribute_type_to_string(uint32_t t) {
8 | switch (t) {
9 | case STUN_ATTR_TYPE_NONE: { return "STUN_ATTR_TYPE_NONE"; }
10 | case STUN_ATTR_MAPPED_ADDR: { return "STUN_ATTR_MAPPED_ADDR"; }
11 | case STUN_ATTR_CHANGE_REQ: { return "STUN_ATTR_CHANGE_REQ"; }
12 | case STUN_ATTR_USERNAME: { return "STUN_ATTR_USERNAME"; }
13 | case STUN_ATTR_MESSAGE_INTEGRITY: { return "STUN_ATTR_MESSAGE_INTEGRITY"; }
14 | case STUN_ATTR_ERR_CODE: { return "STUN_ATTR_ERR_CODE"; }
15 | case STUN_ATTR_UNKNOWN_ATTRIBUTES: { return "STUN_ATTR_UNKNOWN_ATTRIBUTES"; }
16 | case STUN_ATTR_CHANNEL_NUMBER: { return "STUN_ATTR_CHANNEL_NUMBER"; }
17 | case STUN_ATTR_LIFETIME: { return "STUN_ATTR_LIFETIME"; }
18 | case STUN_ATTR_XOR_PEER_ADDR: { return "STUN_ATTR_XOR_PEER_ADDR"; }
19 | case STUN_ATTR_DATA: { return "STUN_ATTR_DATA"; }
20 | case STUN_ATTR_REALM: { return "STUN_ATTR_REALM"; }
21 | case STUN_ATTR_NONCE: { return "STUN_ATTR_NONCE"; }
22 | case STUN_ATTR_XOR_RELAY_ADDRESS: { return "STUN_ATTR_XOR_RELAY_ADDRESS"; }
23 | case STUN_ATTR_REQ_ADDRESS_FAMILY: { return "STUN_ATTR_REQ_ADDRESS_FAMILY"; }
24 | case STUN_ATTR_EVEN_PORT: { return "STUN_ATTR_EVEN_PORT"; }
25 | case STUN_ATTR_REQUESTED_TRANSPORT: { return "STUN_ATTR_REQUESTED_TRANSPORT"; }
26 | case STUN_ATTR_DONT_FRAGMENT: { return "STUN_ATTR_DONT_FRAGMENT"; }
27 | case STUN_ATTR_XOR_MAPPED_ADDRESS: { return "STUN_ATTR_XOR_MAPPED_ADDRESS"; }
28 | case STUN_ATTR_RESERVATION_TOKEN: { return "STUN_ATTR_RESERVATION_TOKEN"; }
29 | case STUN_ATTR_PRIORITY: { return "STUN_ATTR_PRIORITY"; }
30 | case STUN_ATTR_USE_CANDIDATE: { return "STUN_ATTR_USE_CANDIDATE"; }
31 | case STUN_ATTR_PADDING: { return "STUN_ATTR_PADDING"; }
32 | case STUN_ATTR_RESPONSE_PORT: { return "STUN_ATTR_RESPONSE_PORT"; }
33 | case STUN_ATTR_SOFTWARE: { return "STUN_ATTR_SOFTWARE"; }
34 | case STUN_ATTR_ALTERNATE_SERVER: { return "STUN_ATTR_ALTERNATE_SERVER"; }
35 | case STUN_ATTR_FINGERPRINT: { return "STUN_ATTR_FINGERPRINT"; }
36 | case STUN_ATTR_ICE_CONTROLLED: { return "STUN_ATTR_ICE_CONTROLLED"; }
37 | case STUN_ATTR_ICE_CONTROLLING: { return "STUN_ATTR_ICE_CONTROLLING"; }
38 | case STUN_ATTR_RESPONSE_ORIGIN: { return "STUN_ATTR_RESPONSE_ORIGIN"; }
39 | case STUN_ATTR_OTHER_ADDRESS: { return "STUN_ATTR_OTHER_ADDRESS"; }
40 | default: { return "unknown"; }
41 | }
42 | }
43 |
44 | std::string message_type_to_string(uint32_t t) {
45 | switch (t) {
46 | case STUN_BINDING_REQUEST: { return "STUN_BINDING_REQUEST"; }
47 | case STUN_BINDING_RESPONSE: { return "STUN_BINDING_RESPONSE"; }
48 | case STUN_BINDING_ERROR_RESPONSE: { return "STUN_BINDING_ERROR_RESPONSE"; }
49 | case STUN_BINDING_INDICATION: { return "STUN_BINDING_INDICATION"; }
50 | default: { return "unknown"; }
51 | }
52 | }
53 |
54 | /* ----------------------------------------------------------- */
55 |
56 | } /* namespace stun */
57 |
--------------------------------------------------------------------------------
/src/stun/Utils.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include /* for crc */
6 |
7 | #include
8 | #include
9 |
10 | namespace stun {
11 |
12 | bool compute_hmac_sha1(uint8_t* message, uint32_t nbytes, std::string key, uint8_t* output) {
13 |
14 | if (!message) {
15 | printf("Error: can't compute hmac_sha1 as the input message is empty in compute_hmac_sha1().\n");
16 | return false;
17 | }
18 |
19 | if (nbytes == 0) {
20 | printf("Error: can't compute hmac_sha1 as the input length is invalid in compute_hmac_sha1().\n");
21 | return false;
22 | }
23 |
24 | if (key.size() == 0) {
25 | printf("Error: can't compute the hmac_sha1 as the key size is 0 in compute_hmac_sha1().\n");
26 | return false;
27 | }
28 |
29 | if (!output) {
30 | printf("Error: can't compute the hmac_sha as the output buffer is NULL in compute_hmac_sha1().\n");
31 | return false;
32 | }
33 |
34 | unsigned int len;
35 | HMAC_CTX ctx;
36 | HMAC_CTX_init(&ctx);
37 |
38 | if (!HMAC_Init_ex(&ctx, (const unsigned char*)key.c_str(), key.size(), EVP_sha1(), NULL)) {
39 | printf("Error: cannot init the HMAC context in compute_hmac_sha1().\n");
40 | }
41 |
42 | HMAC_Update(&ctx, (const unsigned char*)message, nbytes);
43 | HMAC_Final(&ctx, output, &len);
44 |
45 | #if 1
46 | printf("stun::compute_hmac_sha1 - verbose: computing hash over %u bytes, using key `%s`:\n", nbytes, key.c_str());
47 | printf("-----------------------------------\n\t0: ");
48 | int nl = 0, lines = 0;
49 | for (int i = 0; i < nbytes; ++i, ++nl) {
50 | if (nl == 4) {
51 | printf("\n\t");
52 | nl = 0;
53 | lines++;
54 | printf("%d: ", lines);
55 | }
56 | printf("%02X ", message[i]);
57 | }
58 | printf("\n-----------------------------------\n");
59 | #endif
60 |
61 | #if 1
62 | printf("stun::compute_hmac_sha1 - verbose: computed hash: ");
63 | for(unsigned int i = 0; i < len; ++i) {
64 | printf("%02X ", output[i]);
65 | }
66 | printf("\n");
67 | #endif
68 |
69 | return true;
70 |
71 | }
72 |
73 | bool compute_message_integrity(std::vector& buffer, std::string key, uint8_t* output) {
74 |
75 | uint16_t dx = 20;
76 | uint16_t offset = 0;
77 | uint16_t len = 0;
78 | uint16_t type = 0;
79 | uint8_t curr_size[2];
80 |
81 | if (!buffer.size()) {
82 | printf("Error: cannot compute message integrity; buffer empty.\n");
83 | return false;
84 | }
85 |
86 | if (!key.size()) {
87 | printf("Error: cannot compute message inegrity, key empty.\n");
88 | return false;
89 | }
90 |
91 | curr_size[0] = buffer[2];
92 | curr_size[1] = buffer[3];
93 |
94 | while (dx < buffer.size()) {
95 |
96 | type |= buffer[dx + 1] & 0x00FF;
97 | type |= (buffer[dx + 0] << 8) & 0xFF00;
98 | dx += 2;
99 |
100 | len |= (buffer[dx + 1] & 0x00FF);
101 | len |= (buffer[dx + 0] << 8) & 0xFF00;
102 | dx += 2;
103 |
104 | offset = dx;
105 | dx += len;
106 |
107 | /* skip padding. */
108 | while ( (dx & 0x03) != 0 && dx < buffer.size()) {
109 | dx++;
110 | }
111 |
112 | if (type == STUN_ATTR_MESSAGE_INTEGRITY) {
113 | break;
114 | }
115 |
116 | type = 0;
117 | len = 0;
118 | }
119 |
120 | /* rewrite Message-Length header field */
121 | buffer[2] = (offset >> 8) & 0xFF;
122 | buffer[3] = offset & 0xFF;
123 |
124 | /*
125 | and compute the sha1
126 | we subtract the last 4 bytes, which are the attribute-type and
127 | attribute-length of the Message-Integrity field which are not
128 | used.
129 | */
130 | if (!stun::compute_hmac_sha1(&buffer[0], offset - 4, key, output)) {
131 | buffer[2] = curr_size[0];
132 | buffer[3] = curr_size[1];
133 | return false;
134 | }
135 |
136 | /* rewrite message-length. */
137 | buffer[2] = curr_size[0];
138 | buffer[3] = curr_size[1];
139 |
140 | return true;
141 | }
142 |
143 | bool compute_fingerprint(std::vector& buffer, uint32_t& result) {
144 |
145 | uint32_t dx = 20;
146 | uint16_t offset = 0;
147 | uint16_t len = 0; /* messsage-length */
148 | uint16_t type = 0;
149 | uint8_t curr_size[2];
150 |
151 | if (!buffer.size()) {
152 | printf("Error: cannot compute fingerprint because the buffer is empty.\n");
153 | }
154 |
155 | /* copy current message-length */
156 | curr_size[0] = buffer[2];
157 | curr_size[1] = buffer[3];
158 |
159 | /* compute the size that should be used as Message-Length when computing the CRC32 */
160 | while (dx < buffer.size()) {
161 |
162 | type |= buffer[dx + 1] & 0x00FF;
163 | type |= (buffer[dx + 0] << 8) & 0xFF00;
164 | dx += 2;
165 |
166 | len |= buffer[dx + 1] & 0x00FF;
167 | len |= (buffer[dx + 0] << 8) & 0xFF00;
168 | dx += 2;
169 |
170 | offset = dx;
171 | dx += len;
172 |
173 | /* skip padding. */
174 | while ( (dx & 0x03) != 0 && dx < buffer.size()) {
175 | dx++;
176 | }
177 |
178 | if (type == STUN_ATTR_FINGERPRINT) {
179 | break;
180 | }
181 |
182 | type = 0;
183 | len = 0;
184 | }
185 |
186 | /* rewrite message-length */
187 | offset -= 16;
188 | buffer[2] = (offset >> 8) & 0xFF;
189 | buffer[3] = offset & 0xFF;
190 |
191 | result = crc32(0L, &buffer[0], offset + 12) ^ 0x5354554e;
192 |
193 | /* and reset the size */
194 | buffer[2] = curr_size[0];
195 | buffer[3] = curr_size[1];
196 |
197 |
198 | return true;
199 | }
200 |
201 | } /* namespace stun */
202 |
--------------------------------------------------------------------------------
/src/test_webrtc_hmac_sha1.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | int main() {
9 | printf("\n\ntest_hmac_sha1\n\n");
10 |
11 | std::string data = "Lorem ipsum dolor sit amet, consectetuer"
12 | "adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum"
13 | "sociis natoque penatibus et magnis dis parturient montes, nascetur"
14 | "ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu,"
15 | "pretium quis, sem. Nulla consequat massa quis enim. Donec pede"
16 | "justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim"
17 | "justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam"
18 | "dictum felis eu pede mollis pretium. Integer tincidunt. Cras"
19 | "dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend"
20 | "tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend"
21 | "ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a,"
22 | "tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque"
23 | "rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur"
24 | "ullamcorper ultricies nisi. Nam eget dui.";
25 |
26 | std::string key = "z2L4bezUSUjQUqSAJBvnMxza";
27 | unsigned char result[20];
28 | unsigned int len;
29 | HMAC_CTX ctx;
30 |
31 | HMAC_CTX_init(&ctx);
32 |
33 | if (!HMAC_Init_ex(&ctx, (const unsigned char*)key.c_str(), key.size(), EVP_sha1(), NULL)) {
34 | printf("Error: cannot init the HMAC.\n");
35 | }
36 |
37 | HMAC_Update(&ctx, (const unsigned char*)data.c_str(), data.size());
38 | HMAC_Final(&ctx, result, &len);
39 |
40 | printf("Hash: ");
41 | for(unsigned int i = 0; i < len; ++i) {
42 | printf("%02X ", result[i]);
43 | }
44 | printf("\n\n");
45 | }
46 |
--------------------------------------------------------------------------------
/src/test_webrtc_ice.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | test_ice
3 | ---------
4 |
5 | This is used during development of the library; it contains ugly snippets of
6 | code to try/experiment with the different parts of libwebrtc. This is not
7 | meant to be used by end-users of the library. This is also not a unit test.
8 |
9 | */
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | #define PASSWORD "Q9wQj99nsQzldVI5ZuGXbEWRK5RhRXdC" /* our ice-pwd value */
24 |
25 | static void on_udp_data(std::string rip, uint16_t rport, std::string lip, uint16_t lport, uint8_t* data, uint32_t nbytes, void* user); /* gets called when we recieve data on our 'candidate' */
26 | static void on_dtls_data(uint8_t* data, uint32_t nbytes, void* user); /* gets called when we need to send DTLS related data */
27 |
28 | rtc::ConnectionUDP* udp_ptr = NULL;
29 | dtls::Parser* dtls_parser_ptr = NULL;
30 | bool stun_done = false;
31 |
32 | int main() {
33 |
34 | printf("\n\ntest_ice\n\n");
35 |
36 | /* read SDP file. */
37 | std::ifstream ifs("stream.sdp", std::ios::in);
38 | if(!ifs.is_open()) {
39 | printf("Error: cannot read stream.sdp.\n");
40 | exit(1);
41 | }
42 |
43 | std::string input_sdp( (std::istreambuf_iterator(ifs)) , std::istreambuf_iterator());
44 | if (input_sdp.size() == 0) {
45 | printf("Error: input SDP is empty.\n");
46 | exit(1);
47 | }
48 |
49 | /* parse the input */
50 | dtls::Context dtls_ctx;
51 | dtls::Parser dtls_parser;
52 | rtc::ConnectionUDP sock;
53 | stun::Reader stun;
54 |
55 | sdp::SDP offer;
56 | sdp::Reader reader;
57 | sdp::Writer writer;
58 | sdp::Media* audio;
59 | sdp::Media* video;
60 | std::string output_sdp;
61 | std::string ice_ufrag;
62 | std::string ice_pwd;
63 | std::string fingerprint;
64 |
65 | udp_ptr = &sock;
66 | dtls_parser_ptr = &dtls_parser;
67 |
68 | if (reader.parse(input_sdp, &offer) < 0) {
69 | printf("Error: cannot parse input SDP.\n");
70 | exit(1);
71 | }
72 |
73 | /* manipulate the offer so we can use it as an answer. */
74 | {
75 |
76 | if (!dtls_ctx.init("./server-cert.pem", "./server-key.pem")) {
77 | exit(2);
78 | }
79 |
80 | dtls_parser.ssl = dtls_ctx.createSSL();
81 | if(!dtls_parser.init()) {
82 | printf("Cannot init the dtls parser.\n");
83 | exit(1);
84 | }
85 | if (!dtls_ctx.getFingerprint(fingerprint)) {
86 | printf("Error: cannot create dtls/fingerprint sdp attribute.\n");
87 | exit(1);
88 | }
89 |
90 | ice_ufrag = ice::gen_random_string(10);
91 | ice_pwd = ice::gen_random_string(32);
92 |
93 | if (offer.find(sdp::SDP_VIDEO, &video)) {
94 | video->remove(sdp::SDP_ATTR_CANDIDATE);
95 | video->remove(sdp::SDP_ATTR_ICE_UFRAG);
96 | video->remove(sdp::SDP_ATTR_ICE_PWD);
97 | video->remove(sdp::SDP_ATTR_ICE_OPTIONS); /* chrome will try to use google-ice which is a bit different then normal ice */
98 | video->remove(sdp::SDP_ATTR_FINGERPRINT);
99 | video->remove(sdp::SDP_ATTR_SETUP);
100 | video->add(new sdp::Attribute("ice-ufrag", ice_ufrag, sdp::SDP_ATTR_ICE_UFRAG));
101 | video->add(new sdp::Attribute("ice-pwd", ice_pwd, sdp::SDP_ATTR_ICE_PWD));
102 | video->add(new sdp::Attribute("candidate", "4252876256 1 udp 2122260223 192.168.0.194 59976 typ host"));
103 | video->add(new sdp::AttributeFingerprint("sha-256", fingerprint));
104 | video->add(new sdp::AttributeSetup(sdp::SDP_PASSIVE));
105 | }
106 |
107 | if (offer.find(sdp::SDP_AUDIO, &audio)) {
108 | audio->remove(sdp::SDP_ATTR_CANDIDATE);
109 | audio->remove(sdp::SDP_ATTR_ICE_UFRAG);
110 | audio->remove(sdp::SDP_ATTR_ICE_PWD);
111 | audio->remove(sdp::SDP_ATTR_ICE_OPTIONS);
112 | audio->remove(sdp::SDP_ATTR_FINGERPRINT);
113 | audio->remove(sdp::SDP_ATTR_SETUP);
114 | audio->add(new sdp::Attribute("ice-ufrag", ice_ufrag, sdp::SDP_ATTR_ICE_UFRAG));
115 | audio->add(new sdp::Attribute("ice-pwd", ice_pwd, sdp::SDP_ATTR_ICE_PWD));
116 | audio->add(new sdp::Attribute("candidate", "4252876256 1 udp 2122260223 192.168.0.194 59976 typ host"));
117 | audio->add(new sdp::AttributeFingerprint("sha-256", fingerprint));
118 | audio->add(new sdp::AttributeSetup(sdp::SDP_PASSIVE));
119 | }
120 | }
121 |
122 | /* and reconstruct the answer */
123 | {
124 | output_sdp = writer.toString(&offer);
125 | printf("\n\n%s\n\n", output_sdp.c_str());
126 | }
127 |
128 | if (!sock.bind("192.168.0.194", 59976)) {
129 | exit(1);
130 | }
131 |
132 | sock.on_data = on_udp_data;
133 | sock.user = (void*)&stun;
134 |
135 | dtls_parser.on_data = on_dtls_data;
136 | dtls_parser.user = (void*)&dtls_parser;
137 |
138 | /* start receiving data */
139 | while (true) {
140 | sock.update();
141 | }
142 | return 0;
143 | }
144 |
145 | static void on_udp_data(std::string rip, uint16_t rport,
146 | std::string lip, uint16_t lport,
147 | uint8_t* data, uint32_t nbytes, void* user)
148 | {
149 | stun::Message msg;
150 | stun::Reader* stun = static_cast(user);
151 | int r = stun->process(data, nbytes, &msg);
152 |
153 | if (r == 0) {
154 | /* valid stun message */
155 | msg.computeMessageIntegrity(PASSWORD);
156 | }
157 | else if (r == 1) {
158 | /* other data, e.g. DTLS ClientHello or SRTP data */
159 | if (dtls_parser_ptr) {
160 | dtls_parser_ptr->process(data, nbytes);
161 | }
162 | }
163 | else {
164 | printf("Error: unhandled stun::Reader::process() result.\n");
165 | exit(1);
166 | }
167 | }
168 |
169 | static void on_dtls_data(uint8_t* data, uint32_t nbytes, void* user) {
170 | /* handle dtls data, e.g. send back to sock. */
171 | }
172 |
--------------------------------------------------------------------------------
/src/test_webrtc_ice_agent.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #define USE_SEND 1 /* Enable sending of VP8 data */
13 | #define USE_RECORDING 0 /* Records received video into .ivf file that you can concert to webm using e.g. avconv */
14 | #define RECORDING_MAX_FRAMES 4000 /* Once we've recorded this amount of frames we exit the application */
15 |
16 | video::AggregatorVP8* aggregator;
17 | ice::Agent* agent;
18 | ice::Stream* video_stream;
19 | video::WriterIVF* ivf;
20 | sig::Signaling sigserv;
21 |
22 | #if USE_SEND
23 | extern "C" {
24 | # include
25 | }
26 | # include
27 | # include
28 | # include
29 | # define WIDTH 320
30 | # define HEIGHT 240
31 | # define FRAMERATE 25
32 | # define RTP_PACKET_SIZE (1000)
33 |
34 | video::EncoderSettings settings;
35 | video::EncoderVP8 encoder;
36 | rtp::WriterVP8 rtp_writer;
37 |
38 | static void on_vp8_packet(video::EncoderVP8* enc, const vpx_codec_cx_pkt* pkt, int64_t pts);
39 | static void on_rtp_packet(rtp::PacketVP8* pkt, void* user);
40 |
41 | #endif
42 |
43 | static void on_rtp_data(ice::Stream* stream, ice::CandidatePair* pair, uint8_t* data, uint32_t nbytes, void* user);
44 |
45 | int main() {
46 |
47 | printf("\n\ntest_ice_agent\n\n");
48 |
49 | std::vector interfaces = ice::get_interface_addresses();
50 |
51 | /* create our stream with the candidates */
52 | aggregator = new video::AggregatorVP8();
53 | ivf = new video::WriterIVF();
54 | agent = new ice::Agent();
55 | video_stream = new ice::Stream(STREAM_FLAG_VP8 | STREAM_FLAG_RTCP_MUX | STREAM_FLAG_SENDRECV);
56 |
57 | video_stream->on_rtp = on_rtp_data;
58 | video_stream->user_rtp = aggregator;
59 | video_stream->addLocalCandidate(new ice::Candidate("127.0.0.1", 59976));
60 |
61 | /* add the stream to the agent */
62 | agent->addStream(video_stream);
63 |
64 | /* set the ice-pwd that is used in the MessageIntegrity attribute of the STUN messages */
65 | agent->setCredentials("5PN2qmWqBl", "Q9wQj99nsQzldVI5ZuGXbEWRK5RhRXdC");
66 |
67 | if (ivf->open("test.ivf", 320, 240, 1, 25) < 0) {
68 | printf("Error: cannot open output ivf file.\n");
69 | exit(1);
70 | }
71 |
72 | /* initialize the agent (e.g. sets up the sockets) */
73 | if (!agent->init()) {
74 | exit(1);
75 | }
76 |
77 | /* setup signaling server */
78 | /* -------------------------------------------------- */
79 |
80 | sig::SignalingSettings sigserv_settings;
81 | sigserv_settings.port = "9001";
82 |
83 | if (0 != sigserv.init(sigserv_settings)) {
84 | printf("main - error: cannot init signaling server.\n");
85 | exit(1);
86 | }
87 |
88 | std::string sdp = agent->getSDP();
89 | if (0 != sigserv.addRoom(new sig::Room("party", sdp))) {
90 | printf("main - error: cannot add the room.\n");
91 | exit(1);
92 | }
93 | if (0 != sigserv.start()) {
94 | printf("main - error: cannot star signaling server.\n");
95 | exit(1);
96 | }
97 | /* -------------------------------------------------- */
98 |
99 | #if USE_SEND
100 |
101 | /* Setup our video encoder that uses libvideogenerator to stream some test video */
102 | settings.width = WIDTH;
103 | settings.height = HEIGHT;
104 | settings.fps_num = 1;
105 | settings.fps_den = FRAMERATE;
106 |
107 | /* initialize the encoder. */
108 | if (encoder.init(settings) < 0) {
109 | printf("main - error: cannot initialize the vp8 encoder.\n");
110 | exit(1);
111 | }
112 |
113 | encoder.on_packet = on_vp8_packet;
114 | rtp_writer.on_packet = on_rtp_packet;
115 |
116 | /* initialize the video generator. */
117 | video_generator gen;
118 | if (video_generator_init(&gen, WIDTH, HEIGHT, FRAMERATE) < 0) {
119 | printf("main - error: cannot initialize the video generator.\n");
120 | exit(1);
121 | }
122 |
123 | uint64_t pts;
124 | uint64_t now = uv_hrtime();
125 | uint64_t time_started = now;
126 | uint64_t frame_delay = (1.0 / FRAMERATE) * 1000llu * 1000llu * 1000llu;
127 | uint64_t frame_timeout = now + frame_delay;
128 |
129 | #endif /* USE_SEND */
130 |
131 | while (true) {
132 | agent->update();
133 |
134 | #if USE_SEND
135 | now = uv_hrtime();
136 | if (now > frame_timeout) {
137 | pts = (uv_hrtime() - time_started) / (1000llu * 1000llu);
138 | frame_timeout = now + frame_delay;
139 | video_generator_update(&gen);
140 | encoder.encode(gen.y, gen.strides[0],
141 | gen.u, gen.strides[1],
142 | gen.v, gen.strides[2],
143 | pts);
144 | }
145 | #endif /* USE_SEND */
146 | }
147 |
148 | return 0;
149 | }
150 |
151 |
152 | static void on_rtp_data(ice::Stream* stream,
153 | ice::CandidatePair* pair,
154 | uint8_t* data, uint32_t nbytes, void* user)
155 | {
156 |
157 | printf("on_rtp_data - vebose: received RTP data, %u bytes.\n", nbytes);
158 |
159 | video::AggregatorVP8* agg = static_cast(user);
160 |
161 | rtp::PacketVP8 pkt;
162 | if (rtp::rtp_vp8_decode(data, nbytes, &pkt) < 0) {
163 | printf("on_rtp_data - error: cannot decode vp8 rtp data.\n");
164 | return;
165 | }
166 |
167 | #if USE_RECORDING
168 | int r;
169 | if (agg->addPacket(&pkt) == AGGREGATOR_VP8_GOT_FRAME) {
170 | r = ivf->write(agg->buffer, agg->nbytes, ivf->nframes);
171 | if (ivf->nframes >= RECORDING_MAX_FRAMES) {
172 | ivf->close();
173 | printf("on_rtp_data - ready storing frames.\n");
174 | exit(1);
175 | }
176 | if (r < 0) {
177 | printf("on_rtp_data - error: cannot write frame: %d\n", r);
178 | exit(1);
179 | }
180 | }
181 | #endif
182 |
183 | }
184 |
185 | #if USE_SEND
186 | static void on_vp8_packet(video::EncoderVP8* enc, const vpx_codec_cx_pkt* pkt, int64_t pts) {
187 | printf("on_vp8_packet - verbose: got vp8 packet: %lld\n", pts);
188 | rtp_writer.packetize(pkt);
189 | }
190 |
191 | static void on_rtp_packet(rtp::PacketVP8* pkt, void* user) {
192 | if (!pkt) { return; }
193 | video_stream->sendRTP(pkt->payload, pkt->nbytes);
194 | }
195 | #endif
196 |
--------------------------------------------------------------------------------
/src/test_webrtc_libwebsockets.cpp:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | test_libwebsockets
4 | ------------------
5 |
6 | Unfinished test with libwebsockets; at some point libwebrtc may implement a simple
7 | signaling server to add support for easy interop with a web app.
8 |
9 | */
10 | #include