├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── alphartc_gym ├── __init__.py ├── gym.py ├── gym_connect.py ├── gym_process.py ├── tests │ ├── __init__.py │ ├── data │ │ ├── 4G_3mbps.json │ │ ├── 4G_500kbps.json │ │ ├── 4G_700kbps.json │ │ ├── 5G_12mbps.json │ │ ├── 5G_13mbps.json │ │ ├── WIRED_200kbps.json │ │ ├── WIRED_35mbps.json │ │ ├── WIRED_900kbs.json │ │ ├── loss │ │ │ ├── trace_loss_0.json │ │ │ ├── trace_loss_0dot1.json │ │ │ ├── trace_loss_0dot5.json │ │ │ ├── trace_loss_pattern_2.json │ │ │ ├── trace_loss_pattern_3.json │ │ │ └── trace_loss_pattern_4.json │ │ ├── rtt │ │ │ ├── trace_rtt_200.json │ │ │ ├── trace_rtt_400.json │ │ │ ├── trace_rtt_600.json │ │ │ ├── trace_rtt_pattern_2.json │ │ │ ├── trace_rtt_pattern_3.json │ │ │ └── trace_rtt_pattern_4.json │ │ ├── trace_300k.json │ │ └── trace_example.json │ ├── test_gym.py │ ├── test_gym_connect.py │ ├── test_gym_loss.py │ ├── test_gym_process.py │ ├── test_gym_rtt.py │ └── test_gym_stability.py └── utils │ ├── __init__.py │ ├── packet_info.py │ └── packet_record.py ├── azure-pipelines.yml ├── dockers └── Dockerfile.compile ├── ns-app ├── scratch │ └── webrtc_test │ │ ├── gym_connector.cc │ │ ├── gym_connector.h │ │ ├── network_controller_proxy.cc │ │ ├── network_controller_proxy.h │ │ ├── network_controller_proxy_factory.cc │ │ ├── network_controller_proxy_factory.h │ │ ├── network_estimator_proxy.cc │ │ ├── network_estimator_proxy.h │ │ ├── network_estimator_proxy_factory.cc │ │ ├── network_estimator_proxy_factory.h │ │ ├── trace_player.cc │ │ ├── trace_player.h │ │ └── webrtc-static.cc └── src │ └── ex-webrtc │ ├── model │ ├── byte-order.h │ ├── simulated-process-thread.cc │ ├── simulated-process-thread.h │ ├── simulated-task-queue.cc │ ├── simulated-task-queue.h │ ├── simulated-thread.cc │ ├── simulated-thread.h │ ├── webrtc-clock.cc │ ├── webrtc-clock.h │ ├── webrtc-config.cc │ ├── webrtc-config.h │ ├── webrtc-defines.h │ ├── webrtc-emu-controller.cc │ ├── webrtc-emu-controller.h │ ├── webrtc-receiver.cc │ ├── webrtc-receiver.h │ ├── webrtc-sender.cc │ ├── webrtc-sender.h │ ├── webrtc-simu-controller.cc │ └── webrtc-simu-controller.h │ └── wscript └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | target/* 3 | traces/* 4 | **/__pycache__/* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "AlphaRTC"] 2 | path = AlphaRTC 3 | url = https://github.com/OpenNetLab/AlphaRTC.git 4 | branch = gym 5 | [submodule "ns-3-dev"] 6 | path = ns-3-dev 7 | url = https://gitlab.com/nsnam/ns-3-dev.git 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build_profile := release 2 | 3 | dockers_dir := dockers 4 | build_dockerfile := $(dockers_dir)/Dockerfile.compile 5 | 6 | compile_docker := gym-compile 7 | 8 | work_dir := `pwd` 9 | ns_dir := $(work_dir)/ns-3-dev 10 | ns_app := $(work_dir)/ns-app 11 | scratch_webrtc_dir := $(ns_app)/scratch/webrtc_test 12 | ex_webrtc_dir := $(ns_app)/src/ex-webrtc 13 | ex_webrtc_model_dir := $(ex_webrtc_dir)/model 14 | ex_webrtc_wscript_path := $(ex_webrtc_dir)/wscript 15 | alphartc_dir := $(work_dir)/AlphaRTC 16 | alphartc_test_dir := $(alphartc_dir)/test 17 | alphartc_api_dir := $(alphartc_dir)/api 18 | alphartc_modules_dir := $(alphartc_dir)/modules 19 | alphartc_rtc_base_dir := $(alphartc_dir)/rtc_base 20 | target_dir := $(work_dir)/target 21 | 22 | docker_user := onl 23 | docker_work_dir := /home/$(docker_user) 24 | docker_alphartc_dir := $(docker_work_dir)/alphartc 25 | docker_ns_dir := $(docker_work_dir)/ns 26 | docker_ns_scratch_webrtc_dir := $(docker_ns_dir)/scratch/webrtc_test 27 | docker_ex_webrtc_dir := $(docker_ns_dir)/src/ex-webrtc 28 | docker_ex_webrtc_model_dir := $(docker_ex_webrtc_dir)/model 29 | docker_ex_webrtc_wscript_path := $(docker_ex_webrtc_dir)/wscript 30 | docker_ex_webrtc_test_dir := $(docker_ex_webrtc_dir)/test 31 | docker_ex_webrtc_api_dir := $(docker_ex_webrtc_dir)/api 32 | docker_ex_webrtc_modules_dir := $(docker_ex_webrtc_dir)/modules 33 | docker_ex_webrtc_rtc_base_dir := $(docker_ex_webrtc_dir)/rtc_base 34 | docker_target_dir := $(docker_ns_dir)/target 35 | docker_ns_build_dir := $(docker_ns_dir)/build/scratch/webrtc_test 36 | 37 | docker_flags := --rm \ 38 | -v $(ns_dir):$(docker_ns_dir) \ 39 | -v $(alphartc_dir):$(docker_alphartc_dir) \ 40 | -v $(scratch_webrtc_dir):$(docker_ns_scratch_webrtc_dir) \ 41 | -v $(ex_webrtc_model_dir):$(docker_ex_webrtc_model_dir) \ 42 | -v $(ex_webrtc_wscript_path):$(docker_ex_webrtc_wscript_path) \ 43 | -v $(alphartc_test_dir):$(docker_ex_webrtc_test_dir) \ 44 | -v $(alphartc_api_dir):$(docker_ex_webrtc_api_dir) \ 45 | -v $(alphartc_modules_dir):$(docker_ex_webrtc_modules_dir) \ 46 | -v $(alphartc_rtc_base_dir):$(docker_ex_webrtc_rtc_base_dir) \ 47 | -w $(docker_ns_dir) \ 48 | -e ALPHARTC_DIR=$(docker_alphartc_dir) 49 | 50 | all: 51 | make init 52 | make sync 53 | make gym 54 | 55 | init: 56 | docker build dockers --build-arg UID=$(shell id -u) --build-arg GUID=$(shell id -g) -f $(build_dockerfile) -t $(compile_docker) 57 | git submodule init 58 | git submodule update 59 | 60 | sync: 61 | make -C $(alphartc_dir) init 62 | make -C $(alphartc_dir) sync host_workdir=$(work_dir) docker_homedir=/app docker_workdir=/app/AlphaRTC 63 | make -C $(alphartc_dir) lib 64 | 65 | gym: 66 | mkdir -p $(target_dir) 67 | docker run $(docker_flags) \ 68 | -v $(target_dir):$(docker_target_dir) \ 69 | $(compile_docker) \ 70 | bash -c \ 71 | " \ 72 | $(docker_ns_dir)/waf configure --enable-static \ 73 | --check-cxx-compiler=clang++ --check-c-compiler=clang \ 74 | --build-profile=$(build_profile); \ 75 | $(docker_ns_dir)/waf build; \ 76 | cp $(docker_ns_dir)/build/scratch/webrtc_test/webrtc_test $(docker_target_dir)/gym \ 77 | " 78 | 79 | login: 80 | docker run $(docker_flags) --ulimit core=-1 --security-opt seccomp=unconfined --privileged -ti \ 81 | -v $(target_dir):$(docker_target_dir) \ 82 | $(compile_docker) \ 83 | bash 84 | 85 | clean: 86 | docker run $(docker_flags) $(compile_docker) \ 87 | bash -c \ 88 | " \ 89 | rm -f $(docker_ns_build_dir)/* \ 90 | " 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gym 2 | 3 | [![Build Status](https://dev.azure.com/OpenNetLab/ONL-github/_apis/build/status/OpenNetLab.gym?branchName=master)](https://dev.azure.com/OpenNetLab/ONL-github/_build/latest?definitionId=6&branchName=master) 4 | 5 | This gym leverages NS3 and WebRTC, which can be used by reinforcement learning or other methods to build a Bandwidth Controller for WebRTC. 6 | 7 | ### Usage 8 | 9 | You can use this Gym by a Python interface that was defined in [gym.py](alphartc_gym/gym.py). Here is an example [gym-example](https://github.com/OpenNetLab/gym-example) to use this Gym training a bandwidth estimator. 10 | 11 | ### Setup Guide 12 | 13 | #### Get Gym 14 | 15 | ```sh 16 | git clone https://github.com/OpenNetLab/gym gym 17 | cd gym 18 | ``` 19 | 20 | #### Install dependencies(Ubuntu 18.04 or Ubuntu 20.04) 21 | 22 | ```sh 23 | sudo apt install libzmq5 python3 python3-pip 24 | python3 -m pip install -r requirements.txt 25 | # Install Docker 26 | curl -fsSL get.docker.com -o get-docker.sh 27 | sudo sh get-docker.sh 28 | sudo usermod -aG docker ${USER} 29 | ``` 30 | 31 | #### Download pre-compiled binary 32 | 33 | If your OS is ubuntu18.04 or ubuntu20.04, we recommend you directly downloading pre-compiled binary, and please skip the step [Build Gym binary](#Build-Gym-binary) 34 | 35 | The pre-compiled binary can be found from the latest [GithubRelease](https://github.com/OpenNetLab/gym/releases/latest/download/target.tar.gz). Please download and uncompress it in the current folder. 36 | ``` 37 | wget https://github.com/OpenNetLab/gym/releases/latest/download/target.tar.gz 38 | tar -xvzf target.tar.gz 39 | ``` 40 | 41 | #### Build Gym binary 42 | 43 | ```sh 44 | make init 45 | make sync 46 | make gym # build_profile=debug 47 | ``` 48 | 49 | If you want to build the debug version, try `make gym build_profile=debug` 50 | 51 | #### Verify gym 52 | 53 | ```sh 54 | python3 -m pytest alphartc_gym 55 | ``` 56 | 57 | ### Inspiration 58 | 59 | Thanks [SoonyangZhang](https://github.com/SoonyangZhang) provides the inspiration for the gym 60 | 61 | -------------------------------------------------------------------------------- /alphartc_gym/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from .gym import Gym 5 | -------------------------------------------------------------------------------- /alphartc_gym/gym.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from alphartc_gym import gym_process 5 | from alphartc_gym import gym_connect 6 | 7 | import os 8 | import uuid 9 | 10 | class Gym(object): 11 | def __init__(self, gym_id: str = None): 12 | if gym_id == None: 13 | gym_id = str(uuid.uuid4().hex) 14 | self.gym_id = gym_id 15 | self.gym_conn = None 16 | self.gym_process = None 17 | 18 | def reset( 19 | self, 20 | trace_path: str = "", 21 | report_interval_ms: int = 60, 22 | duration_time_ms: int = 3000): 23 | """ Reset gym environment, this function will destroy the old context 24 | and create a new one for next step. 25 | 26 | Parameters 27 | trace_path: a path to indicate a trace file with json format. 28 | The following is an exmaple of trace file 29 | { 30 | "type": "video", 31 | "downlink": {}, 32 | "uplink": { 33 | "trace_pattern": [ 34 | { 35 | "duration": 60000, # duration time 36 | "capacity": 300, # link bandwidth (kbps) 37 | "loss": 0.1, # loss rate (0.0~0.1) 38 | "rtt" : 85, # round trip delay (ms) 39 | "jitter": 0, # not supported in current version 40 | }, 41 | ... 42 | ] 43 | } 44 | } 45 | report_interfal_ms: to indicate the report interval. It indicates 46 | the a step's duration, the unit is Millisecond. 47 | duration_time_ms: to indicate the duration for an epoch. 48 | If this value is None or 0, the duration will be caculated 49 | according to the trace file. 50 | """ 51 | if self.gym_conn: 52 | del self.gym_conn 53 | if self.gym_process: 54 | del self.gym_process 55 | self.gym_process = gym_process.GymProcess( 56 | self.gym_id, 57 | trace_path, 58 | report_interval_ms, 59 | duration_time_ms) 60 | self.gym_conn = gym_connect.GymConnector(self.gym_id) 61 | 62 | def step(self, bandwidth_bps: int): 63 | """ Set a estimated bandwidth for Gym 64 | 65 | Parameters 66 | bandwidth_bps: the estimated bandwidth, the unit is bps 67 | 68 | Return value: a pair of stats list and done 69 | stats list: it's a list of packet stat, each stat is a python 70 | dict. The stats could be empty list. 71 | The following is an example for the stats 72 | [ 73 | { 74 | 'arrival_time_ms': 66113, 75 | 'header_length': 24, 76 | 'padding_length': 0, 77 | 'payload_size': 1389, 78 | 'payload_type': 126, 79 | 'send_time_ms': 60999, 80 | 'sequence_number': 54366, 81 | 'ssrc': 12648429 82 | }, 83 | { 84 | 'arrival_time_ms': 66181, 85 | 'header_length': 24, 86 | 'padding_length': 0, 87 | 'payload_size': 1389, 88 | 'payload_type': 126, 89 | 'send_time_ms': 61069, 90 | 'sequence_number': 54411, 91 | 'ssrc': 12648429} 92 | ] 93 | done: A flag to indicate whether this epoch is finished. 94 | True means this epoch is done. 95 | """ 96 | stats = self.gym_conn.step(bandwidth_bps) 97 | if stats != None: 98 | return stats, False 99 | return [], True 100 | 101 | def __del__(self): 102 | if os.path.exists(gym_connect.__ZMQ_PATH__ + self.gym_id): 103 | os.remove(gym_connect.__ZMQ_PATH__ + self.gym_id) 104 | -------------------------------------------------------------------------------- /alphartc_gym/gym_connect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import zmq 5 | import json 6 | 7 | __ZMQ_TYPE__ = "ipc://" 8 | __ZMQ_PATH__ = "/tmp/" 9 | __ZMQ_PREFIX__ = __ZMQ_TYPE__ + __ZMQ_PATH__ 10 | __GYM_EXIT_FLAG__ = b"Bye" 11 | 12 | class GymConnector(object): 13 | def __init__(self, gym_id = "gym"): 14 | self.gym_id = gym_id 15 | self.zmq_ctx = zmq.Context() 16 | self.zmq_sock = self.zmq_ctx.socket(zmq.REQ) 17 | self.zmq_sock.connect(__ZMQ_PREFIX__ + self.gym_id) 18 | 19 | def step(self, bandwidth_bps = int): 20 | self.zmq_sock.send_string(str(int(bandwidth_bps))) 21 | rep = self.zmq_sock.recv() 22 | if rep == __GYM_EXIT_FLAG__: 23 | return None 24 | return json.loads(rep) 25 | 26 | def __del__(self): 27 | self.zmq_sock.disconnect(__ZMQ_PREFIX__ + self.gym_id) 28 | -------------------------------------------------------------------------------- /alphartc_gym/gym_process.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import subprocess 5 | import os 6 | import signal 7 | 8 | __ROOT_PATH__ = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 9 | __GYM_PROCESS_PATH__ = os.path.join(__ROOT_PATH__, "target", "gym") 10 | 11 | 12 | class GymProcess(object): 13 | def __init__( 14 | self, 15 | gym_id: str = "gym", 16 | trace_path: str = "", 17 | report_interval_ms: int = 60, 18 | duration_time_ms: int = 3000): 19 | process_args = [__GYM_PROCESS_PATH__, "--standalone_test_only=false"] 20 | process_args.append("--gym_id="+str(gym_id)) 21 | if trace_path: 22 | process_args.append("--trace_path="+trace_path) 23 | if report_interval_ms: 24 | process_args.append("--report_interval_ms="+str(report_interval_ms)) 25 | if duration_time_ms: 26 | process_args.append("--duration_time_ms="+str(duration_time_ms)) 27 | self.gym = subprocess.Popen(process_args) 28 | 29 | def wait(self, timeout = None): 30 | return self.gym.wait(timeout) 31 | 32 | def __del__(self): 33 | self.gym.send_signal(signal.SIGINT) 34 | self.gym.send_signal(signal.SIGKILL) 35 | -------------------------------------------------------------------------------- /alphartc_gym/tests/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | -------------------------------------------------------------------------------- /alphartc_gym/tests/data/4G_3mbps.json: -------------------------------------------------------------------------------- 1 | { 2 | "uplink": { 3 | "trace_pattern": [ 4 | { 5 | "capacity": 0, 6 | "duration": 200 7 | }, 8 | { 9 | "capacity": 1720, 10 | "duration": 200 11 | }, 12 | { 13 | "capacity": 1240, 14 | "duration": 199 15 | }, 16 | { 17 | "capacity": 2200, 18 | "duration": 200 19 | }, 20 | { 21 | "capacity": 3, 22 | "duration": 199 23 | }, 24 | { 25 | "capacity": 2080, 26 | "duration": 199 27 | }, 28 | { 29 | "capacity": 3040, 30 | "duration": 199 31 | }, 32 | { 33 | "capacity": 2640, 34 | "duration": 200 35 | }, 36 | { 37 | "capacity": 1920, 38 | "duration": 199 39 | }, 40 | { 41 | "capacity": 0, 42 | "duration": 199 43 | }, 44 | { 45 | "capacity": 0, 46 | "duration": 200 47 | }, 48 | { 49 | "capacity": 0, 50 | "duration": 199 51 | }, 52 | { 53 | "capacity": 0, 54 | "duration": 200 55 | }, 56 | { 57 | "capacity": 80100, 58 | "duration": 199 59 | }, 60 | { 61 | "capacity": 4600, 62 | "duration": 200 63 | }, 64 | { 65 | "capacity": 560, 66 | "duration": 200 67 | }, 68 | { 69 | "capacity": 0, 70 | "duration": 199 71 | }, 72 | { 73 | "capacity": 3570, 74 | "duration": 200 75 | }, 76 | { 77 | "capacity": 1160, 78 | "duration": 199 79 | }, 80 | { 81 | "capacity": 160, 82 | "duration": 200 83 | }, 84 | { 85 | "capacity": 3600, 86 | "duration": 200 87 | }, 88 | { 89 | "capacity": 8039999, 90 | "duration": 200 91 | }, 92 | { 93 | "capacity": 4880, 94 | "duration": 199 95 | }, 96 | { 97 | "capacity": 4400, 98 | "duration": 200 99 | }, 100 | { 101 | "capacity": 2560, 102 | "duration": 200 103 | }, 104 | { 105 | "capacity": 3440, 106 | "duration": 200 107 | }, 108 | { 109 | "capacity": 3840, 110 | "duration": 200 111 | }, 112 | { 113 | "capacity": 2680, 114 | "duration": 199 115 | }, 116 | { 117 | "capacity": 0, 118 | "duration": 200 119 | }, 120 | { 121 | "capacity": 440, 122 | "duration": 200 123 | }, 124 | { 125 | "capacity": 5360, 126 | "duration": 200 127 | }, 128 | { 129 | "capacity": 4360, 130 | "duration": 200 131 | }, 132 | { 133 | "capacity": 5920, 134 | "duration": 199 135 | }, 136 | { 137 | "capacity": 4480, 138 | "duration": 200 139 | }, 140 | { 141 | "capacity": 1560, 142 | "duration": 200 143 | }, 144 | { 145 | "capacity": 0, 146 | "duration": 200 147 | }, 148 | { 149 | "capacity": 2400, 150 | "duration": 200 151 | }, 152 | { 153 | "capacity": 2600, 154 | "duration": 199 155 | }, 156 | { 157 | "capacity": 4440, 158 | "duration": 200 159 | }, 160 | { 161 | "capacity": 3760, 162 | "duration": 200 163 | }, 164 | { 165 | "capacity": 3320, 166 | "duration": 199 167 | }, 168 | { 169 | "capacity": 3120, 170 | "duration": 200 171 | }, 172 | { 173 | "capacity": 3800, 174 | "duration": 199 175 | }, 176 | { 177 | "capacity": 3440, 178 | "duration": 200 179 | }, 180 | { 181 | "capacity": 2800, 182 | "duration": 199 183 | }, 184 | { 185 | "capacity": 4120, 186 | "duration": 199 187 | }, 188 | { 189 | "capacity": 4120, 190 | "duration": 200 191 | }, 192 | { 193 | "capacity": 2480, 194 | "duration": 199 195 | }, 196 | { 197 | "capacity": 4160, 198 | "duration": 200 199 | }, 200 | { 201 | "capacity": 3760, 202 | "duration": 199 203 | }, 204 | { 205 | "capacity": 2280, 206 | "duration": 199 207 | }, 208 | { 209 | "capacity": 4520, 210 | "duration": 200 211 | }, 212 | { 213 | "capacity": 4440, 214 | "duration": 199 215 | }, 216 | { 217 | "capacity": 3160, 218 | "duration": 200 219 | }, 220 | { 221 | "capacity": 0, 222 | "duration": 199 223 | }, 224 | { 225 | "capacity": 2200, 226 | "duration": 199 227 | }, 228 | { 229 | "capacity": 3640, 230 | "duration": 200 231 | }, 232 | { 233 | "capacity": 3640, 234 | "duration": 199 235 | }, 236 | { 237 | "capacity": 5890, 238 | "duration": 200 239 | }, 240 | { 241 | "capacity": 6070, 242 | "duration": 199 243 | }, 244 | { 245 | "capacity": 0, 246 | "duration": 199 247 | }, 248 | { 249 | "capacity": 0, 250 | "duration": 200 251 | }, 252 | { 253 | "capacity": 4130, 254 | "duration": 199 255 | }, 256 | { 257 | "capacity": 999, 258 | "duration": 200 259 | }, 260 | { 261 | "capacity": 0, 262 | "duration": 199 263 | }, 264 | { 265 | "capacity": 4080, 266 | "duration": 199 267 | }, 268 | { 269 | "capacity": 3200, 270 | "duration": 200 271 | }, 272 | { 273 | "capacity": 5400, 274 | "duration": 199 275 | }, 276 | { 277 | "capacity": 5400, 278 | "duration": 200 279 | }, 280 | { 281 | "capacity": 3280, 282 | "duration": 199 283 | }, 284 | { 285 | "capacity": 5440, 286 | "duration": 199 287 | }, 288 | { 289 | "capacity": 5870, 290 | "duration": 200 291 | }, 292 | { 293 | "capacity": 5, 294 | "duration": 199 295 | }, 296 | { 297 | "capacity": 7050, 298 | "duration": 200 299 | }, 300 | { 301 | "capacity": 6320, 302 | "duration": 199 303 | }, 304 | { 305 | "capacity": 5200, 306 | "duration": 199 307 | }, 308 | { 309 | "capacity": 4840, 310 | "duration": 200 311 | }, 312 | { 313 | "capacity": 6880, 314 | "duration": 199 315 | }, 316 | { 317 | "capacity": 5880, 318 | "duration": 200 319 | }, 320 | { 321 | "capacity": 3440, 322 | "duration": 199 323 | }, 324 | { 325 | "capacity": 4960, 326 | "duration": 199 327 | }, 328 | { 329 | "capacity": 5280, 330 | "duration": 199 331 | }, 332 | { 333 | "capacity": 2640, 334 | "duration": 200 335 | }, 336 | { 337 | "capacity": 5200, 338 | "duration": 199 339 | }, 340 | { 341 | "capacity": 4760, 342 | "duration": 199 343 | }, 344 | { 345 | "capacity": 2240, 346 | "duration": 199 347 | }, 348 | { 349 | "capacity": 0, 350 | "duration": 199 351 | }, 352 | { 353 | "capacity": 801, 354 | "duration": 200 355 | }, 356 | { 357 | "capacity": 3400, 358 | "duration": 199 359 | }, 360 | { 361 | "capacity": 3200, 362 | "duration": 199 363 | }, 364 | { 365 | "capacity": 4640, 366 | "duration": 199 367 | }, 368 | { 369 | "capacity": 4080, 370 | "duration": 199 371 | }, 372 | { 373 | "capacity": 2080, 374 | "duration": 200 375 | }, 376 | { 377 | "capacity": 3400, 378 | "duration": 199 379 | }, 380 | { 381 | "capacity": 3680, 382 | "duration": 199 383 | }, 384 | { 385 | "capacity": 520, 386 | "duration": 199 387 | }, 388 | { 389 | "capacity": 0, 390 | "duration": 199 391 | }, 392 | { 393 | "capacity": 0, 394 | "duration": 200 395 | }, 396 | { 397 | "capacity": 0, 398 | "duration": 199 399 | }, 400 | { 401 | "capacity": 2280, 402 | "duration": 199 403 | }, 404 | { 405 | "capacity": 2240, 406 | "duration": 199 407 | }, 408 | { 409 | "capacity": 1160, 410 | "duration": 199 411 | }, 412 | { 413 | "capacity": 0, 414 | "duration": 200 415 | }, 416 | { 417 | "capacity": 1800, 418 | "duration": 199 419 | }, 420 | { 421 | "capacity": 7590, 422 | "duration": 199 423 | }, 424 | { 425 | "capacity": 11700, 426 | "duration": 199 427 | }, 428 | { 429 | "capacity": 3320, 430 | "duration": 199 431 | }, 432 | { 433 | "capacity": 1640, 434 | "duration": 200 435 | }, 436 | { 437 | "capacity": 520, 438 | "duration": 199 439 | }, 440 | { 441 | "capacity": 1360, 442 | "duration": 199 443 | }, 444 | { 445 | "capacity": 2, 446 | "duration": 199 447 | }, 448 | { 449 | "capacity": 320, 450 | "duration": 199 451 | }, 452 | { 453 | "capacity": 0, 454 | "duration": 200 455 | }, 456 | { 457 | "capacity": 2760, 458 | "duration": 199 459 | }, 460 | { 461 | "capacity": 1520, 462 | "duration": 199 463 | }, 464 | { 465 | "capacity": 0, 466 | "duration": 199 467 | }, 468 | { 469 | "capacity": 800, 470 | "duration": 199 471 | }, 472 | { 473 | "capacity": 1320, 474 | "duration": 200 475 | }, 476 | { 477 | "capacity": 0, 478 | "duration": 199 479 | }, 480 | { 481 | "capacity": 0, 482 | "duration": 199 483 | }, 484 | { 485 | "capacity": 0, 486 | "duration": 199 487 | }, 488 | { 489 | "capacity": 0, 490 | "duration": 199 491 | }, 492 | { 493 | "capacity": 80100, 494 | "duration": 200 495 | }, 496 | { 497 | "capacity": 4160, 498 | "duration": 199 499 | }, 500 | { 501 | "capacity": 0, 502 | "duration": 199 503 | }, 504 | { 505 | "capacity": 80100, 506 | "duration": 199 507 | }, 508 | { 509 | "capacity": 4520, 510 | "duration": 199 511 | }, 512 | { 513 | "capacity": 679, 514 | "duration": 200 515 | }, 516 | { 517 | "capacity": 0, 518 | "duration": 199 519 | }, 520 | { 521 | "capacity": 4570, 522 | "duration": 199 523 | }, 524 | { 525 | "capacity": 2360, 526 | "duration": 199 527 | }, 528 | { 529 | "capacity": 0, 530 | "duration": 199 531 | }, 532 | { 533 | "capacity": 3200, 534 | "duration": 200 535 | }, 536 | { 537 | "capacity": 3080, 538 | "duration": 199 539 | }, 540 | { 541 | "capacity": 0, 542 | "duration": 199 543 | }, 544 | { 545 | "capacity": 1880, 546 | "duration": 199 547 | }, 548 | { 549 | "capacity": 3240, 550 | "duration": 199 551 | }, 552 | { 553 | "capacity": 0, 554 | "duration": 200 555 | }, 556 | { 557 | "capacity": 200, 558 | "duration": 199 559 | }, 560 | { 561 | "capacity": 6530, 562 | "duration": 199 563 | }, 564 | { 565 | "capacity": 0, 566 | "duration": 199 567 | }, 568 | { 569 | "capacity": 440, 570 | "duration": 199 571 | }, 572 | { 573 | "capacity": 7280, 574 | "duration": 200 575 | }, 576 | { 577 | "capacity": 4280, 578 | "duration": 199 579 | }, 580 | { 581 | "capacity": 6920, 582 | "duration": 199 583 | }, 584 | { 585 | "capacity": 7330, 586 | "duration": 199 587 | }, 588 | { 589 | "capacity": 1680, 590 | "duration": 199 591 | }, 592 | { 593 | "capacity": 0, 594 | "duration": 200 595 | }, 596 | { 597 | "capacity": 3440, 598 | "duration": 199 599 | }, 600 | { 601 | "capacity": 3040, 602 | "duration": 199 603 | }, 604 | { 605 | "capacity": 0, 606 | "duration": 199 607 | }, 608 | { 609 | "capacity": 1960, 610 | "duration": 199 611 | }, 612 | { 613 | "capacity": 3840, 614 | "duration": 200 615 | }, 616 | { 617 | "capacity": 5320, 618 | "duration": 199 619 | }, 620 | { 621 | "capacity": 6730, 622 | "duration": 199 623 | }, 624 | { 625 | "capacity": 4680, 626 | "duration": 199 627 | }, 628 | { 629 | "capacity": 0, 630 | "duration": 199 631 | }, 632 | { 633 | "capacity": 280, 634 | "duration": 200 635 | }, 636 | { 637 | "capacity": 6160, 638 | "duration": 199 639 | }, 640 | { 641 | "capacity": 2760, 642 | "duration": 199 643 | }, 644 | { 645 | "capacity": 5640, 646 | "duration": 200 647 | }, 648 | { 649 | "capacity": 4760, 650 | "duration": 199 651 | }, 652 | { 653 | "capacity": 1920, 654 | "duration": 200 655 | }, 656 | { 657 | "capacity": 0, 658 | "duration": 199 659 | }, 660 | { 661 | "capacity": 2920, 662 | "duration": 200 663 | }, 664 | { 665 | "capacity": 3840, 666 | "duration": 200 667 | }, 668 | { 669 | "capacity": 0, 670 | "duration": 199 671 | }, 672 | { 673 | "capacity": 2200, 674 | "duration": 200 675 | }, 676 | { 677 | "capacity": 4160, 678 | "duration": 199 679 | }, 680 | { 681 | "capacity": 0, 682 | "duration": 200 683 | }, 684 | { 685 | "capacity": 1600, 686 | "duration": 200 687 | }, 688 | { 689 | "capacity": 5280, 690 | "duration": 199 691 | }, 692 | { 693 | "capacity": 0, 694 | "duration": 200 695 | }, 696 | { 697 | "capacity": 401, 698 | "duration": 199 699 | }, 700 | { 701 | "capacity": 5390, 702 | "duration": 200 703 | }, 704 | { 705 | "capacity": 4930, 706 | "duration": 200 707 | }, 708 | { 709 | "capacity": 6390, 710 | "duration": 199 711 | }, 712 | { 713 | "capacity": 6200, 714 | "duration": 200 715 | }, 716 | { 717 | "capacity": 2600, 718 | "duration": 199 719 | }, 720 | { 721 | "capacity": 2600, 722 | "duration": 200 723 | }, 724 | { 725 | "capacity": 4290, 726 | "duration": 200 727 | }, 728 | { 729 | "capacity": 2600, 730 | "duration": 199 731 | }, 732 | { 733 | "capacity": 0, 734 | "duration": 200 735 | }, 736 | { 737 | "capacity": 1600, 738 | "duration": 199 739 | }, 740 | { 741 | "capacity": 3920, 742 | "duration": 200 743 | }, 744 | { 745 | "capacity": 0, 746 | "duration": 200 747 | }, 748 | { 749 | "capacity": 0, 750 | "duration": 199 751 | }, 752 | { 753 | "capacity": 6480, 754 | "duration": 200 755 | }, 756 | { 757 | "capacity": 0, 758 | "duration": 199 759 | }, 760 | { 761 | "capacity": 0, 762 | "duration": 200 763 | }, 764 | { 765 | "capacity": 4690, 766 | "duration": 200 767 | }, 768 | { 769 | "capacity": 1080, 770 | "duration": 199 771 | }, 772 | { 773 | "capacity": 0, 774 | "duration": 200 775 | }, 776 | { 777 | "capacity": 4400, 778 | "duration": 199 779 | }, 780 | { 781 | "capacity": 1800, 782 | "duration": 200 783 | }, 784 | { 785 | "capacity": 0, 786 | "duration": 200 787 | }, 788 | { 789 | "capacity": 2360, 790 | "duration": 199 791 | }, 792 | { 793 | "capacity": 1920, 794 | "duration": 200 795 | }, 796 | { 797 | "capacity": 0, 798 | "duration": 199 799 | }, 800 | { 801 | "capacity": 240, 802 | "duration": 200 803 | }, 804 | { 805 | "capacity": 4360, 806 | "duration": 200 807 | }, 808 | { 809 | "capacity": 0, 810 | "duration": 199 811 | }, 812 | { 813 | "capacity": 40, 814 | "duration": 200 815 | }, 816 | { 817 | "capacity": 4040, 818 | "duration": 199 819 | }, 820 | { 821 | "capacity": 2120, 822 | "duration": 200 823 | }, 824 | { 825 | "capacity": 3160, 826 | "duration": 200 827 | }, 828 | { 829 | "capacity": 5150, 830 | "duration": 199 831 | }, 832 | { 833 | "capacity": 760, 834 | "duration": 200 835 | }, 836 | { 837 | "capacity": 0, 838 | "duration": 199 839 | }, 840 | { 841 | "capacity": 3560, 842 | "duration": 200 843 | }, 844 | { 845 | "capacity": 1760, 846 | "duration": 200 847 | }, 848 | { 849 | "capacity": 0, 850 | "duration": 199 851 | }, 852 | { 853 | "capacity": 0, 854 | "duration": 200 855 | }, 856 | { 857 | "capacity": 120, 858 | "duration": 199 859 | }, 860 | { 861 | "capacity": 0, 862 | "duration": 200 863 | }, 864 | { 865 | "capacity": 1280, 866 | "duration": 200 867 | }, 868 | { 869 | "capacity": 3200, 870 | "duration": 199 871 | }, 872 | { 873 | "capacity": 0, 874 | "duration": 200 875 | }, 876 | { 877 | "capacity": 1520, 878 | "duration": 199 879 | }, 880 | { 881 | "capacity": 4320, 882 | "duration": 200 883 | }, 884 | { 885 | "capacity": 3680, 886 | "duration": 200 887 | }, 888 | { 889 | "capacity": 4600, 890 | "duration": 199 891 | }, 892 | { 893 | "capacity": 4320, 894 | "duration": 200 895 | }, 896 | { 897 | "capacity": 160, 898 | "duration": 199 899 | }, 900 | { 901 | "capacity": 0, 902 | "duration": 200 903 | }, 904 | { 905 | "capacity": 1560, 906 | "duration": 200 907 | }, 908 | { 909 | "capacity": 1080, 910 | "duration": 199 911 | }, 912 | { 913 | "capacity": 0, 914 | "duration": 200 915 | }, 916 | { 917 | "capacity": 3570, 918 | "duration": 199 919 | }, 920 | { 921 | "capacity": 2840, 922 | "duration": 200 923 | }, 924 | { 925 | "capacity": 6570, 926 | "duration": 200 927 | }, 928 | { 929 | "capacity": 6800, 930 | "duration": 199 931 | }, 932 | { 933 | "capacity": 5920, 934 | "duration": 200 935 | }, 936 | { 937 | "capacity": 0, 938 | "duration": 199 939 | }, 940 | { 941 | "capacity": 240, 942 | "duration": 200 943 | }, 944 | { 945 | "capacity": 6530, 946 | "duration": 200 947 | }, 948 | { 949 | "capacity": 0, 950 | "duration": 199 951 | }, 952 | { 953 | "capacity": 0, 954 | "duration": 200 955 | }, 956 | { 957 | "capacity": 6090, 958 | "duration": 199 959 | }, 960 | { 961 | "capacity": 839, 962 | "duration": 200 963 | }, 964 | { 965 | "capacity": 0, 966 | "duration": 200 967 | }, 968 | { 969 | "capacity": 6440, 970 | "duration": 199 971 | }, 972 | { 973 | "capacity": 2800, 974 | "duration": 200 975 | }, 976 | { 977 | "capacity": 0, 978 | "duration": 199 979 | }, 980 | { 981 | "capacity": 3010, 982 | "duration": 200 983 | }, 984 | { 985 | "capacity": 3600, 986 | "duration": 200 987 | }, 988 | { 989 | "capacity": 0, 990 | "duration": 199 991 | }, 992 | { 993 | "capacity": 2400, 994 | "duration": 200 995 | }, 996 | { 997 | "capacity": 5920, 998 | "duration": 199 999 | }, 1000 | { 1001 | "capacity": 0, 1002 | "duration": 200 1003 | }, 1004 | { 1005 | "capacity": 882, 1006 | "duration": 200 1007 | }, 1008 | { 1009 | "capacity": 6480, 1010 | "duration": 199 1011 | }, 1012 | { 1013 | "capacity": 3800, 1014 | "duration": 200 1015 | }, 1016 | { 1017 | "capacity": 7640, 1018 | "duration": 199 1019 | }, 1020 | { 1021 | "capacity": 7030, 1022 | "duration": 200 1023 | }, 1024 | { 1025 | "capacity": 960, 1026 | "duration": 200 1027 | }, 1028 | { 1029 | "capacity": 0, 1030 | "duration": 199 1031 | }, 1032 | { 1033 | "capacity": 5080, 1034 | "duration": 200 1035 | }, 1036 | { 1037 | "capacity": 2560, 1038 | "duration": 199 1039 | }, 1040 | { 1041 | "capacity": 5720, 1042 | "duration": 200 1043 | }, 1044 | { 1045 | "capacity": 6170, 1046 | "duration": 200 1047 | }, 1048 | { 1049 | "capacity": 2560, 1050 | "duration": 199 1051 | }, 1052 | { 1053 | "capacity": 0, 1054 | "duration": 200 1055 | }, 1056 | { 1057 | "capacity": 3170, 1058 | "duration": 199 1059 | }, 1060 | { 1061 | "capacity": 5520, 1062 | "duration": 200 1063 | }, 1064 | { 1065 | "capacity": 3920, 1066 | "duration": 200 1067 | }, 1068 | { 1069 | "capacity": 7360, 1070 | "duration": 199 1071 | }, 1072 | { 1073 | "capacity": 6670, 1074 | "duration": 200 1075 | }, 1076 | { 1077 | "capacity": 79900, 1078 | "duration": 199 1079 | }, 1080 | { 1081 | "capacity": 240, 1082 | "duration": 200 1083 | }, 1084 | { 1085 | "capacity": 5160, 1086 | "duration": 200 1087 | }, 1088 | { 1089 | "capacity": 4160, 1090 | "duration": 199 1091 | }, 1092 | { 1093 | "capacity": 5800, 1094 | "duration": 200 1095 | }, 1096 | { 1097 | "capacity": 5560, 1098 | "duration": 199 1099 | }, 1100 | { 1101 | "capacity": 4720, 1102 | "duration": 200 1103 | }, 1104 | { 1105 | "capacity": 7010, 1106 | "duration": 200 1107 | }, 1108 | { 1109 | "capacity": 6790, 1110 | "duration": 199 1111 | }, 1112 | { 1113 | "capacity": 4440, 1114 | "duration": 200 1115 | }, 1116 | { 1117 | "capacity": 0, 1118 | "duration": 199 1119 | }, 1120 | { 1121 | "capacity": 0, 1122 | "duration": 200 1123 | }, 1124 | { 1125 | "capacity": 4770, 1126 | "duration": 200 1127 | }, 1128 | { 1129 | "capacity": 0, 1130 | "duration": 199 1131 | }, 1132 | { 1133 | "capacity": 2040, 1134 | "duration": 200 1135 | }, 1136 | { 1137 | "capacity": 7280, 1138 | "duration": 199 1139 | }, 1140 | { 1141 | "capacity": 0, 1142 | "duration": 200 1143 | }, 1144 | { 1145 | "capacity": 0, 1146 | "duration": 200 1147 | }, 1148 | { 1149 | "capacity": 6890, 1150 | "duration": 199 1151 | }, 1152 | { 1153 | "capacity": 679, 1154 | "duration": 200 1155 | }, 1156 | { 1157 | "capacity": 2280, 1158 | "duration": 199 1159 | }, 1160 | { 1161 | "capacity": 6520, 1162 | "duration": 200 1163 | }, 1164 | { 1165 | "capacity": 2360, 1166 | "duration": 200 1167 | }, 1168 | { 1169 | "capacity": 9680, 1170 | "duration": 199 1171 | }, 1172 | { 1173 | "capacity": 7040, 1174 | "duration": 200 1175 | }, 1176 | { 1177 | "capacity": 3800, 1178 | "duration": 199 1179 | }, 1180 | { 1181 | "capacity": 0, 1182 | "duration": 200 1183 | }, 1184 | { 1185 | "capacity": 3970, 1186 | "duration": 200 1187 | }, 1188 | { 1189 | "capacity": 5470, 1190 | "duration": 199 1191 | }, 1192 | { 1193 | "capacity": 0, 1194 | "duration": 200 1195 | }, 1196 | { 1197 | "capacity": 2800, 1198 | "duration": 199 1199 | }, 1200 | { 1201 | "capacity": 6440, 1202 | "duration": 200 1203 | }, 1204 | { 1205 | "capacity": 0, 1206 | "duration": 200 1207 | }, 1208 | { 1209 | "capacity": 0, 1210 | "duration": 199 1211 | }, 1212 | { 1213 | "capacity": 5680, 1214 | "duration": 200 1215 | }, 1216 | { 1217 | "capacity": 1080, 1218 | "duration": 199 1219 | }, 1220 | { 1221 | "capacity": 120, 1222 | "duration": 200 1223 | }, 1224 | { 1225 | "capacity": 5730, 1226 | "duration": 49 1227 | } 1228 | ] 1229 | } 1230 | } -------------------------------------------------------------------------------- /alphartc_gym/tests/data/WIRED_900kbs.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 504, 8 | "loss": 0.0, 9 | "capacity": 790, 10 | "jitter": 0.0 11 | }, 12 | { 13 | "duration": 507, 14 | "loss": 0.0, 15 | "capacity": 900, 16 | "jitter": 0.0 17 | }, 18 | { 19 | "duration": 514, 20 | "loss": 0.0, 21 | "capacity": 879, 22 | "jitter": 0.0 23 | }, 24 | { 25 | "duration": 500, 26 | "loss": 0.0, 27 | "capacity": 915, 28 | "jitter": 0.0 29 | }, 30 | { 31 | "duration": 503, 32 | "loss": 0.0, 33 | "capacity": 821, 34 | "jitter": 0.0 35 | }, 36 | { 37 | "duration": 501, 38 | "loss": 0.0, 39 | "capacity": 867, 40 | "jitter": 0.0 41 | }, 42 | { 43 | "duration": 505, 44 | "loss": 0.0, 45 | "capacity": 819, 46 | "jitter": 0.0 47 | }, 48 | { 49 | "duration": 507, 50 | "loss": 0.0, 51 | "capacity": 870, 52 | "jitter": 0.0 53 | }, 54 | { 55 | "duration": 502, 56 | "loss": 0.0, 57 | "capacity": 857, 58 | "jitter": 0.0 59 | }, 60 | { 61 | "duration": 502, 62 | "loss": 0.0, 63 | "capacity": 694, 64 | "jitter": 0.0 65 | }, 66 | { 67 | "duration": 519, 68 | "loss": 0.0, 69 | "capacity": 872, 70 | "jitter": 0.0 71 | }, 72 | { 73 | "duration": 500, 74 | "loss": 0.0, 75 | "capacity": 687, 76 | "jitter": 0.0 77 | }, 78 | { 79 | "duration": 500, 80 | "loss": 0.0, 81 | "capacity": 926, 82 | "jitter": 0.0 83 | }, 84 | { 85 | "duration": 501, 86 | "loss": 0.0, 87 | "capacity": 856, 88 | "jitter": 0.0 89 | }, 90 | { 91 | "duration": 518, 92 | "loss": 0.0, 93 | "capacity": 856, 94 | "jitter": 0.0 95 | }, 96 | { 97 | "duration": 501, 98 | "loss": 0.0, 99 | "capacity": 980, 100 | "jitter": 0.0 101 | }, 102 | { 103 | "duration": 501, 104 | "loss": 0.0, 105 | "capacity": 811, 106 | "jitter": 0.0 107 | }, 108 | { 109 | "duration": 519, 110 | "loss": 0.0, 111 | "capacity": 982, 112 | "jitter": 0.0 113 | }, 114 | { 115 | "duration": 500, 116 | "loss": 0.0, 117 | "capacity": 831, 118 | "jitter": 0.0 119 | }, 120 | { 121 | "duration": 514, 122 | "loss": 0.0, 123 | "capacity": 853, 124 | "jitter": 0.0 125 | }, 126 | { 127 | "duration": 505, 128 | "loss": 0.0, 129 | "capacity": 847, 130 | "jitter": 0.0 131 | }, 132 | { 133 | "duration": 502, 134 | "loss": 0.0, 135 | "capacity": 955, 136 | "jitter": 0.0 137 | }, 138 | { 139 | "duration": 519, 140 | "loss": 0.0, 141 | "capacity": 780, 142 | "jitter": 0.0 143 | }, 144 | { 145 | "duration": 501, 146 | "loss": 0.0, 147 | "capacity": 973, 148 | "jitter": 0.0 149 | }, 150 | { 151 | "duration": 521, 152 | "loss": 0.0, 153 | "capacity": 591, 154 | "jitter": 0.0 155 | }, 156 | { 157 | "duration": 508, 158 | "loss": 0.0, 159 | "capacity": 1034, 160 | "jitter": 0.0 161 | }, 162 | { 163 | "duration": 512, 164 | "loss": 0.0, 165 | "capacity": 638, 166 | "jitter": 0.0 167 | }, 168 | { 169 | "duration": 513, 170 | "loss": 0.0, 171 | "capacity": 1016, 172 | "jitter": 0.0 173 | }, 174 | { 175 | "duration": 506, 176 | "loss": 0.0, 177 | "capacity": 761, 178 | "jitter": 0.0 179 | }, 180 | { 181 | "duration": 500, 182 | "loss": 0.0, 183 | "capacity": 1014, 184 | "jitter": 0.0 185 | }, 186 | { 187 | "duration": 513, 188 | "loss": 0.0, 189 | "capacity": 806, 190 | "jitter": 0.0 191 | }, 192 | { 193 | "duration": 509, 194 | "loss": 0.0, 195 | "capacity": 852, 196 | "jitter": 0.0 197 | }, 198 | { 199 | "duration": 502, 200 | "loss": 0.0, 201 | "capacity": 922, 202 | "jitter": 0.0 203 | }, 204 | { 205 | "duration": 512, 206 | "loss": 0.0, 207 | "capacity": 854, 208 | "jitter": 0.0 209 | }, 210 | { 211 | "duration": 506, 212 | "loss": 0.0, 213 | "capacity": 979, 214 | "jitter": 0.0 215 | }, 216 | { 217 | "duration": 519, 218 | "loss": 0.0, 219 | "capacity": 822, 220 | "jitter": 0.0 221 | }, 222 | { 223 | "duration": 520, 224 | "loss": 0.0, 225 | "capacity": 856, 226 | "jitter": 0.0 227 | }, 228 | { 229 | "duration": 500, 230 | "loss": 0.0, 231 | "capacity": 933, 232 | "jitter": 0.0 233 | }, 234 | { 235 | "duration": 500, 236 | "loss": 0.0, 237 | "capacity": 848, 238 | "jitter": 0.0 239 | }, 240 | { 241 | "duration": 501, 242 | "loss": 0.0, 243 | "capacity": 762, 244 | "jitter": 0.0 245 | }, 246 | { 247 | "duration": 518, 248 | "loss": 0.0, 249 | "capacity": 959, 250 | "jitter": 0.0 251 | }, 252 | { 253 | "duration": 501, 254 | "loss": 0.0, 255 | "capacity": 873, 256 | "jitter": 0.0 257 | }, 258 | { 259 | "duration": 514, 260 | "loss": 0.0, 261 | "capacity": 867, 262 | "jitter": 0.0 263 | }, 264 | { 265 | "duration": 505, 266 | "loss": 0.0, 267 | "capacity": 853, 268 | "jitter": 0.0 269 | }, 270 | { 271 | "duration": 501, 272 | "loss": 0.0, 273 | "capacity": 888, 274 | "jitter": 0.0 275 | }, 276 | { 277 | "duration": 501, 278 | "loss": 0.0, 279 | "capacity": 905, 280 | "jitter": 0.0 281 | }, 282 | { 283 | "duration": 503, 284 | "loss": 0.0, 285 | "capacity": 763, 286 | "jitter": 0.0 287 | }, 288 | { 289 | "duration": 515, 290 | "loss": 0.0, 291 | "capacity": 832, 292 | "jitter": 0.0 293 | }, 294 | { 295 | "duration": 501, 296 | "loss": 0.0, 297 | "capacity": 1014, 298 | "jitter": 0.0 299 | }, 300 | { 301 | "duration": 500, 302 | "loss": 0.0, 303 | "capacity": 836, 304 | "jitter": 0.0 305 | }, 306 | { 307 | "duration": 500, 308 | "loss": 0.0, 309 | "capacity": 908, 310 | "jitter": 0.0 311 | }, 312 | { 313 | "duration": 500, 314 | "loss": 0.0, 315 | "capacity": 752, 316 | "jitter": 0.0 317 | }, 318 | { 319 | "duration": 503, 320 | "loss": 0.0, 321 | "capacity": 970, 322 | "jitter": 0.0 323 | }, 324 | { 325 | "duration": 516, 326 | "loss": 0.0, 327 | "capacity": 863, 328 | "jitter": 0.0 329 | }, 330 | { 331 | "duration": 501, 332 | "loss": 0.0, 333 | "capacity": 816, 334 | "jitter": 0.0 335 | }, 336 | { 337 | "duration": 509, 338 | "loss": 0.0, 339 | "capacity": 947, 340 | "jitter": 0.0 341 | }, 342 | { 343 | "duration": 512, 344 | "loss": 0.0, 345 | "capacity": 765, 346 | "jitter": 0.0 347 | }, 348 | { 349 | "duration": 514, 350 | "loss": 0.0, 351 | "capacity": 896, 352 | "jitter": 0.0 353 | }, 354 | { 355 | "duration": 505, 356 | "loss": 0.0, 357 | "capacity": 986, 358 | "jitter": 0.0 359 | }, 360 | { 361 | "duration": 500, 362 | "loss": 0.0, 363 | "capacity": 763, 364 | "jitter": 0.0 365 | }, 366 | { 367 | "duration": 500, 368 | "loss": 0.0, 369 | "capacity": 951, 370 | "jitter": 0.0 371 | }, 372 | { 373 | "duration": 501, 374 | "loss": 0.0, 375 | "capacity": 800, 376 | "jitter": 0.0 377 | }, 378 | { 379 | "duration": 520, 380 | "loss": 0.0, 381 | "capacity": 829, 382 | "jitter": 0.0 383 | }, 384 | { 385 | "duration": 510, 386 | "loss": 0.0, 387 | "capacity": 924, 388 | "jitter": 0.0 389 | }, 390 | { 391 | "duration": 504, 392 | "loss": 0.0, 393 | "capacity": 908, 394 | "jitter": 0.0 395 | }, 396 | { 397 | "duration": 505, 398 | "loss": 0.0, 399 | "capacity": 670, 400 | "jitter": 0.0 401 | }, 402 | { 403 | "duration": 500, 404 | "loss": 0.0, 405 | "capacity": 896, 406 | "jitter": 0.0 407 | }, 408 | { 409 | "duration": 500, 410 | "loss": 0.0, 411 | "capacity": 556, 412 | "jitter": 0.0 413 | }, 414 | { 415 | "duration": 510, 416 | "loss": 0.0, 417 | "capacity": 1032, 418 | "jitter": 0.0 419 | }, 420 | { 421 | "duration": 510, 422 | "loss": 0.0, 423 | "capacity": 996, 424 | "jitter": 0.0 425 | }, 426 | { 427 | "duration": 503, 428 | "loss": 0.0, 429 | "capacity": 879, 430 | "jitter": 0.0 431 | }, 432 | { 433 | "duration": 507, 434 | "loss": 0.0, 435 | "capacity": 779, 436 | "jitter": 0.0 437 | }, 438 | { 439 | "duration": 510, 440 | "loss": 0.0, 441 | "capacity": 893, 442 | "jitter": 0.0 443 | }, 444 | { 445 | "duration": 500, 446 | "loss": 0.0, 447 | "capacity": 981, 448 | "jitter": 0.0 449 | }, 450 | { 451 | "duration": 500, 452 | "loss": 0.0, 453 | "capacity": 779, 454 | "jitter": 0.0 455 | }, 456 | { 457 | "duration": 520, 458 | "loss": 0.0, 459 | "capacity": 918, 460 | "jitter": 0.0 461 | }, 462 | { 463 | "duration": 500, 464 | "loss": 0.0, 465 | "capacity": 811, 466 | "jitter": 0.0 467 | }, 468 | { 469 | "duration": 504, 470 | "loss": 0.0, 471 | "capacity": 938, 472 | "jitter": 0.0 473 | }, 474 | { 475 | "duration": 511, 476 | "loss": 0.0, 477 | "capacity": 821, 478 | "jitter": 0.0 479 | }, 480 | { 481 | "duration": 506, 482 | "loss": 0.0, 483 | "capacity": 848, 484 | "jitter": 0.0 485 | }, 486 | { 487 | "duration": 520, 488 | "loss": 0.0, 489 | "capacity": 815, 490 | "jitter": 0.0 491 | }, 492 | { 493 | "duration": 520, 494 | "loss": 0.0, 495 | "capacity": 919, 496 | "jitter": 0.0 497 | }, 498 | { 499 | "duration": 518, 500 | "loss": 0.0, 501 | "capacity": 877, 502 | "jitter": 0.0 503 | }, 504 | { 505 | "duration": 500, 506 | "loss": 0.0, 507 | "capacity": 908, 508 | "jitter": 0.0 509 | }, 510 | { 511 | "duration": 500, 512 | "loss": 0.0, 513 | "capacity": 848, 514 | "jitter": 0.0 515 | }, 516 | { 517 | "duration": 501, 518 | "loss": 0.0, 519 | "capacity": 927, 520 | "jitter": 0.0 521 | }, 522 | { 523 | "duration": 500, 524 | "loss": 0.0, 525 | "capacity": 853, 526 | "jitter": 0.0 527 | }, 528 | { 529 | "duration": 500, 530 | "loss": 0.0, 531 | "capacity": 808, 532 | "jitter": 0.0 533 | }, 534 | { 535 | "duration": 500, 536 | "loss": 0.0, 537 | "capacity": 844, 538 | "jitter": 0.0 539 | }, 540 | { 541 | "duration": 500, 542 | "loss": 0.0, 543 | "capacity": 814, 544 | "jitter": 0.0 545 | }, 546 | { 547 | "duration": 500, 548 | "loss": 0.0, 549 | "capacity": 934, 550 | "jitter": 0.0 551 | }, 552 | { 553 | "duration": 521, 554 | "loss": 0.0, 555 | "capacity": 766, 556 | "jitter": 0.0 557 | }, 558 | { 559 | "duration": 504, 560 | "loss": 0.0, 561 | "capacity": 825, 562 | "jitter": 0.0 563 | }, 564 | { 565 | "duration": 516, 566 | "loss": 0.0, 567 | "capacity": 773, 568 | "jitter": 0.0 569 | }, 570 | { 571 | "duration": 508, 572 | "loss": 0.0, 573 | "capacity": 1031, 574 | "jitter": 0.0 575 | }, 576 | { 577 | "duration": 510, 578 | "loss": 0.0, 579 | "capacity": 603, 580 | "jitter": 0.0 581 | }, 582 | { 583 | "duration": 500, 584 | "loss": 0.0, 585 | "capacity": 1141, 586 | "jitter": 0.0 587 | }, 588 | { 589 | "duration": 500, 590 | "loss": 0.0, 591 | "capacity": 798, 592 | "jitter": 0.0 593 | }, 594 | { 595 | "duration": 501, 596 | "loss": 0.0, 597 | "capacity": 772, 598 | "jitter": 0.0 599 | }, 600 | { 601 | "duration": 520, 602 | "loss": 0.0, 603 | "capacity": 892, 604 | "jitter": 0.0 605 | }, 606 | { 607 | "duration": 517, 608 | "loss": 0.0, 609 | "capacity": 955, 610 | "jitter": 0.0 611 | }, 612 | { 613 | "duration": 500, 614 | "loss": 0.0, 615 | "capacity": 755, 616 | "jitter": 0.0 617 | }, 618 | { 619 | "duration": 500, 620 | "loss": 0.0, 621 | "capacity": 991, 622 | "jitter": 0.0 623 | }, 624 | { 625 | "duration": 502, 626 | "loss": 0.0, 627 | "capacity": 751, 628 | "jitter": 0.0 629 | }, 630 | { 631 | "duration": 500, 632 | "loss": 0.0, 633 | "capacity": 908, 634 | "jitter": 0.0 635 | }, 636 | { 637 | "duration": 521, 638 | "loss": 0.0, 639 | "capacity": 860, 640 | "jitter": 0.0 641 | }, 642 | { 643 | "duration": 518, 644 | "loss": 0.0, 645 | "capacity": 981, 646 | "jitter": 0.0 647 | }, 648 | { 649 | "duration": 502, 650 | "loss": 0.0, 651 | "capacity": 804, 652 | "jitter": 0.0 653 | }, 654 | { 655 | "duration": 504, 656 | "loss": 0.0, 657 | "capacity": 907, 658 | "jitter": 0.0 659 | }, 660 | { 661 | "duration": 514, 662 | "loss": 0.0, 663 | "capacity": 804, 664 | "jitter": 0.0 665 | }, 666 | { 667 | "duration": 500, 668 | "loss": 0.0, 669 | "capacity": 863, 670 | "jitter": 0.0 671 | }, 672 | { 673 | "duration": 521, 674 | "loss": 0.0, 675 | "capacity": 786, 676 | "jitter": 0.0 677 | }, 678 | { 679 | "duration": 503, 680 | "loss": 0.0, 681 | "capacity": 869, 682 | "jitter": 0.0 683 | }, 684 | { 685 | "duration": 358, 686 | "loss": 0.0, 687 | "capacity": 1051, 688 | "jitter": 0.0 689 | } 690 | ] 691 | } 692 | } -------------------------------------------------------------------------------- /alphartc_gym/tests/data/loss/trace_loss_0.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 200, 8 | "capacity": 1000, 9 | "loss": 0, 10 | "rtt": 0, 11 | "jitter": 0 12 | } 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /alphartc_gym/tests/data/loss/trace_loss_0dot1.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 200, 8 | "capacity": 1000, 9 | "loss": 0.1, 10 | "rtt": 0, 11 | "jitter": 0 12 | } 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /alphartc_gym/tests/data/loss/trace_loss_0dot5.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 200, 8 | "capacity": 1000, 9 | "loss": 0.5, 10 | "rtt": 0, 11 | "jitter": 0 12 | } 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /alphartc_gym/tests/data/loss/trace_loss_pattern_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 500, 8 | "capacity": 1000, 9 | "loss": 0, 10 | "rtt": 0, 11 | "jitter": 0 12 | }, 13 | { 14 | "duration": 500, 15 | "capacity": 1000, 16 | "loss": 0.2, 17 | "rtt": 0, 18 | "jitter": 0 19 | } 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /alphartc_gym/tests/data/loss/trace_loss_pattern_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 500, 8 | "capacity": 1000, 9 | "loss": 0, 10 | "rtt": 0, 11 | "jitter": 0 12 | }, 13 | { 14 | "duration": 500, 15 | "capacity": 1000, 16 | "loss": 0.2, 17 | "rtt": 0, 18 | "jitter": 0 19 | }, 20 | { 21 | "duration": 500, 22 | "capacity": 1000, 23 | "loss": 0.1, 24 | "rtt": 0, 25 | "jitter": 0 26 | } 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /alphartc_gym/tests/data/loss/trace_loss_pattern_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 500, 8 | "capacity": 1000, 9 | "loss": 0, 10 | "rtt": 0, 11 | "jitter": 0 12 | }, 13 | { 14 | "duration": 500, 15 | "capacity": 1000, 16 | "loss": 0.2, 17 | "rtt": 0, 18 | "jitter": 0 19 | }, 20 | { 21 | "duration": 500, 22 | "capacity": 1000, 23 | "loss": 0.5, 24 | "rtt": 0, 25 | "jitter": 0 26 | }, 27 | { 28 | "duration": 500, 29 | "capacity": 1000, 30 | "loss": 0.2, 31 | "rtt": 0, 32 | "jitter": 0 33 | } 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /alphartc_gym/tests/data/rtt/trace_rtt_200.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 1000, 8 | "capacity": 1000, 9 | "rtt": 200, 10 | "loss": 0, 11 | "jitter": 0 12 | } 13 | ] 14 | } 15 | } -------------------------------------------------------------------------------- /alphartc_gym/tests/data/rtt/trace_rtt_400.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 1000, 8 | "capacity": 1000, 9 | "rtt": 400, 10 | "loss": 0, 11 | "jitter": 0 12 | } 13 | ] 14 | } 15 | } -------------------------------------------------------------------------------- /alphartc_gym/tests/data/rtt/trace_rtt_600.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 1000, 8 | "capacity": 1000, 9 | "rtt": 600, 10 | "loss": 0, 11 | "jitter": 0 12 | } 13 | ] 14 | } 15 | } -------------------------------------------------------------------------------- /alphartc_gym/tests/data/rtt/trace_rtt_pattern_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 2000, 8 | "capacity": 1000, 9 | "loss": 0, 10 | "rtt": 200, 11 | "jitter": 0 12 | }, 13 | { 14 | "duration": 2000, 15 | "capacity": 1000, 16 | "loss": 0, 17 | "rtt": 400, 18 | "jitter": 0 19 | } 20 | ] 21 | } 22 | } -------------------------------------------------------------------------------- /alphartc_gym/tests/data/rtt/trace_rtt_pattern_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 2000, 8 | "capacity": 1000, 9 | "loss": 0, 10 | "rtt": 200, 11 | "jitter": 0 12 | }, 13 | { 14 | "duration": 2000, 15 | "capacity": 1000, 16 | "loss": 0, 17 | "rtt": 200, 18 | "jitter": 0 19 | }, 20 | { 21 | "duration": 2000, 22 | "capacity": 1000, 23 | "loss": 0, 24 | "rtt": 200, 25 | "jitter": 0 26 | } 27 | ] 28 | } 29 | } -------------------------------------------------------------------------------- /alphartc_gym/tests/data/rtt/trace_rtt_pattern_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 2000, 8 | "capacity": 1000, 9 | "loss": 0, 10 | "rtt": 200, 11 | "jitter": 0 12 | }, 13 | { 14 | "duration": 2000, 15 | "capacity": 1000, 16 | "loss": 0, 17 | "rtt": 400, 18 | "jitter": 0 19 | }, 20 | { 21 | "duration": 2000, 22 | "capacity": 1000, 23 | "loss": 0, 24 | "rtt": 300, 25 | "jitter": 0 26 | }, 27 | { 28 | "duration": 2000, 29 | "capacity": 1000, 30 | "loss": 0, 31 | "rtt": 500, 32 | "jitter": 0 33 | } 34 | ] 35 | } 36 | } -------------------------------------------------------------------------------- /alphartc_gym/tests/data/trace_300k.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 60000, 8 | "capacity": 300, 9 | "loss": 0, 10 | "jitter": 0 11 | } 12 | ] 13 | } 14 | } -------------------------------------------------------------------------------- /alphartc_gym/tests/data/trace_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "video", 3 | "downlink": {}, 4 | "uplink": { 5 | "trace_pattern": [ 6 | { 7 | "duration": 2896, 8 | "capacity": 179, 9 | "loss": 0, 10 | "rtt": 89, 11 | "jitter": 0 12 | }, 13 | { 14 | "duration": 613, 15 | "capacity": 177, 16 | "loss": 0, 17 | "rtt": 103, 18 | "jitter": 0 19 | }, 20 | { 21 | "duration": 2750, 22 | "capacity": 500, 23 | "loss": 0, 24 | "rtt": 85, 25 | "jitter": 0 26 | }, 27 | { 28 | "duration": 916, 29 | "capacity": 1000, 30 | "loss": 0, 31 | "rtt": 91, 32 | "jitter": 0 33 | }, 34 | { 35 | "duration": 157, 36 | "capacity": 3000, 37 | "loss": 0, 38 | "rtt": 95, 39 | "jitter": 0 40 | }, 41 | { 42 | "duration": 62, 43 | "capacity": 500, 44 | "loss": 0, 45 | "rtt": 87, 46 | "jitter": 0 47 | }, 48 | { 49 | "duration": 1585, 50 | "capacity": 600, 51 | "loss": 0, 52 | "rtt": 88, 53 | "jitter": 0 54 | }, 55 | { 56 | "duration": 653, 57 | "capacity": 100, 58 | "loss": 0, 59 | "rtt": 99, 60 | "jitter": 0 61 | }, 62 | { 63 | "duration": 72, 64 | "capacity": 900, 65 | "loss": 0, 66 | "rtt": 72, 67 | "jitter": 0 68 | }, 69 | { 70 | "duration": 1286, 71 | "capacity": 187, 72 | "loss": 0, 73 | "rtt": 94, 74 | "jitter": 0 75 | }, 76 | { 77 | "duration": 7727, 78 | "capacity": 123, 79 | "loss": 0, 80 | "rtt": 86, 81 | "jitter": 0 82 | }, 83 | { 84 | "duration": 2274, 85 | "capacity": 555, 86 | "loss": 0, 87 | "rtt": 94, 88 | "jitter": 0 89 | }, 90 | { 91 | "duration": 5617, 92 | "capacity": 147, 93 | "loss": 0, 94 | "rtt": 87, 95 | "jitter": 0 96 | }, 97 | { 98 | "duration": 3864, 99 | "capacity": 4567, 100 | "loss": 0, 101 | "rtt": 98, 102 | "jitter": 0 103 | }, 104 | { 105 | "duration": 401, 106 | "capacity": 1234, 107 | "loss": 0, 108 | "rtt": 96, 109 | "jitter": 0 110 | }, 111 | { 112 | "duration": 3524, 113 | "capacity": 657, 114 | "loss": 0, 115 | "rtt": 101, 116 | "jitter": 0 117 | }, 118 | { 119 | "duration": 631, 120 | "capacity": 179, 121 | "loss": 0, 122 | "rtt": 78, 123 | "jitter": 0 124 | }, 125 | { 126 | "duration": 1479, 127 | "capacity": 179, 128 | "loss": 0, 129 | "rtt": 86, 130 | "jitter": 0 131 | }, 132 | { 133 | "duration": 23, 134 | "capacity": 179, 135 | "loss": 0, 136 | "rtt": 82, 137 | "jitter": 0 138 | }, 139 | { 140 | "duration": 1192, 141 | "capacity": 179, 142 | "loss": 0, 143 | "rtt": 78, 144 | "jitter": 0 145 | }, 146 | { 147 | "duration": 196, 148 | "capacity": 179, 149 | "loss": 0, 150 | "rtt": 89, 151 | "jitter": 0 152 | }, 153 | { 154 | "duration": 808, 155 | "capacity": 179, 156 | "loss": 0, 157 | "rtt": 97, 158 | "jitter": 0 159 | }, 160 | { 161 | "duration": 1650, 162 | "capacity": 179, 163 | "loss": 0, 164 | "rtt": 106, 165 | "jitter": 0 166 | }, 167 | { 168 | "duration": 165, 169 | "capacity": 179, 170 | "loss": 0, 171 | "rtt": 97, 172 | "jitter": 0 173 | }, 174 | { 175 | "duration": 107, 176 | "capacity": 179, 177 | "loss": 0, 178 | "rtt": 87, 179 | "jitter": 0 180 | }, 181 | { 182 | "duration": 1202, 183 | "capacity": 179, 184 | "loss": 0, 185 | "rtt": 82, 186 | "jitter": 0 187 | }, 188 | { 189 | "duration": 2336, 190 | "capacity": 179, 191 | "loss": 0, 192 | "rtt": 100, 193 | "jitter": 0 194 | }, 195 | { 196 | "duration": 1444, 197 | "capacity": 179, 198 | "loss": 0, 199 | "rtt": 82, 200 | "jitter": 0 201 | }, 202 | { 203 | "duration": 1480, 204 | "capacity": 179, 205 | "loss": 0, 206 | "rtt": 88, 207 | "jitter": 0 208 | }, 209 | { 210 | "duration": 493, 211 | "capacity": 179, 212 | "loss": 0, 213 | "rtt": 89, 214 | "jitter": 0 215 | }, 216 | { 217 | "duration": 457, 218 | "capacity": 179, 219 | "loss": 0, 220 | "rtt": 105, 221 | "jitter": 0 222 | }, 223 | { 224 | "duration": 1707, 225 | "capacity": 179, 226 | "loss": 0, 227 | "rtt": 88, 228 | "jitter": 0 229 | }, 230 | { 231 | "duration": 2179, 232 | "capacity": 179, 233 | "loss": 0, 234 | "rtt": 90, 235 | "jitter": 0 236 | }, 237 | { 238 | "duration": 241, 239 | "capacity": 179, 240 | "loss": 0, 241 | "rtt": 94, 242 | "jitter": 0 243 | }, 244 | { 245 | "duration": 4446, 246 | "capacity": 179, 247 | "loss": 0, 248 | "rtt": 82, 249 | "jitter": 0 250 | }, 251 | { 252 | "duration": 227, 253 | "capacity": 179, 254 | "loss": 0, 255 | "rtt": 95, 256 | "jitter": 0 257 | }, 258 | { 259 | "duration": 3819, 260 | "capacity": 179, 261 | "loss": 0, 262 | "rtt": 91, 263 | "jitter": 0 264 | }, 265 | { 266 | "duration": 1143, 267 | "capacity": 179, 268 | "loss": 0, 269 | "rtt": 94, 270 | "jitter": 0 271 | }, 272 | { 273 | "duration": 139, 274 | "capacity": 179, 275 | "loss": 0, 276 | "rtt": 91, 277 | "jitter": 0 278 | }, 279 | { 280 | "duration": 94, 281 | "capacity": 179, 282 | "loss": 0, 283 | "rtt": 90, 284 | "jitter": 0 285 | }, 286 | { 287 | "duration": 1123, 288 | "capacity": 179, 289 | "loss": 0, 290 | "rtt": 85, 291 | "jitter": 0 292 | }, 293 | { 294 | "duration": 119, 295 | "capacity": 179, 296 | "loss": 0, 297 | "rtt": 84, 298 | "jitter": 0 299 | }, 300 | { 301 | "duration": 4666, 302 | "capacity": 179, 303 | "loss": 0, 304 | "rtt": 83, 305 | "jitter": 0 306 | }, 307 | { 308 | "duration": 374, 309 | "capacity": 179, 310 | "loss": 0, 311 | "rtt": 102, 312 | "jitter": 0 313 | }, 314 | { 315 | "duration": 409, 316 | "capacity": 179, 317 | "loss": 0, 318 | "rtt": 91, 319 | "jitter": 0 320 | }, 321 | { 322 | "duration": 557, 323 | "capacity": 179, 324 | "loss": 0, 325 | "rtt": 99, 326 | "jitter": 0 327 | }, 328 | { 329 | "duration": 843, 330 | "capacity": 179, 331 | "loss": 0, 332 | "rtt": 100, 333 | "jitter": 0 334 | }, 335 | { 336 | "duration": 519, 337 | "capacity": 179, 338 | "loss": 0, 339 | "rtt": 84, 340 | "jitter": 0 341 | }, 342 | { 343 | "duration": 2159, 344 | "capacity": 179, 345 | "loss": 0, 346 | "rtt": 95, 347 | "jitter": 0 348 | }, 349 | { 350 | "duration": 8764, 351 | "capacity": 179, 352 | "loss": 0, 353 | "rtt": 89, 354 | "jitter": 0 355 | }, 356 | { 357 | "duration": 4114, 358 | "capacity": 179, 359 | "loss": 0, 360 | "rtt": 80, 361 | "jitter": 0 362 | }, 363 | { 364 | "duration": 7243, 365 | "capacity": 179, 366 | "loss": 0, 367 | "rtt": 89, 368 | "jitter": 0 369 | }, 370 | { 371 | "duration": 3133, 372 | "capacity": 179, 373 | "loss": 0, 374 | "rtt": 97, 375 | "jitter": 0 376 | }, 377 | { 378 | "duration": 1720, 379 | "capacity": 179, 380 | "loss": 0, 381 | "rtt": 93, 382 | "jitter": 0 383 | }, 384 | { 385 | "duration": 1971, 386 | "capacity": 179, 387 | "loss": 0, 388 | "rtt": 77, 389 | "jitter": 0 390 | }, 391 | { 392 | "duration": 6271, 393 | "capacity": 179, 394 | "loss": 0, 395 | "rtt": 92, 396 | "jitter": 0 397 | } 398 | ] 399 | } 400 | } -------------------------------------------------------------------------------- /alphartc_gym/tests/test_gym.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from alphartc_gym import gym 5 | 6 | import os 7 | import glob 8 | 9 | 10 | def test_basic(): 11 | total_stats = [] 12 | g = gym.Gym("test_gym") 13 | g.reset() 14 | while True: 15 | stats, done = g.step(1000) 16 | if not done: 17 | total_stats += stats 18 | else: 19 | break 20 | assert(total_stats) 21 | for stats in total_stats: 22 | assert(isinstance(stats, dict)) 23 | 24 | def test_multiple_instances(): 25 | total_stats = [] 26 | g1 = gym.Gym() 27 | g2 = gym.Gym() 28 | g1.reset() 29 | g2.reset() 30 | while True: 31 | stats, done = g1.step(1000) 32 | if not done: 33 | total_stats += stats 34 | else: 35 | break 36 | while True: 37 | stats, done = g2.step(1000) 38 | if not done: 39 | total_stats += stats 40 | else: 41 | break 42 | assert(total_stats) 43 | for stats in total_stats: 44 | assert(isinstance(stats, dict)) 45 | 46 | def test_trace(): 47 | total_stats = [] 48 | trace_files = os.path.join(os.path.dirname(__file__), "data", "*.json") 49 | for trace_file in glob.glob(trace_files): 50 | g = gym.Gym("test_gym") 51 | g.reset(trace_path=trace_file, report_interval_ms=60, duration_time_ms=0) 52 | while True: 53 | stats, done = g.step(1000) 54 | if not done: 55 | total_stats += stats 56 | else: 57 | break 58 | assert(total_stats) 59 | for stats in total_stats: 60 | assert(isinstance(stats, dict)) 61 | -------------------------------------------------------------------------------- /alphartc_gym/tests/test_gym_connect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from alphartc_gym import gym_connect 5 | 6 | import zmq 7 | 8 | import threading 9 | import pytest 10 | 11 | 12 | def create_reply(conn_name: str): 13 | ctx = zmq.Context() 14 | sock = ctx.socket(zmq.REP) 15 | sock.bind(gym_connect.__ZMQ_PREFIX__ + conn_name) 16 | return ctx, sock 17 | 18 | 19 | def fill_reply(sock): 20 | def fill_reply_(): 21 | bandwidth = sock.recv() 22 | assert(bandwidth.decode('utf-8').isnumeric()) 23 | sock.send(b"{}") 24 | bandwidth = sock.recv() 25 | assert(bandwidth.decode('utf-8').isnumeric()) 26 | sock.send(gym_connect.__GYM_EXIT_FLAG__) 27 | t = threading.Thread(target=fill_reply_) 28 | t.start() 29 | return t 30 | 31 | 32 | @pytest.mark.timeout(1) 33 | def test_reply_and_request(): 34 | ctx, sock = create_reply("test_gym") 35 | t = fill_reply(sock) 36 | conn = gym_connect.GymConnector("test_gym") 37 | assert(conn.step(1000) is not None) 38 | assert(conn.step(1000) is None) 39 | t.join() 40 | 41 | 42 | @pytest.mark.timeout(1) 43 | def test_request_and_reply(): 44 | conn = gym_connect.GymConnector("test_gym") 45 | ctx, sock = create_reply("test_gym") 46 | t = fill_reply(sock) 47 | assert(conn.step(1000) is not None) 48 | assert(conn.step(1000) is None) 49 | t.join() 50 | -------------------------------------------------------------------------------- /alphartc_gym/tests/test_gym_loss.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from alphartc_gym import gym 5 | import os, json 6 | 7 | 8 | ERROR_STATIC = 0.05 9 | ERROR_DYNAMICAL = 0.07 10 | 11 | 12 | def get_gym_stats(trace_path, duration_time_ms=3000, bandwidth_bps=1000000): 13 | total_stats = [] 14 | g = gym.Gym() 15 | g.reset(trace_path=trace_path, duration_time_ms=duration_time_ms) 16 | 17 | while True: 18 | stats, done = g.step(bandwidth_bps) 19 | if not done: 20 | total_stats += stats 21 | else: 22 | return total_stats 23 | 24 | 25 | def get_info_from_trace(trace_file): 26 | with open(trace_file, 'r') as f: 27 | return json.loads(f.read()) 28 | 29 | 30 | def get_abs_path_by_name(trace_name): 31 | trace_path = os.path.join( 32 | os.path.dirname(__file__), 33 | "data/loss", 34 | trace_name) 35 | return trace_path 36 | 37 | 38 | def single_loss_available(trace_path): 39 | total_stats = get_gym_stats(trace_path) 40 | assert (total_stats) 41 | 42 | for stats in total_stats: 43 | assert (isinstance(stats, dict)) 44 | 45 | 46 | def single_loss_persistence(trace_path, run_times=1, bandwidth_bps=1000000): 47 | # get information from trace 48 | trace_data = get_info_from_trace(trace_path) 49 | trace_pattern = trace_data["uplink"]["trace_pattern"] 50 | predict_error_rate = trace_pattern[0]["loss"] 51 | duration_time_ms = sum([item["duration"] for item in trace_pattern]) * run_times 52 | 53 | total_stats = get_gym_stats(trace_path, duration_time_ms=duration_time_ms, bandwidth_bps=bandwidth_bps) 54 | assert (total_stats) 55 | 56 | mp_src_seq = {} 57 | now_total, now_loss = 0, 0 58 | for i in range(len(total_stats)): 59 | ssrc = total_stats[i]["ssrc"] 60 | if ssrc not in mp_src_seq: 61 | mp_src_seq[ssrc] = 0 62 | # calculate loss packet 63 | if mp_src_seq[ssrc] + 1 < total_stats[i]["sequence_number"]: 64 | while mp_src_seq[ssrc] + 1 < total_stats[i]["sequence_number"]: 65 | now_total += 1 66 | now_loss += 1 67 | mp_src_seq[ssrc] += 1 68 | now_total += 1 69 | mp_src_seq[ssrc] += 1 70 | assert abs(now_loss / now_total - predict_error_rate) <= predict_error_rate * ERROR_STATIC 71 | 72 | 73 | def single_loss_dynamically(trace_path, run_times=1, bandwidth_bps=1000000): 74 | # get information from trace 75 | trace_data = get_info_from_trace(trace_path) 76 | trace_pattern = trace_data["uplink"]["trace_pattern"] 77 | trace_duration_time_ms = sum([item["duration"] for item in trace_pattern]) 78 | predict_error_rate = sum([item["loss"] * (item["duration"] / trace_duration_time_ms) for item in trace_pattern]) 79 | duration_time_ms = sum([item["duration"] for item in trace_pattern]) * run_times 80 | 81 | total_stats = get_gym_stats(trace_path, duration_time_ms=duration_time_ms, bandwidth_bps=bandwidth_bps) 82 | assert (total_stats) 83 | 84 | mp_src_seq = {} 85 | now_total, now_loss = 0, 0 86 | for i in range(len(total_stats)): 87 | ssrc = total_stats[i]["ssrc"] 88 | if ssrc not in mp_src_seq: 89 | mp_src_seq[ssrc] = 0 90 | # calculate loss packet 91 | if mp_src_seq[ssrc] + 1 < total_stats[i]["sequence_number"]: 92 | while mp_src_seq[ssrc] + 1 < total_stats[i]["sequence_number"]: 93 | now_total += 1 94 | now_loss += 1 95 | mp_src_seq[ssrc] += 1 96 | now_total += 1 97 | mp_src_seq[ssrc] += 1 98 | assert abs(now_loss / now_total - predict_error_rate) <= predict_error_rate * ERROR_DYNAMICAL 99 | 100 | 101 | def test_loss_available(): 102 | traces_name = ["trace_loss_0.json", "trace_loss_0dot1.json", "trace_loss_0dot5.json"] 103 | for trace in traces_name: 104 | trace_path = get_abs_path_by_name(trace) 105 | single_loss_available(trace_path) 106 | 107 | 108 | def test_loss_persistence(): 109 | traces_name = ["trace_loss_0.json", "trace_loss_0dot1.json", "trace_loss_0dot5.json"] 110 | for trace in traces_name: 111 | trace_path = get_abs_path_by_name(trace) 112 | single_loss_persistence(trace_path, run_times=1000, bandwidth_bps=300000) 113 | 114 | def test_loss_dynamically(): 115 | traces_name = ["trace_loss_pattern_2.json", "trace_loss_pattern_3.json", "trace_loss_pattern_4.json"] 116 | for trace in traces_name: 117 | trace_path = get_abs_path_by_name(trace) 118 | single_loss_dynamically(trace_path, run_times=1000, bandwidth_bps=300000) 119 | -------------------------------------------------------------------------------- /alphartc_gym/tests/test_gym_process.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from alphartc_gym import gym_process 5 | from alphartc_gym import gym_connect 6 | 7 | import zmq 8 | 9 | import os 10 | import pytest 11 | 12 | @pytest.mark.timeout(1.5) 13 | def test_base(): 14 | proc = gym_process.GymProcess( 15 | gym_id="test_gym", 16 | report_interval_ms=60, 17 | duration_time_ms=1000) 18 | conn = gym_connect.GymConnector(gym_id = "test_gym") 19 | all_stats = [] 20 | while True: 21 | stats = conn.step(1e9) 22 | if stats == None: 23 | break 24 | all_stats += stats 25 | # stats shouldn't be empty 26 | assert(all_stats) 27 | for stats in all_stats: 28 | assert(isinstance(stats, dict)) 29 | assert(proc.wait() == 0) 30 | 31 | @pytest.mark.timeout(200) 32 | def test_trace(): 33 | trace_path = os.path.join( 34 | os.path.dirname(__file__), 35 | "data", 36 | "trace_example.json") 37 | proc = gym_process.GymProcess( 38 | gym_id="test_gym", 39 | trace_path=trace_path, 40 | report_interval_ms=60, 41 | duration_time_ms=0) 42 | conn = gym_connect.GymConnector(gym_id = "test_gym") 43 | all_stats = [] 44 | while True: 45 | stats = conn.step(1e9) 46 | if stats == None: 47 | break 48 | all_stats += stats 49 | # stats shouldn't be empty 50 | assert(all_stats) 51 | for stats in all_stats: 52 | assert(isinstance(stats, dict)) 53 | assert(proc.wait() == 0) 54 | -------------------------------------------------------------------------------- /alphartc_gym/tests/test_gym_rtt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from alphartc_gym import gym 5 | import os, json 6 | 7 | 8 | ERROR = 0.05 9 | 10 | 11 | def get_gym_stats(trace_path, duration_time_ms=3000, bandwidth_bps=1000): 12 | total_stats = [] 13 | g = gym.Gym() 14 | g.reset(trace_path=trace_path, duration_time_ms=duration_time_ms) 15 | 16 | while True: 17 | stats, done = g.step(bandwidth_bps) 18 | if not done: 19 | total_stats += stats 20 | else: 21 | return total_stats 22 | 23 | 24 | def cal_pkt_transmit_time_ms(header_bytes, payload_bytes, bw_kps): 25 | return (header_bytes + payload_bytes) * 8 / bw_kps 26 | 27 | 28 | def get_info_from_trace(trace_file): 29 | with open(trace_file, 'r') as f: 30 | return json.loads(f.read()) 31 | 32 | 33 | def get_abs_path_by_name(trace_name): 34 | trace_path = os.path.join( 35 | os.path.dirname(__file__), 36 | "data/rtt", 37 | trace_name) 38 | return trace_path 39 | 40 | 41 | def single_rtt_available(trace_path): 42 | total_stats = get_gym_stats(trace_path) 43 | assert (total_stats) 44 | 45 | for stats in total_stats: 46 | assert (isinstance(stats, dict)) 47 | 48 | 49 | def single_rtt_persistence(trace_path): 50 | # get information from trace 51 | trace_data = get_info_from_trace(trace_path) 52 | trace_pattern = trace_data["uplink"]["trace_pattern"] 53 | one_way_delay = trace_pattern[0]["rtt"] / 2 54 | bandwidth_kbps = trace_pattern[0]["capacity"] 55 | 56 | total_stats = get_gym_stats(trace_path) 57 | assert (total_stats) 58 | 59 | for status in total_stats: 60 | transmission_delay = cal_pkt_transmit_time_ms(status["header_length"], 61 | status["payload_size"], bw_kps=bandwidth_kbps) 62 | predict_arrival_time_ms = status["send_time_ms"] + one_way_delay + transmission_delay 63 | assert abs(status["arrival_time_ms"] - predict_arrival_time_ms) <= ERROR * status["arrival_time_ms"] 64 | 65 | 66 | def single_rtt_dynamically(trace_path, run_times=1): 67 | # get information from trace 68 | trace_data = get_info_from_trace(trace_path) 69 | trace_pattern = trace_data["uplink"]["trace_pattern"] 70 | one_way_delay_list = sorted([item["rtt"] / 2 for item in trace_pattern]) 71 | rtt_sample_cnt = [0] * len(one_way_delay_list) 72 | bandwidth_kbps = trace_pattern[0]["capacity"] 73 | duration_time_ms = sum([item["duration"] for item in trace_pattern]) * run_times 74 | 75 | total_stats = get_gym_stats(trace_path, duration_time_ms=duration_time_ms) 76 | assert (total_stats) 77 | 78 | for status in total_stats: 79 | transmission_delay = cal_pkt_transmit_time_ms(status["header_length"], 80 | status["payload_size"], bw_kps=bandwidth_kbps) 81 | actual_delay = status["arrival_time_ms"] - status["send_time_ms"] - transmission_delay 82 | rtt_in_trace = False 83 | for i in range(len(one_way_delay_list)): 84 | if abs(actual_delay - one_way_delay_list[i]) <= ERROR * one_way_delay_list[i]: 85 | rtt_sample_cnt[i] += 1 86 | rtt_in_trace = True 87 | 88 | assert rtt_in_trace == True, "actual rtt not exist in trace" 89 | 90 | for i in range(len(rtt_sample_cnt)): 91 | assert rtt_sample_cnt[i] > 0 92 | if i: 93 | assert abs(rtt_sample_cnt[i-1] - rtt_sample_cnt[i]) <= ERROR * rtt_sample_cnt[i] 94 | 95 | 96 | def test_rtt_available(): 97 | traces_name = ["trace_rtt_200.json", "trace_rtt_400.json", "trace_rtt_600.json"] 98 | for trace in traces_name: 99 | trace_path = get_abs_path_by_name(trace) 100 | single_rtt_available(trace_path) 101 | 102 | 103 | def test_rtt_persistence(): 104 | traces_name = ["trace_rtt_200.json", "trace_rtt_400.json", "trace_rtt_600.json"] 105 | for trace in traces_name: 106 | trace_path = get_abs_path_by_name(trace) 107 | single_rtt_persistence(trace_path) 108 | 109 | 110 | def test_rtt_dynamically(): 111 | traces_name = ["trace_rtt_pattern_2.json", "trace_rtt_pattern_3.json", "trace_rtt_pattern_4.json"] 112 | for trace in traces_name: 113 | trace_path = get_abs_path_by_name(trace) 114 | single_rtt_dynamically(trace_path, run_times=20) 115 | -------------------------------------------------------------------------------- /alphartc_gym/tests/test_gym_stability.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from alphartc_gym import gym 5 | from alphartc_gym.utils.packet_info import PacketInfo 6 | from alphartc_gym.utils.packet_record import PacketRecord 7 | import os 8 | import json 9 | 10 | 11 | GROUND_TRUTH = 300000 12 | ERROR = 0.2 13 | 14 | def check_result(result, except_min, except_max, info): 15 | assert result >= except_min, \ 16 | f"{info}={result}, less than minimal except {except_min}" 17 | assert result <= except_max, \ 18 | f"{info}={result}, larger than maximal except {except_max}" 19 | 20 | def gym_single_test(trace_path, load_ratio): 21 | g = gym.Gym("test_gym") 22 | g.reset(trace_path=trace_path, report_interval_ms=60, duration_time_ms=0) 23 | packet_record = PacketRecord() 24 | packet_record.reset() 25 | 26 | while True: 27 | packet_list, done = g.step(GROUND_TRUTH * load_ratio) 28 | if not done: 29 | for pkt in packet_list: 30 | packet_info = PacketInfo() 31 | packet_info.payload_type = pkt["payload_type"] 32 | packet_info.ssrc = pkt["ssrc"] 33 | packet_info.sequence_number = pkt["sequence_number"] 34 | packet_info.send_timestamp = pkt["send_time_ms"] 35 | packet_info.receive_timestamp = pkt["arrival_time_ms"] 36 | packet_info.padding_length = pkt["padding_length"] 37 | packet_info.header_length = pkt["header_length"] 38 | packet_info.payload_size = pkt["payload_size"] 39 | packet_info.bandwidth_prediction = GROUND_TRUTH * load_ratio 40 | packet_record.on_receive(packet_info) 41 | else: 42 | break 43 | 44 | # Check whole receiving rate at the end 45 | receiving_rate = packet_record.calculate_receiving_rate() 46 | check_result(receiving_rate, GROUND_TRUTH * min(1, load_ratio) * (1 - ERROR), 47 | GROUND_TRUTH * min(1, load_ratio * (1 + ERROR)), 'receiving_rate') 48 | 49 | def test_gym_stability(): 50 | g = gym.Gym("test_gym") 51 | trace_path = os.path.join( 52 | os.path.dirname(__file__), 53 | "data", 54 | "trace_300k.json") 55 | gym_single_test(trace_path, 0.75) 56 | gym_single_test(trace_path, 1.25) 57 | 58 | if __name__ == "__main__": 59 | test_gym_stability() -------------------------------------------------------------------------------- /alphartc_gym/utils/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | -------------------------------------------------------------------------------- /alphartc_gym/utils/packet_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | class PacketInfo: 5 | def __init__(self): 6 | self.payload_type = None 7 | self.sequence_number = None # int 8 | self.send_timestamp = None # int, ms 9 | self.ssrc = None # int 10 | self.padding_length = None # int, B 11 | self.header_length = None # int, B 12 | self.receive_timestamp = None # int, ms 13 | self.payload_size = None # int, B 14 | self.bandwidth_prediction = None # int, bps 15 | 16 | def __str__(self): 17 | return ( 18 | f"receive_timestamp: { self.receive_timestamp }ms" 19 | f", send_timestamp: { self.send_timestamp }ms" 20 | f", payload_size: { self.payload_size }B" 21 | ) 22 | -------------------------------------------------------------------------------- /alphartc_gym/utils/packet_record.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import numpy as np 5 | 6 | 7 | class PacketRecord: 8 | # feature_interval can be modified 9 | def __init__(self, base_delay_ms=200): 10 | self.base_delay_ms = base_delay_ms 11 | self.reset() 12 | 13 | def reset(self): 14 | self.packet_num = 0 15 | self.packet_list = [] # ms 16 | self.last_seqNo = {} 17 | self.timer_delta = None # ms 18 | self.min_seen_delay = self.base_delay_ms # ms 19 | # ms, record the rtime of the last packet in last interval, 20 | self.last_interval_rtime = None 21 | 22 | def clear(self): 23 | self.packet_num = 0 24 | if self.packet_list: 25 | self.last_interval_rtime = self.packet_list[-1]['timestamp'] 26 | self.packet_list = [] 27 | 28 | def on_receive(self, packet_info): 29 | assert (len(self.packet_list) == 0 30 | or packet_info.receive_timestamp 31 | >= self.packet_list[-1]['timestamp']), \ 32 | "The incoming packets receive_timestamp disordered" 33 | 34 | # Calculate the loss count 35 | loss_count = 0 36 | if packet_info.ssrc in self.last_seqNo: 37 | loss_count = max(0, 38 | packet_info.sequence_number - self.last_seqNo[packet_info.ssrc] - 1) 39 | self.last_seqNo[packet_info.ssrc] = packet_info.sequence_number 40 | 41 | # Calculate packet delay 42 | if self.timer_delta is None: 43 | # shift delay of the first packet to base delay 44 | self.timer_delta = self.base_delay_ms - \ 45 | (packet_info.receive_timestamp - packet_info.send_timestamp) 46 | delay = self.timer_delta + \ 47 | (packet_info.receive_timestamp - packet_info.send_timestamp) 48 | self.min_seen_delay = min(delay, self.min_seen_delay) 49 | 50 | # Check the last interval rtime 51 | if self.last_interval_rtime is None: 52 | self.last_interval_rtime = packet_info.receive_timestamp 53 | 54 | # Record result in current packet 55 | packet_result = { 56 | 'timestamp': packet_info.receive_timestamp, # ms 57 | 'delay': delay, # ms 58 | 'payload_byte': packet_info.payload_size, # B 59 | 'loss_count': loss_count, # p 60 | 'bandwidth_prediction': packet_info.bandwidth_prediction # bps 61 | } 62 | self.packet_list.append(packet_result) 63 | self.packet_num += 1 64 | 65 | def _get_result_list(self, interval, key): 66 | if self.packet_num == 0: 67 | return [] 68 | 69 | result_list = [] 70 | if interval == 0: 71 | interval = self.packet_list[-1]['timestamp'] -\ 72 | self.last_interval_rtime 73 | start_time = self.packet_list[-1]['timestamp'] - interval 74 | index = self.packet_num - 1 75 | while index >= 0 and self.packet_list[index]['timestamp'] > start_time: 76 | result_list.append(self.packet_list[index][key]) 77 | index -= 1 78 | return result_list 79 | 80 | def calculate_average_delay(self, interval=0): 81 | ''' 82 | Calulate the average delay in the last interval time, 83 | interval=0 means based on the whole packets 84 | The unit of return value: ms 85 | ''' 86 | delay_list = self._get_result_list(interval=interval, key='delay') 87 | if delay_list: 88 | return np.mean(delay_list) - self.base_delay_ms 89 | else: 90 | return 0 91 | 92 | def calculate_loss_ratio(self, interval=0): 93 | ''' 94 | Calulate the loss ratio in the last interval time, 95 | interval=0 means based on the whole packets 96 | The unit of return value: packet/packet 97 | ''' 98 | loss_list = self._get_result_list(interval=interval, key='loss_count') 99 | if loss_list: 100 | loss_num = np.sum(loss_list) 101 | received_num = len(loss_list) 102 | return loss_num / (loss_num + received_num) 103 | else: 104 | return 0 105 | 106 | def calculate_receiving_rate(self, interval=0): 107 | ''' 108 | Calulate the receiving rate in the last interval time, 109 | interval=0 means based on the whole packets 110 | The unit of return value: bps 111 | ''' 112 | received_size_list = self._get_result_list(interval=interval, key='payload_byte') 113 | if received_size_list: 114 | received_nbytes = np.sum(received_size_list) 115 | if interval == 0: 116 | interval = self.packet_list[-1]['timestamp'] -\ 117 | self.last_interval_rtime 118 | return received_nbytes * 8 / interval * 1000 119 | else: 120 | return 0 121 | 122 | def calculate_latest_prediction(self): 123 | ''' 124 | The unit of return value: bps 125 | ''' 126 | if self.packet_num > 0: 127 | return self.packet_list[-1]['bandwidth_prediction'] 128 | else: 129 | return 0 -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | branches: 8 | include: 9 | - "*" 10 | 11 | 12 | pool: 13 | vmImage: 'ubuntu-latest' 14 | 15 | 16 | steps: 17 | - checkout: self 18 | 19 | - script: make init 20 | displayName: 'Build environment' 21 | 22 | - script: make sync 23 | displayName: 'Sync dependencies' 24 | 25 | - script: make gym 26 | displayName: 'Build gym' 27 | 28 | - script: | 29 | sudo apt install -qq -y libzmq5 python3 python3-pip 30 | python3 -m pip install -r requirements.txt 31 | displayName: 'Install runtime dependencies' 32 | 33 | - script: python3 -m pytest alphartc_gym 34 | displayName: 'Run test' 35 | 36 | - publish: $(System.DefaultWorkingDirectory)/target 37 | continueOnError: true 38 | artifact: target 39 | displayName: "Archive alphartc gym" 40 | 41 | - script: tar -cvzf target.tar.gz target 42 | displayName: 'Compress target' 43 | 44 | - task: GitHubRelease@0 45 | inputs: 46 | gitHubConnection: 'Pterosaur (1)' 47 | repositoryName: '$(Build.Repository.Name)' 48 | action: 'create' 49 | tagSource: manual 50 | tag: $(Build.BuildNumber) 51 | title: target 52 | assets: 'target.tar.gz' 53 | changeLogCompareToRelease: 'lastFullRelease' 54 | changeLogType: 'commitBased' 55 | displayName: "Release target" 56 | -------------------------------------------------------------------------------- /dockers/Dockerfile.compile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | sudo git python3 libglib2.0-dev build-essential \ 5 | libgtest-dev clang nlohmann-json-dev libzmq3-dev \ 6 | libboost-dev 7 | 8 | # Add ONL user 9 | ARG USER=onl 10 | ARG UID 11 | ARG GUID 12 | 13 | RUN groupadd -f -r -g ${GUID} g${USER} 14 | RUN useradd ${USER} -l -u ${UID} -g ${GUID} -m -s /bin/bash 15 | RUN echo "${USER}:${USER}" | chpasswd 16 | RUN adduser ${USER} sudo 17 | USER ${USER} 18 | 19 | # Download NS3 20 | ARG WORK_DIR=/home/${USER} 21 | ARG NS_DIR=${NS_ALLINONE}/${NS_VERSION} 22 | 23 | WORKDIR ${NS_DIR} 24 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/gym_connector.cc: -------------------------------------------------------------------------------- 1 | #include "gym_connector.h" 2 | 3 | #include "ns3/simulator.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace webrtc; 10 | 11 | constexpr char kBandwidthQueuePrefix[] = "/bandwidth_"; 12 | constexpr char kStatsQueuePrefix[] = "/stats_"; 13 | constexpr char kZmqTypePrefix[] = "ipc:///tmp/"; 14 | constexpr char kGymExitFlag[] = "Bye"; 15 | constexpr double kPacingFactor = 2.5f; 16 | 17 | GymConnector::GymConnector( 18 | const std::string &gym_id, 19 | std::uint64_t report_interval_ms, 20 | BandwidthType init_bandwidth) : 21 | current_bandwidth_(init_bandwidth), 22 | report_interval_ms_(report_interval_ms), 23 | gym_id_(gym_id), 24 | zmq_sock_(zmq_ctx_, zmq::socket_type::rep), 25 | zmq_wait_reply_(false) { 26 | zmq_sock_.bind(kZmqTypePrefix + gym_id_); 27 | } 28 | 29 | GymConnector::~GymConnector() { 30 | if (zmq_wait_reply_) { 31 | const std::string exit_flag(kGymExitFlag); 32 | zmq_sock_.send(exit_flag.c_str(), exit_flag.length()); 33 | } 34 | zmq_sock_.unbind(kZmqTypePrefix + gym_id_); 35 | } 36 | 37 | void GymConnector::Step(std::uint64_t delay_ms) { 38 | zmq::message_t msg; 39 | zmq_sock_.recv(&msg); 40 | BandwidthType bandwidth; 41 | std::string bandwidth_str(static_cast(msg.data()), msg.size()); 42 | try { 43 | bandwidth = boost::lexical_cast(bandwidth_str); 44 | } 45 | catch(const boost::bad_lexical_cast& e) 46 | { 47 | const std::string error_msg = "Wrong bandwidth " + bandwidth_str; 48 | zmq_sock_.send(error_msg.c_str(), error_msg.length()); 49 | ns3::Simulator::Stop(); 50 | return; 51 | } 52 | SetBandwidth(bandwidth); 53 | zmq_wait_reply_ = true; 54 | ns3::Simulator::Schedule(ns3::MilliSeconds(report_interval_ms_ + delay_ms), &GymConnector::ReportStats, this); 55 | } 56 | 57 | void GymConnector::ReportStats() { 58 | auto stats = ConsumeStats(); 59 | nlohmann::json j = stats; 60 | const auto json_str = j.dump(); 61 | zmq_sock_.send(json_str.c_str(), json_str.length()); 62 | zmq_wait_reply_ = false; 63 | ns3::Simulator::ScheduleNow(&GymConnector::Step, this, 0); 64 | } 65 | 66 | void GymConnector::SetBandwidth(BandwidthType bandwidth) { 67 | if (bandwidth == current_bandwidth_) { 68 | return; 69 | } 70 | { 71 | std::unique_lock guard(mutex_bandiwidth_); 72 | current_bandwidth_ = bandwidth; 73 | } 74 | } 75 | 76 | NetworkControlUpdate GymConnector::GetNetworkControlUpdate(const Timestamp& at_time) const { 77 | BandwidthType current_bandwidth = {0}; 78 | 79 | { 80 | std::shared_lock guard(mutex_bandiwidth_); 81 | current_bandwidth = current_bandwidth_; 82 | } 83 | 84 | NetworkControlUpdate update; 85 | DataRate target_rate = DataRate::BitsPerSec(current_bandwidth); 86 | 87 | update.target_rate = TargetTransferRate(); 88 | update.target_rate->network_estimate.at_time = at_time; 89 | update.target_rate->network_estimate.bandwidth = target_rate; 90 | update.target_rate->network_estimate.loss_rate_ratio = 0; 91 | update.target_rate->network_estimate.round_trip_time = TimeDelta::Millis(0); 92 | update.target_rate->network_estimate.bwe_period = TimeDelta::Seconds(3); 93 | update.target_rate->at_time = at_time; 94 | update.target_rate->target_rate = target_rate; 95 | 96 | update.pacer_config = PacerConfig(); 97 | update.pacer_config->at_time = at_time; 98 | update.pacer_config->time_window = TimeDelta::Seconds(1); 99 | update.pacer_config->data_window = kPacingFactor * target_rate * update.pacer_config->time_window; 100 | update.pacer_config->pad_window = DataRate::BitsPerSec(0) * update.pacer_config->time_window; 101 | 102 | return update; 103 | } 104 | 105 | void GymConnector::ProduceStates( 106 | int64_t arrival_time_ms, 107 | size_t payload_size, 108 | const RTPHeader& header, 109 | const PacketResult& packet_result) { 110 | 111 | nlohmann::json j; 112 | j["send_time_ms"] = packet_result.sent_packet.send_time.ms(); 113 | j["arrival_time_ms"] = packet_result.receive_time.ms(); 114 | j["payload_type"] = header.payloadType; 115 | j["sequence_number"] = header.sequenceNumber; 116 | j["ssrc"] = header.ssrc; 117 | j["padding_length"] = header.paddingLength; 118 | j["header_length"] = header.headerLength; 119 | j["payload_size"] = payload_size; 120 | 121 | // const std::string stats = j.dump(); 122 | { 123 | std::unique_lock guard(mutex_stats_); 124 | stats_.push_back(j); 125 | } 126 | } 127 | 128 | std::list GymConnector::ConsumeStats() { 129 | std::list stats; 130 | { 131 | std::unique_lock guard(mutex_stats_); 132 | std::swap(stats, stats_); 133 | } 134 | return stats; 135 | } 136 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/gym_connector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "api/transport/network_control.h" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class GymConnector { 15 | public: 16 | using BandwidthType = std::uint32_t; 17 | 18 | GymConnector( 19 | const std::string &gym_id = "gym", 20 | std::uint64_t report_interval_ms = 60, 21 | BandwidthType init_bandwidth = 0); 22 | 23 | virtual ~GymConnector(); 24 | 25 | void Step(std::uint64_t delay_ms = 0); 26 | 27 | void ReportStats(); 28 | 29 | void SetBandwidth(BandwidthType bandwidth); 30 | 31 | webrtc::NetworkControlUpdate GetNetworkControlUpdate(const webrtc::Timestamp & at_time) const; 32 | 33 | void ProduceStates( 34 | int64_t arrival_time_ms, 35 | size_t payload_size, 36 | const webrtc::RTPHeader& header, 37 | const webrtc::PacketResult& packet_result); 38 | 39 | std::list ConsumeStats(); 40 | 41 | private: 42 | 43 | BandwidthType current_bandwidth_; 44 | mutable std::shared_timed_mutex mutex_bandiwidth_; 45 | std::list stats_; 46 | std::mutex mutex_stats_; 47 | 48 | const std::uint64_t report_interval_ms_; 49 | 50 | const std::string gym_id_; 51 | zmq::context_t zmq_ctx_; 52 | zmq::socket_t zmq_sock_; 53 | bool zmq_wait_reply_; 54 | }; 55 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/network_controller_proxy.cc: -------------------------------------------------------------------------------- 1 | #include "network_controller_proxy.h" 2 | 3 | using namespace webrtc; 4 | 5 | NetworkControllerProxy::NetworkControllerProxy( 6 | const GymConnector &conn) : 7 | gym_conn_(conn) { 8 | } 9 | 10 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnNetworkAvailability(webrtc::NetworkAvailability msg) { 11 | return GetUpdate(msg.at_time); 12 | } 13 | 14 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnNetworkRouteChange(webrtc::NetworkRouteChange msg) { 15 | return GetUpdate(msg.at_time); 16 | } 17 | 18 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnProcessInterval(webrtc::ProcessInterval msg) { 19 | return GetUpdate(msg.at_time); 20 | } 21 | 22 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnRemoteBitrateReport(webrtc::RemoteBitrateReport msg) { 23 | return GetUpdate(msg.receive_time); 24 | } 25 | 26 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnRoundTripTimeUpdate(webrtc::RoundTripTimeUpdate msg) { 27 | return GetUpdate(msg.receive_time); 28 | } 29 | 30 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnSentPacket(webrtc::SentPacket sent_packet) { 31 | return GetUpdate(sent_packet.send_time); 32 | } 33 | 34 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnReceivedPacket(webrtc::ReceivedPacket received_packet) { 35 | return GetUpdate(received_packet.receive_time); 36 | } 37 | 38 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnStreamsConfig(webrtc::StreamsConfig msg) { 39 | return GetUpdate(msg.at_time); 40 | } 41 | 42 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnTargetRateConstraints(webrtc::TargetRateConstraints constraints) { 43 | return GetUpdate(constraints.at_time); 44 | } 45 | 46 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnTransportLossReport(webrtc::TransportLossReport msg) { 47 | return GetUpdate(msg.receive_time); 48 | } 49 | 50 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnTransportPacketsFeedback(webrtc::TransportPacketsFeedback report) { 51 | return GetUpdate(report.feedback_time); 52 | } 53 | 54 | webrtc::NetworkControlUpdate NetworkControllerProxy::OnNetworkStateEstimate(webrtc::NetworkStateEstimate msg) { 55 | return GetUpdate(msg.update_time); 56 | } 57 | 58 | webrtc::NetworkControlUpdate NetworkControllerProxy::GetUpdate(webrtc::Timestamp at_time) const { 59 | return gym_conn_.GetNetworkControlUpdate(at_time); 60 | } 61 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/network_controller_proxy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "api/transport/network_control.h" 4 | #include "gym_connector.h" 5 | 6 | class NetworkControllerProxy : public webrtc::NetworkControllerInterface 7 | { 8 | public: 9 | NetworkControllerProxy(const GymConnector &conn); 10 | 11 | // Called when network availabilty changes. 12 | webrtc::NetworkControlUpdate OnNetworkAvailability(webrtc::NetworkAvailability msg) override; 13 | 14 | // Called when the receiving or sending endpoint changes address. 15 | webrtc::NetworkControlUpdate OnNetworkRouteChange(webrtc::NetworkRouteChange msg) override; 16 | 17 | // Called periodically with a periodicy as specified by 18 | // NetworkControllerFactoryInterface::GetProcessInterval. 19 | webrtc::NetworkControlUpdate OnProcessInterval(webrtc::ProcessInterval msg) override; 20 | 21 | // Called when remotely calculated bitrate is received. 22 | webrtc::NetworkControlUpdate OnRemoteBitrateReport(webrtc::RemoteBitrateReport msg) override; 23 | 24 | // Called round trip time has been calculated by protocol specific mechanisms. 25 | webrtc::NetworkControlUpdate OnRoundTripTimeUpdate(webrtc::RoundTripTimeUpdate msg) override; 26 | 27 | // Called when a packet is sent on the network. 28 | webrtc::NetworkControlUpdate OnSentPacket(webrtc::SentPacket sent_packet) override; 29 | 30 | // Called when a packet is received from the remote client. 31 | webrtc::NetworkControlUpdate OnReceivedPacket(webrtc::ReceivedPacket received_packet) override; 32 | 33 | // Called when the stream specific configuration has been updated. 34 | webrtc::NetworkControlUpdate OnStreamsConfig(webrtc::StreamsConfig msg) override; 35 | 36 | // Called when target transfer rate constraints has been changed. 37 | webrtc::NetworkControlUpdate OnTargetRateConstraints(webrtc::TargetRateConstraints constraints) override; 38 | 39 | // Called when a protocol specific calculation of packet loss has been made. 40 | webrtc::NetworkControlUpdate OnTransportLossReport(webrtc::TransportLossReport msg) override; 41 | 42 | // Called with per packet feedback regarding receive time. 43 | webrtc::NetworkControlUpdate OnTransportPacketsFeedback(webrtc::TransportPacketsFeedback report) override; 44 | 45 | // Called with network state estimate updates. 46 | webrtc::NetworkControlUpdate OnNetworkStateEstimate(webrtc::NetworkStateEstimate msg) override; 47 | 48 | private: 49 | webrtc::NetworkControlUpdate GetUpdate(webrtc::Timestamp at_time) const; 50 | 51 | const GymConnector &gym_conn_; 52 | }; 53 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/network_controller_proxy_factory.cc: -------------------------------------------------------------------------------- 1 | #include "network_controller_proxy_factory.h" 2 | 3 | using namespace webrtc; 4 | 5 | NetworkControllerProxyFactory::NetworkControllerProxyFactory(const GymConnector &conn) : 6 | gym_conn_(conn) { 7 | } 8 | 9 | std::unique_ptr NetworkControllerProxyFactory::Create( 10 | NetworkControllerConfig config) { 11 | return std::make_unique(gym_conn_); 12 | } 13 | 14 | TimeDelta NetworkControllerProxyFactory::GetProcessInterval() const { 15 | const int64_t kUpdateIntervalMs = 25; 16 | return TimeDelta::Millis(kUpdateIntervalMs); 17 | } 18 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/network_controller_proxy_factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "network_controller_proxy.h" 4 | #include "gym_connector.h" 5 | 6 | #include "api/transport/network_control.h" 7 | 8 | class NetworkControllerProxyFactory : public webrtc::NetworkControllerFactoryInterface { 9 | public: 10 | NetworkControllerProxyFactory(const GymConnector &conn); 11 | 12 | // Used to create a new network controller, requires an observer to be 13 | // provided to handle callbacks. 14 | std::unique_ptr Create( 15 | webrtc::NetworkControllerConfig config) override; 16 | 17 | // Returns the interval by which the network controller expects 18 | // OnProcessInterval calls. 19 | virtual webrtc::TimeDelta GetProcessInterval() const override; 20 | 21 | private: 22 | const GymConnector &gym_conn_; 23 | }; 24 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/network_estimator_proxy.cc: -------------------------------------------------------------------------------- 1 | #include "network_estimator_proxy.h" 2 | 3 | #include "ns3/simulator.h" 4 | 5 | using namespace webrtc; 6 | 7 | NetworkStateEstimatorProxy::NetworkStateEstimatorProxy(GymConnector &conn) : gym_conn_(conn) { 8 | } 9 | 10 | absl::optional NetworkStateEstimatorProxy::GetCurrentEstimate() { 11 | return absl::optional(); 12 | } 13 | 14 | void NetworkStateEstimatorProxy::OnTransportPacketsFeedback(const TransportPacketsFeedback& feedback) { 15 | } 16 | 17 | void NetworkStateEstimatorProxy::OnReceivedPacket(const PacketResult& packet_result) { 18 | } 19 | 20 | void NetworkStateEstimatorProxy::OnRouteChange(const NetworkRouteChange& route_change) { 21 | } 22 | 23 | void NetworkStateEstimatorProxy::OnReceivedPacketDetail( 24 | int64_t arrival_time_ms, 25 | size_t payload_size, 26 | const RTPHeader& header, 27 | const PacketResult& packet_result) { 28 | gym_conn_.ProduceStates(arrival_time_ms, payload_size, header, packet_result); 29 | } 30 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/network_estimator_proxy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gym_connector.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "api/transport/network_control.h" 10 | #include "api/transport/network_types.h" 11 | #include "api/units/data_rate.h" 12 | #include "absl/types/optional.h" 13 | 14 | // This class is for rate control, you can add your model 15 | // to control the WebRTC sending bitrate. 16 | class NetworkStateEstimatorProxy : public webrtc::NetworkStateEstimator { 17 | public: 18 | NetworkStateEstimatorProxy(GymConnector &conn); 19 | // Gets the current best estimate according to the estimator. 20 | absl::optional GetCurrentEstimate() override; 21 | // Called with per packet feedback regarding receive time. 22 | // Used when the NetworkStateEstimator runs in the sending endpoint. 23 | void OnTransportPacketsFeedback(const webrtc::TransportPacketsFeedback& feedback) override; 24 | // Called with per packet feedback regarding receive time. 25 | // Used when the NetworkStateEstimator runs in the receiving endpoint. 26 | void OnReceivedPacket(const webrtc::PacketResult& packet_result) override; 27 | // Called when the receiving or sending endpoint changes address. 28 | void OnRouteChange(const webrtc::NetworkRouteChange& route_change) override; 29 | void OnReceivedPacketDetail(int64_t arrival_time_ms, 30 | size_t payload_size, 31 | const webrtc::RTPHeader& header, 32 | const webrtc::PacketResult& packet_result) override; 33 | ~NetworkStateEstimatorProxy() override {} 34 | 35 | private: 36 | GymConnector &gym_conn_; 37 | }; 38 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/network_estimator_proxy_factory.cc: -------------------------------------------------------------------------------- 1 | #include "network_estimator_proxy_factory.h" 2 | #include "network_estimator_proxy.h" 3 | 4 | using namespace webrtc; 5 | 6 | std::unique_ptr NetworkStateEstimatorProxyFactory::Create( 7 | const WebRtcKeyValueConfig* key_value_config) { 8 | return std::make_unique(gym_conn_); 9 | } 10 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/network_estimator_proxy_factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gym_connector.h" 4 | 5 | #include "api/transport/network_control.h" 6 | #include "api/transport/network_types.h" 7 | 8 | #include 9 | 10 | class NetworkStateEstimatorProxyFactory : public webrtc::NetworkStateEstimatorFactory { 11 | public: 12 | NetworkStateEstimatorProxyFactory(GymConnector &conn) : gym_conn_(conn) { 13 | } 14 | 15 | std::unique_ptr Create( 16 | const webrtc::WebRtcKeyValueConfig* key_value_config) override; 17 | 18 | ~NetworkStateEstimatorProxyFactory() override {} 19 | 20 | private: 21 | GymConnector &gym_conn_; 22 | }; -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/trace_player.cc: -------------------------------------------------------------------------------- 1 | #include "trace_player.h" 2 | 3 | #include "ns3/simulator.h" 4 | #include "ns3/string.h" 5 | #include "ns3/error-model.h" 6 | #include "ns3/double.h" 7 | #include "ns3/pointer.h" 8 | #include "ns3/channel.h" 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | using namespace std; 16 | using namespace nlohmann; 17 | using namespace boost; 18 | using namespace ns3; 19 | 20 | TracePlayer::TracePlayer( 21 | const string &trace_path, 22 | NodeContainer &nodes) : 23 | source_file_(trace_path), 24 | nodes_(nodes) { 25 | LoadTrace(); 26 | PlayTrace(); 27 | } 28 | 29 | std::uint64_t TracePlayer::GetTotalDuration() const { 30 | std::uint64_t total_duration = 0; 31 | for (const auto &trace: traces_) { 32 | total_duration += trace.duration_ms_; 33 | } 34 | return total_duration; 35 | } 36 | 37 | void TracePlayer::LoadTrace() { 38 | ifstream f(source_file_); 39 | auto j = json::parse(f); 40 | auto uplink_traces = j["uplink"]["trace_pattern"]; 41 | std::vector traces; 42 | traces.reserve(uplink_traces.size()); 43 | 44 | TraceItem last_available_value; 45 | for (const auto &trace: uplink_traces) { 46 | TraceItem ti; 47 | ti.capacity_ = lexical_cast(trace["capacity"]); 48 | ti.duration_ms_ = lexical_cast(trace["duration"]); 49 | if (trace.find("loss") != trace.end()) { 50 | ti.loss_rate_ = lexical_cast(trace["loss"]); 51 | last_available_value.loss_rate_ = ti.loss_rate_; 52 | } else { 53 | ti.loss_rate_ = last_available_value.loss_rate_; 54 | } 55 | if (trace.find("rtt") != trace.end()) { 56 | ti.rtt_ms_ = lexical_cast(trace["rtt"]); 57 | last_available_value.rtt_ms_ = ti.rtt_ms_; 58 | } else { 59 | ti.rtt_ms_ = last_available_value.rtt_ms_; 60 | } 61 | traces.push_back(std::move(ti)); 62 | } 63 | traces_.swap(traces); 64 | } 65 | 66 | void TracePlayer::PlayTrace(size_t trace_index) { 67 | if (traces_.empty()) { 68 | return; 69 | } 70 | if (trace_index >= traces_.size()) { 71 | trace_index = 0; 72 | } 73 | const auto &trace = traces_[trace_index]; 74 | for (size_t i = 0; i < nodes_.GetN(); i++) { 75 | auto node = nodes_.Get(i); 76 | // set delay in channel 77 | if (trace.rtt_ms_) { 78 | node->GetDevice(0)->GetChannel()->SetAttribute("Delay", StringValue(std::to_string(trace.rtt_ms_.value()/2.0) + "ms")); 79 | } 80 | for (size_t j = 0; j < node->GetNDevices(); j++) { 81 | auto device = 82 | dynamic_cast(PeekPointer(node->GetDevice(j))); 83 | if (device) { 84 | // set loss rate in every device 85 | if (trace.loss_rate_) { 86 | SetLossRate(device, trace.loss_rate_.value()); 87 | } 88 | if (trace.capacity_ == 0) { 89 | SetLossRate(device, 1.0); 90 | } else { 91 | device->SetDataRate(DataRate(trace.capacity_ * 1e3)); 92 | if (!trace.loss_rate_) { 93 | SetLossRate(device, 0.0); 94 | } 95 | } 96 | } 97 | } 98 | } 99 | Simulator::Schedule(MilliSeconds(trace.duration_ms_), &TracePlayer::PlayTrace, this, trace_index + 1); 100 | } 101 | 102 | void TracePlayer::SetLossRate(ns3::PointToPointNetDevice *device, double loss_rate) { 103 | Ptr em = CreateObjectWithAttributes ("RanVar", StringValue("ns3::UniformRandomVariable[Min=0.0|Max=1.0]"), \ 104 | "ErrorRate", DoubleValue (loss_rate), \ 105 | "ErrorUnit", StringValue("ERROR_UNIT_PACKET")); 106 | device->SetAttribute("ReceiveErrorModel", PointerValue (em)); 107 | } 108 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/trace_player.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ns3/point-to-point-net-device.h" 4 | #include "ns3/node-container.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct TraceItem { 12 | std::uint64_t capacity_; 13 | std::uint64_t duration_ms_; 14 | boost::optional loss_rate_; 15 | boost::optional rtt_ms_; 16 | }; 17 | 18 | class TracePlayer { 19 | public: 20 | TracePlayer(const std::string &trace_path, ns3::NodeContainer &nodes); 21 | 22 | std::uint64_t GetTotalDuration() const; 23 | 24 | private: 25 | void LoadTrace(); 26 | 27 | void PlayTrace(size_t trace_index = 0); 28 | 29 | void SetLossRate(ns3::PointToPointNetDevice *device, double loss_rate); 30 | 31 | const std::string source_file_; 32 | std::vector traces_; 33 | ns3::NodeContainer &nodes_; 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /ns-app/scratch/webrtc_test/webrtc-static.cc: -------------------------------------------------------------------------------- 1 | #include "gym_connector.h" 2 | #include "network_estimator_proxy_factory.h" 3 | #include "network_controller_proxy_factory.h" 4 | #include "trace_player.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ns3/webrtc-defines.h" 11 | #include "ns3/core-module.h" 12 | #include "ns3/applications-module.h" 13 | #include "ns3/internet-module.h" 14 | #include "ns3/network-module.h" 15 | #include "ns3/point-to-point-module.h" 16 | #include "ns3/ipv4-global-routing-helper.h" 17 | #include "ns3/traffic-control-module.h" 18 | #include "ns3/log.h" 19 | #include "ns3/ex-webrtc-module.h" 20 | 21 | using namespace ns3; 22 | using namespace std; 23 | 24 | NS_LOG_COMPONENT_DEFINE ("Webrtc-Static"); 25 | 26 | const uint32_t TOPO_DEFAULT_BW = 3000000; 27 | const uint32_t TOPO_DEFAULT_PDELAY =100; 28 | const uint32_t TOPO_DEFAULT_QDELAY =300; 29 | const uint32_t DEFAULT_PACKET_SIZE = 1000; 30 | 31 | static NodeContainer BuildExampleTopo (uint64_t bps, 32 | uint32_t msDelay, 33 | uint32_t msQdelay, 34 | bool enable_random_loss) 35 | { 36 | NodeContainer nodes; 37 | nodes.Create (2); 38 | 39 | PointToPointHelper pointToPoint; 40 | pointToPoint.SetDeviceAttribute ("DataRate", DataRateValue (DataRate (bps))); 41 | pointToPoint.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (msDelay))); 42 | auto bufSize = std::max (DEFAULT_PACKET_SIZE, bps * msQdelay / 8000); 43 | pointToPoint.SetQueue ("ns3::DropTailQueue", 44 | "MaxSize", QueueSizeValue (QueueSize (QueueSizeUnit::BYTES, bufSize))); 45 | 46 | NetDeviceContainer devices = pointToPoint.Install (nodes); 47 | 48 | InternetStackHelper stack; 49 | stack.Install (nodes); 50 | Ipv4AddressHelper address; 51 | std::string nodeip="10.1.1.0"; 52 | address.SetBase (nodeip.c_str(), "255.255.255.0"); 53 | address.Assign (devices); 54 | 55 | // enable tc in ns3.30 56 | TrafficControlHelper tch; 57 | tch.Uninstall (devices); 58 | if(enable_random_loss){ 59 | std::string errorModelType = "ns3::RateErrorModel"; 60 | ObjectFactory factory; 61 | factory.SetTypeId (errorModelType); 62 | Ptr em = factory.Create (); 63 | devices.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em)); 64 | } 65 | return nodes; 66 | } 67 | 68 | template 69 | Ptr CreateApp( 70 | Ptr node, 71 | uint16_t port, 72 | uint64_t start_time_ms, 73 | uint64_t stop_time_ms, 74 | WebrtcSessionManager *manager) { 75 | Ptr app = CreateObject(manager); 76 | node->AddApplication(app); 77 | app->Bind(port); 78 | app->SetStartTime(ns3::MilliSeconds(start_time_ms)); 79 | app->SetStopTime(ns3::MilliSeconds(stop_time_ms)); 80 | return app; 81 | } 82 | 83 | void ConnectApp( 84 | Ptr sender, 85 | Ptr receiver) { 86 | auto sender_addr = 87 | sender->GetNode()->GetObject()->GetAddress(1, 0).GetLocal(); 88 | auto receiver_addr = 89 | receiver->GetNode()->GetObject()->GetAddress(1, 0).GetLocal(); 90 | sender->ConfigurePeer(receiver_addr, receiver->GetBindPort()); 91 | receiver->ConfigurePeer(sender_addr, sender->GetBindPort()); 92 | } 93 | 94 | int main(int argc, char *argv[]){ 95 | LogComponentEnable("WebrtcSender",LOG_LEVEL_ALL); 96 | LogComponentEnable("WebrtcReceiver",LOG_LEVEL_ALL); 97 | // GlobalValue::Bind ("SimulatorImplementationType", StringValue ("ns3::RealtimeSimulatorImpl")); 98 | webrtc_register_clock(); 99 | 100 | uint64_t linkBw = TOPO_DEFAULT_BW; 101 | uint32_t msDelay = TOPO_DEFAULT_PDELAY; 102 | uint32_t msQDelay = TOPO_DEFAULT_QDELAY; 103 | 104 | double loss_rate = 0; 105 | 106 | std::string gym_id("gym"); 107 | std::string trace_path; 108 | std::uint64_t report_interval_ms = 60; 109 | std::uint64_t duration_time_ms = 0; 110 | std::uint32_t video_height = 1080; 111 | std::uint32_t video_width = 1920; 112 | bool standalone_test_only = true; 113 | 114 | CommandLine cmd; 115 | cmd.AddValue("lo", "loss",loss_rate); 116 | cmd.AddValue("gym_id", "gym id should be unique in global system, the default is gym", gym_id); 117 | cmd.AddValue("trace_path", "trace file path", trace_path); 118 | cmd.AddValue("report_interval_ms", "report interval (ms)", report_interval_ms); 119 | cmd.AddValue("duration_time_ms", "duration time (ms), the default is trace log duration", duration_time_ms); 120 | cmd.AddValue("video_height", "video height", video_height); 121 | cmd.AddValue("video_width", "video width", video_width); 122 | cmd.AddValue("standalone_test_only", "standalone test only mode that don't need gym connect", standalone_test_only); 123 | 124 | cmd.Parse (argc, argv); 125 | 126 | bool enable_random_loss=false; 127 | if(loss_rate>0){ 128 | Config::SetDefault ("ns3::RateErrorModel::ErrorRate", DoubleValue (loss_rate)); 129 | Config::SetDefault ("ns3::RateErrorModel::ErrorUnit", StringValue ("ERROR_UNIT_PACKET")); 130 | Config::SetDefault ("ns3::BurstErrorModel::ErrorRate", DoubleValue (loss_rate)); 131 | Config::SetDefault ("ns3::BurstErrorModel::BurstSize", StringValue ("ns3::UniformRandomVariable[Min=1|Max=3]")); 132 | enable_random_loss=true; 133 | } 134 | 135 | NodeContainer nodes = BuildExampleTopo(linkBw, msDelay, msQDelay,enable_random_loss); 136 | 137 | std::unique_ptr trace_player; 138 | if (trace_path.empty() && duration_time_ms == 0) { 139 | duration_time_ms = 5000; 140 | } else if (!trace_path.empty()) { 141 | // Set trace 142 | trace_player = std::make_unique(trace_path, nodes); 143 | if (duration_time_ms == 0) { 144 | duration_time_ms = trace_player->GetTotalDuration(); 145 | } 146 | } 147 | 148 | GymConnector gym_conn(gym_id, report_interval_ms); 149 | if (standalone_test_only) { 150 | gym_conn.SetBandwidth(1e6); 151 | } else { 152 | gym_conn.Step(); 153 | } 154 | 155 | auto cc_factory = std::make_shared(gym_conn); 156 | auto se_factory = std::make_shared(gym_conn); 157 | auto webrtc_manager = std::make_unique(0, duration_time_ms, cc_factory, se_factory); 158 | webrtc_manager->SetFrameHxW(video_height,video_width); 159 | webrtc_manager->CreateClients(); 160 | 161 | uint16_t sendPort=5432; 162 | uint16_t recvPort=5000; 163 | auto sender = CreateApp(nodes.Get(0), sendPort, 0, duration_time_ms, webrtc_manager.get()); 164 | auto receiver = CreateApp(nodes.Get(1), recvPort, 0, duration_time_ms, webrtc_manager.get()); 165 | ConnectApp(sender, receiver); 166 | 167 | Simulator::Stop (MilliSeconds(duration_time_ms + 1)); 168 | Simulator::Run (); 169 | Simulator::Destroy(); 170 | 171 | if (standalone_test_only) { 172 | for (auto &stat : gym_conn.ConsumeStats()) { 173 | std::cout << stat << std::endl; 174 | } 175 | std::cout<<"Simulation ends."< 3 | #if defined(COMPILER_MSVC) 4 | #include 5 | #endif 6 | 7 | namespace basic{ 8 | #define ARCH_CPU_LITTLE_ENDIAN 1 9 | // Returns a value with all bytes in |x| swapped, i.e. reverses the endianness. 10 | inline uint16_t ByteSwap(uint16_t x) { 11 | #if defined(COMPILER_MSVC) 12 | return _byteswap_ushort(x); 13 | #else 14 | return __builtin_bswap16(x); 15 | #endif 16 | } 17 | 18 | inline uint32_t ByteSwap32(uint32_t x) { 19 | #if defined(COMPILER_MSVC) 20 | return _byteswap_ulong(x); 21 | #else 22 | return __builtin_bswap32(x); 23 | #endif 24 | } 25 | 26 | inline uint64_t ByteSwap64(uint64_t x) { 27 | #if defined(COMPILER_MSVC) 28 | return _byteswap_uint64(x); 29 | #else 30 | return __builtin_bswap64(x); 31 | #endif 32 | } 33 | 34 | // Converts the bytes in |x| from network to host order (endianness), and 35 | // returns the result. 36 | inline uint16_t NetToHost16(uint16_t x) { 37 | #if defined(ARCH_CPU_LITTLE_ENDIAN) 38 | return ByteSwap(x); 39 | #else 40 | return x; 41 | #endif 42 | } 43 | inline uint32_t NetToHost32(uint32_t x) { 44 | #if defined(ARCH_CPU_LITTLE_ENDIAN) 45 | return ByteSwap32(x); 46 | #else 47 | return x; 48 | #endif 49 | } 50 | inline uint64_t NetToHost64(uint64_t x) { 51 | #if defined(ARCH_CPU_LITTLE_ENDIAN) 52 | return ByteSwap64(x); 53 | #else 54 | return x; 55 | #endif 56 | } 57 | 58 | // Converts the bytes in |x| from host to network order (endianness), and 59 | // returns the result. 60 | inline uint16_t HostToNet16(uint16_t x) { 61 | #if defined(ARCH_CPU_LITTLE_ENDIAN) 62 | return ByteSwap(x); 63 | #else 64 | return x; 65 | #endif 66 | } 67 | inline uint32_t HostToNet32(uint32_t x) { 68 | #if defined(ARCH_CPU_LITTLE_ENDIAN) 69 | return ByteSwap32(x); 70 | #else 71 | return x; 72 | #endif 73 | } 74 | inline uint64_t HostToNet64(uint64_t x) { 75 | #if defined(ARCH_CPU_LITTLE_ENDIAN) 76 | return ByteSwap64(x); 77 | #else 78 | return x; 79 | #endif 80 | } 81 | } -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/simulated-process-thread.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include "simulated-process-thread.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace webrtc { 17 | namespace { 18 | // Helper function to remove from a std container by value. 19 | template 20 | bool RemoveByValue(C* vec, typename C::value_type val) { 21 | auto it = std::find(vec->begin(), vec->end(), val); 22 | if (it == vec->end()) 23 | return false; 24 | vec->erase(it); 25 | return true; 26 | } 27 | } // namespace 28 | SimulatedProcessThread::SimulatedProcessThread( 29 | sim_time_impl::SimulatedTimeControllerImpl* handler, 30 | absl::string_view name) 31 | : handler_(handler), name_(new char[name.size()]) { 32 | std::copy_n(name.begin(), name.size(), name_); 33 | } 34 | 35 | SimulatedProcessThread::~SimulatedProcessThread() { 36 | handler_->Unregister(this); 37 | delete[] name_; 38 | } 39 | 40 | void SimulatedProcessThread::RunReady(Timestamp at_time) { 41 | CurrentTaskQueueSetter set_current(this); 42 | rtc::CritScope lock(&lock_); 43 | std::vector ready_modules; 44 | for (auto it = delayed_modules_.begin(); 45 | it != delayed_modules_.end() && it->first <= at_time; 46 | it = delayed_modules_.erase(it)) { 47 | for (auto module : it->second) { 48 | ready_modules.push_back(module); 49 | } 50 | } 51 | for (auto* module : ready_modules) { 52 | module->Process(); 53 | delayed_modules_[GetNextTime(module, at_time)].push_back(module); 54 | } 55 | 56 | for (auto it = delayed_tasks_.begin(); 57 | it != delayed_tasks_.end() && it->first <= at_time; 58 | it = delayed_tasks_.erase(it)) { 59 | for (auto& task : it->second) { 60 | queue_.push_back(std::move(task)); 61 | } 62 | } 63 | while (!queue_.empty()) { 64 | std::unique_ptr task = std::move(queue_.front()); 65 | queue_.pop_front(); 66 | lock_.Leave(); 67 | bool should_delete = task->Run(); 68 | RTC_CHECK(should_delete); 69 | lock_.Enter(); 70 | } 71 | RTC_DCHECK(queue_.empty()); 72 | if (!delayed_modules_.empty()) { 73 | next_run_time_ = delayed_modules_.begin()->first; 74 | } else { 75 | next_run_time_ = Timestamp::PlusInfinity(); 76 | } 77 | if (!delayed_tasks_.empty()) { 78 | next_run_time_ = std::min(next_run_time_, delayed_tasks_.begin()->first); 79 | } 80 | } 81 | void SimulatedProcessThread::Start() { 82 | std::vector starting; 83 | { 84 | rtc::CritScope lock(&lock_); 85 | if (process_thread_running_) 86 | return; 87 | process_thread_running_ = true; 88 | starting.swap(stopped_modules_); 89 | } 90 | for (auto& module : starting) 91 | module->ProcessThreadAttached(this); 92 | 93 | Timestamp at_time = handler_->CurrentTime(); 94 | rtc::CritScope lock(&lock_); 95 | for (auto& module : starting) 96 | delayed_modules_[GetNextTime(module, at_time)].push_back(module); 97 | 98 | if (!queue_.empty()) { 99 | next_run_time_ = Timestamp::MinusInfinity(); 100 | } else if (!delayed_modules_.empty()) { 101 | next_run_time_ = delayed_modules_.begin()->first; 102 | } else { 103 | next_run_time_ = Timestamp::PlusInfinity(); 104 | } 105 | } 106 | 107 | void SimulatedProcessThread::Stop() { 108 | std::vector stopping; 109 | { 110 | rtc::CritScope lock(&lock_); 111 | process_thread_running_ = false; 112 | 113 | for (auto& delayed : delayed_modules_) { 114 | for (auto mod : delayed.second) 115 | stopped_modules_.push_back(mod); 116 | } 117 | delayed_modules_.clear(); 118 | 119 | stopping = stopped_modules_; 120 | } 121 | for (auto& module : stopping) 122 | module->ProcessThreadAttached(nullptr); 123 | } 124 | 125 | void SimulatedProcessThread::WakeUp(Module* module) { 126 | rtc::CritScope lock(&lock_); 127 | for (auto it = delayed_modules_.begin(); it != delayed_modules_.end(); ++it) { 128 | if (RemoveByValue(&it->second, module)) 129 | break; 130 | } 131 | Timestamp next_time = GetNextTime(module, handler_->CurrentTime()); 132 | delayed_modules_[next_time].push_back(module); 133 | next_run_time_ = std::min(next_run_time_, next_time); 134 | } 135 | 136 | void SimulatedProcessThread::RegisterModule(Module* module, 137 | const rtc::Location& from) { 138 | module->ProcessThreadAttached(this); 139 | rtc::CritScope lock(&lock_); 140 | if (!process_thread_running_) { 141 | stopped_modules_.push_back(module); 142 | } else { 143 | Timestamp next_time = GetNextTime(module, handler_->CurrentTime()); 144 | delayed_modules_[next_time].push_back(module); 145 | next_run_time_ = std::min(next_run_time_, next_time); 146 | } 147 | } 148 | 149 | void SimulatedProcessThread::DeRegisterModule(Module* module) { 150 | bool modules_running; 151 | { 152 | rtc::CritScope lock(&lock_); 153 | if (!process_thread_running_) { 154 | RemoveByValue(&stopped_modules_, module); 155 | } else { 156 | for (auto& pair : delayed_modules_) { 157 | if (RemoveByValue(&pair.second, module)) 158 | break; 159 | } 160 | } 161 | modules_running = process_thread_running_; 162 | } 163 | if (modules_running) 164 | module->ProcessThreadAttached(nullptr); 165 | } 166 | 167 | void SimulatedProcessThread::PostTask(std::unique_ptr task) { 168 | rtc::CritScope lock(&lock_); 169 | queue_.emplace_back(std::move(task)); 170 | next_run_time_ = Timestamp::MinusInfinity(); 171 | } 172 | 173 | void SimulatedProcessThread::PostDelayedTask(std::unique_ptr task, 174 | uint32_t milliseconds) { 175 | rtc::CritScope lock(&lock_); 176 | Timestamp target_time = 177 | handler_->CurrentTime() + TimeDelta::Millis(milliseconds); 178 | delayed_tasks_[target_time].push_back(std::move(task)); 179 | next_run_time_ = std::min(next_run_time_, target_time); 180 | } 181 | 182 | Timestamp SimulatedProcessThread::GetNextTime(Module* module, 183 | Timestamp at_time) { 184 | CurrentTaskQueueSetter set_current(this); 185 | return at_time + TimeDelta::Millis(module->TimeUntilNextProcess()); 186 | } 187 | 188 | } // namespace webrtc 189 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/simulated-process-thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #pragma once 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "webrtc-simu-controller.h" 19 | 20 | namespace webrtc { 21 | 22 | class SimulatedProcessThread : public ProcessThread, 23 | public sim_time_impl::SimulatedSequenceRunner { 24 | public: 25 | SimulatedProcessThread(sim_time_impl::SimulatedTimeControllerImpl* handler, 26 | absl::string_view name); 27 | virtual ~SimulatedProcessThread(); 28 | void RunReady(Timestamp at_time) override; 29 | 30 | Timestamp GetNextRunTime() const override { 31 | rtc::CritScope lock(&lock_); 32 | return next_run_time_; 33 | } 34 | 35 | TaskQueueBase* GetAsTaskQueue() override { return this; } 36 | 37 | // ProcessThread interface 38 | void Start() override; 39 | void Stop() override; 40 | void WakeUp(Module* module) override; 41 | void RegisterModule(Module* module, const rtc::Location& from) override; 42 | void DeRegisterModule(Module* module) override; 43 | void PostTask(std::unique_ptr task) override; 44 | void PostDelayedTask(std::unique_ptr task, 45 | uint32_t milliseconds) override; 46 | 47 | private: 48 | void Delete() override { 49 | // ProcessThread shouldn't be deleted as a TaskQueue. 50 | RTC_NOTREACHED(); 51 | } 52 | Timestamp GetNextTime(Module* module, Timestamp at_time); 53 | 54 | sim_time_impl::SimulatedTimeControllerImpl* const handler_; 55 | // Using char* to be debugger friendly. 56 | char* name_; 57 | rtc::CriticalSection lock_; 58 | Timestamp next_run_time_ RTC_GUARDED_BY(lock_) = Timestamp::PlusInfinity(); 59 | 60 | std::deque> queue_; 61 | std::map>> delayed_tasks_ 62 | RTC_GUARDED_BY(lock_); 63 | 64 | bool process_thread_running_ RTC_GUARDED_BY(lock_) = false; 65 | std::vector stopped_modules_ RTC_GUARDED_BY(lock_); 66 | std::map> delayed_modules_ 67 | RTC_GUARDED_BY(lock_); 68 | }; 69 | } // namespace webrtc 70 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/simulated-task-queue.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #include "simulated-task-queue.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace webrtc { 16 | 17 | SimulatedTaskQueue::SimulatedTaskQueue( 18 | sim_time_impl::SimulatedTimeControllerImpl* handler, 19 | absl::string_view name) 20 | : handler_(handler), name_(new char[name.size()]) { 21 | std::copy_n(name.begin(), name.size(), name_); 22 | } 23 | 24 | SimulatedTaskQueue::~SimulatedTaskQueue() { 25 | handler_->Unregister(this); 26 | delete[] name_; 27 | } 28 | 29 | void SimulatedTaskQueue::Delete() { 30 | { 31 | rtc::CritScope lock(&lock_); 32 | ready_tasks_.clear(); 33 | delayed_tasks_.clear(); 34 | } 35 | delete this; 36 | } 37 | 38 | void SimulatedTaskQueue::RunReady(Timestamp at_time) { 39 | rtc::CritScope lock(&lock_); 40 | for (auto it = delayed_tasks_.begin(); 41 | it != delayed_tasks_.end() && it->first <= at_time; 42 | it = delayed_tasks_.erase(it)) { 43 | for (auto& task : it->second) { 44 | ready_tasks_.emplace_back(std::move(task)); 45 | } 46 | } 47 | CurrentTaskQueueSetter set_current(this); 48 | while (!ready_tasks_.empty()) { 49 | std::unique_ptr ready = std::move(ready_tasks_.front()); 50 | ready_tasks_.pop_front(); 51 | lock_.Leave(); 52 | bool delete_task = ready->Run(); 53 | if (delete_task) { 54 | ready.reset(); 55 | } else { 56 | ready.release(); 57 | } 58 | lock_.Enter(); 59 | } 60 | if (!delayed_tasks_.empty()) { 61 | next_run_time_ = delayed_tasks_.begin()->first; 62 | } else { 63 | next_run_time_ = Timestamp::PlusInfinity(); 64 | } 65 | } 66 | 67 | void SimulatedTaskQueue::PostTask(std::unique_ptr task) { 68 | rtc::CritScope lock(&lock_); 69 | ready_tasks_.emplace_back(std::move(task)); 70 | next_run_time_ = Timestamp::MinusInfinity(); 71 | } 72 | 73 | void SimulatedTaskQueue::PostDelayedTask(std::unique_ptr task, 74 | uint32_t milliseconds) { 75 | rtc::CritScope lock(&lock_); 76 | Timestamp target_time = 77 | handler_->CurrentTime() + TimeDelta::Millis(milliseconds); 78 | delayed_tasks_[target_time].push_back(std::move(task)); 79 | next_run_time_ = std::min(next_run_time_, target_time); 80 | } 81 | 82 | } // namespace webrtc 83 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/simulated-task-queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #pragma once 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "webrtc-simu-controller.h" 18 | 19 | namespace webrtc { 20 | 21 | class SimulatedTaskQueue : public TaskQueueBase, 22 | public sim_time_impl::SimulatedSequenceRunner { 23 | public: 24 | SimulatedTaskQueue(sim_time_impl::SimulatedTimeControllerImpl* handler, 25 | absl::string_view name); 26 | 27 | ~SimulatedTaskQueue(); 28 | 29 | void RunReady(Timestamp at_time) override; 30 | 31 | Timestamp GetNextRunTime() const override { 32 | rtc::CritScope lock(&lock_); 33 | return next_run_time_; 34 | } 35 | TaskQueueBase* GetAsTaskQueue() override { return this; } 36 | 37 | // TaskQueueBase interface 38 | void Delete() override; 39 | void PostTask(std::unique_ptr task) override; 40 | void PostDelayedTask(std::unique_ptr task, 41 | uint32_t milliseconds) override; 42 | 43 | private: 44 | sim_time_impl::SimulatedTimeControllerImpl* const handler_; 45 | // Using char* to be debugger friendly. 46 | char* name_; 47 | 48 | rtc::CriticalSection lock_; 49 | 50 | std::deque> ready_tasks_ RTC_GUARDED_BY(lock_); 51 | std::map>> delayed_tasks_ 52 | RTC_GUARDED_BY(lock_); 53 | 54 | Timestamp next_run_time_ RTC_GUARDED_BY(lock_) = Timestamp::PlusInfinity(); 55 | }; 56 | 57 | } // namespace webrtc 58 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/simulated-thread.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #include "simulated-thread.h" 11 | 12 | #include 13 | #include 14 | 15 | #include "rtc_base/task_utils/to_queued_task.h" 16 | 17 | namespace webrtc { 18 | namespace { 19 | 20 | // A socket server that does nothing. It's different from NullSocketServer in 21 | // that it does allow sleep/wakeup. This avoids usage of an Event instance which 22 | // otherwise would cause issues with the simulated Yeild behavior. 23 | class DummySocketServer : public rtc::SocketServer { 24 | public: 25 | rtc::Socket* CreateSocket(int family, int type) override { 26 | RTC_NOTREACHED(); 27 | return nullptr; 28 | } 29 | rtc::AsyncSocket* CreateAsyncSocket(int family, int type) override { 30 | RTC_NOTREACHED(); 31 | return nullptr; 32 | } 33 | bool Wait(int cms, bool process_io) override { 34 | RTC_CHECK_EQ(cms, 0); 35 | return true; 36 | } 37 | void WakeUp() override {} 38 | }; 39 | 40 | } // namespace 41 | 42 | SimulatedThread::SimulatedThread( 43 | sim_time_impl::SimulatedTimeControllerImpl* handler, 44 | absl::string_view name, 45 | std::unique_ptr socket_server) 46 | : rtc::Thread(socket_server ? std::move(socket_server) 47 | : std::make_unique()), 48 | handler_(handler), 49 | name_(new char[name.size()]) { 50 | std::copy_n(name.begin(), name.size(), name_); 51 | } 52 | 53 | SimulatedThread::~SimulatedThread() { 54 | handler_->Unregister(this); 55 | delete[] name_; 56 | } 57 | 58 | void SimulatedThread::RunReady(Timestamp at_time) { 59 | CurrentThreadSetter set_current(this); 60 | ProcessMessages(0); 61 | int delay_ms = GetDelay(); 62 | rtc::CritScope lock(&lock_); 63 | if (delay_ms == kForever) { 64 | next_run_time_ = Timestamp::PlusInfinity(); 65 | } else { 66 | next_run_time_ = at_time + TimeDelta::Millis(delay_ms); 67 | } 68 | } 69 | 70 | void SimulatedThread::Send(const rtc::Location& posted_from, 71 | rtc::MessageHandler* phandler, 72 | uint32_t id, 73 | rtc::MessageData* pdata) { 74 | if (IsQuitting()) 75 | return; 76 | rtc::Message msg; 77 | msg.posted_from = posted_from; 78 | msg.phandler = phandler; 79 | msg.message_id = id; 80 | msg.pdata = pdata; 81 | if (IsCurrent()) { 82 | msg.phandler->OnMessage(&msg); 83 | } else { 84 | TaskQueueBase* yielding_from = TaskQueueBase::Current(); 85 | handler_->StartYield(yielding_from); 86 | CurrentThreadSetter set_current(this); 87 | msg.phandler->OnMessage(&msg); 88 | handler_->StopYield(yielding_from); 89 | } 90 | } 91 | 92 | void SimulatedThread::Post(const rtc::Location& posted_from, 93 | rtc::MessageHandler* phandler, 94 | uint32_t id, 95 | rtc::MessageData* pdata, 96 | bool time_sensitive) { 97 | rtc::Thread::Post(posted_from, phandler, id, pdata, time_sensitive); 98 | rtc::CritScope lock(&lock_); 99 | next_run_time_ = Timestamp::MinusInfinity(); 100 | } 101 | 102 | void SimulatedThread::PostDelayed(const rtc::Location& posted_from, 103 | int delay_ms, 104 | rtc::MessageHandler* phandler, 105 | uint32_t id, 106 | rtc::MessageData* pdata) { 107 | rtc::Thread::PostDelayed(posted_from, delay_ms, phandler, id, pdata); 108 | rtc::CritScope lock(&lock_); 109 | next_run_time_ = 110 | std::min(next_run_time_, Timestamp::Millis(rtc::TimeMillis() + delay_ms)); 111 | } 112 | 113 | void SimulatedThread::PostAt(const rtc::Location& posted_from, 114 | int64_t target_time_ms, 115 | rtc::MessageHandler* phandler, 116 | uint32_t id, 117 | rtc::MessageData* pdata) { 118 | rtc::Thread::PostAt(posted_from, target_time_ms, phandler, id, pdata); 119 | rtc::CritScope lock(&lock_); 120 | next_run_time_ = std::min(next_run_time_, Timestamp::Millis(target_time_ms)); 121 | } 122 | 123 | void SimulatedThread::Stop() { 124 | Thread::Quit(); 125 | } 126 | 127 | SimulatedMainThread::SimulatedMainThread( 128 | sim_time_impl::SimulatedTimeControllerImpl* handler) 129 | : SimulatedThread(handler, "main", nullptr), current_setter_(this) {} 130 | 131 | SimulatedMainThread::~SimulatedMainThread() { 132 | // Removes pending tasks in case they keep shared pointer references to 133 | // objects whose destructor expects to run before the Thread destructor. 134 | Stop(); 135 | DoDestroy(); 136 | } 137 | 138 | } // namespace webrtc 139 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/simulated-thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #pragma once 11 | 12 | #include 13 | 14 | #include "webrtc-simu-controller.h" 15 | 16 | namespace webrtc { 17 | 18 | class SimulatedThread : public rtc::Thread, 19 | public sim_time_impl::SimulatedSequenceRunner { 20 | public: 21 | using CurrentThreadSetter = CurrentThreadSetter; 22 | SimulatedThread(sim_time_impl::SimulatedTimeControllerImpl* handler, 23 | absl::string_view name, 24 | std::unique_ptr socket_server); 25 | ~SimulatedThread() override; 26 | 27 | void RunReady(Timestamp at_time) override; 28 | 29 | Timestamp GetNextRunTime() const override { 30 | rtc::CritScope lock(&lock_); 31 | return next_run_time_; 32 | } 33 | 34 | TaskQueueBase* GetAsTaskQueue() override { return this; } 35 | 36 | // Thread interface 37 | void Send(const rtc::Location& posted_from, 38 | rtc::MessageHandler* phandler, 39 | uint32_t id, 40 | rtc::MessageData* pdata) override; 41 | void Post(const rtc::Location& posted_from, 42 | rtc::MessageHandler* phandler, 43 | uint32_t id, 44 | rtc::MessageData* pdata, 45 | bool time_sensitive) override; 46 | void PostDelayed(const rtc::Location& posted_from, 47 | int delay_ms, 48 | rtc::MessageHandler* phandler, 49 | uint32_t id, 50 | rtc::MessageData* pdata) override; 51 | void PostAt(const rtc::Location& posted_from, 52 | int64_t target_time_ms, 53 | rtc::MessageHandler* phandler, 54 | uint32_t id, 55 | rtc::MessageData* pdata) override; 56 | 57 | void Stop() override; 58 | 59 | private: 60 | sim_time_impl::SimulatedTimeControllerImpl* const handler_; 61 | // Using char* to be debugger friendly. 62 | char* name_; 63 | rtc::CriticalSection lock_; 64 | Timestamp next_run_time_ RTC_GUARDED_BY(lock_) = Timestamp::PlusInfinity(); 65 | }; 66 | 67 | class SimulatedMainThread : public SimulatedThread { 68 | public: 69 | explicit SimulatedMainThread( 70 | sim_time_impl::SimulatedTimeControllerImpl* handler); 71 | ~SimulatedMainThread(); 72 | 73 | private: 74 | CurrentThreadSetter current_setter_; 75 | }; 76 | } // namespace webrtc 77 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-clock.cc: -------------------------------------------------------------------------------- 1 | #include "webrtc-clock.h" 2 | 3 | #include "ns3/simulator.h" 4 | #include "ns3/nstime.h" 5 | 6 | #include 7 | 8 | namespace ns3{ 9 | 10 | class ExternalClock:public rtc::ClockInterface{ 11 | public: 12 | ExternalClock(){} 13 | ~ExternalClock() override{} 14 | int64_t TimeNanos() const override{ 15 | return Simulator::Now().GetNanoSeconds(); 16 | } 17 | }; 18 | 19 | static bool webrtc_clock_init=false; 20 | 21 | static ExternalClock external_clock; 22 | 23 | void webrtc_register_clock(){ 24 | if(!webrtc_clock_init){ 25 | rtc::SetClockForTesting(&external_clock); 26 | webrtc_clock_init=true; 27 | } 28 | } 29 | 30 | } 31 | 32 | namespace webrtc{ 33 | 34 | Timestamp WebrtcSimulationClock::CurrentTime() { 35 | return Timestamp::Micros(rtc::TimeMicros()); 36 | } 37 | 38 | NtpTime WebrtcSimulationClock::CurrentNtpTime() { 39 | int64_t now_ms =rtc::TimeMillis(); 40 | uint32_t seconds = (now_ms / 1000) + kNtpJan1970; 41 | uint32_t fractions = 42 | static_cast((now_ms % 1000) * kMagicNtpFractionalUnit / 1000); 43 | return NtpTime(seconds, fractions); 44 | } 45 | 46 | int64_t WebrtcSimulationClock::CurrentNtpInMilliseconds() { 47 | return TimeInMilliseconds() + 1000 * static_cast(kNtpJan1970); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-clock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "rtc_base/time_utils.h" 8 | #include "system_wrappers/include/clock.h" 9 | 10 | namespace ns3{ 11 | 12 | void webrtc_register_clock(); 13 | 14 | } 15 | 16 | namespace webrtc{ 17 | 18 | class WebrtcSimulationClock : public Clock { 19 | public: 20 | explicit WebrtcSimulationClock(){} 21 | ~WebrtcSimulationClock() override{} 22 | 23 | // Return a timestamp relative to some arbitrary source; the source is fixed 24 | // for this clock. 25 | Timestamp CurrentTime() override; 26 | 27 | // Retrieve an NTP absolute timestamp. 28 | NtpTime CurrentNtpTime() override; 29 | 30 | // Converts an NTP timestamp to a millisecond timestamp. 31 | int64_t CurrentNtpInMilliseconds() override; 32 | }; 33 | 34 | } -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-config.cc: -------------------------------------------------------------------------------- 1 | #include "webrtc-config.h" 2 | #include "webrtc-emu-controller.h" 3 | #include "webrtc-simu-controller.h" 4 | 5 | #include "test/scenario/scenario_config.h" 6 | #include "test/scenario/video_frame_matcher.h" 7 | #include "api/transport/network_control.h" 8 | #include "api/transport/network_types.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace ns3{ 18 | 19 | namespace{ 20 | const uint32_t kInitialBitrateKbps = 60; 21 | const webrtc::DataRate kInitialBitrate = webrtc::DataRate::KilobitsPerSec(kInitialBitrateKbps); 22 | const float kDefaultPacingRate = 2.5f; 23 | } 24 | 25 | WebrtcSessionManager::WebrtcSessionManager( 26 | std::uint64_t start_time_ms, 27 | std::uint64_t stop_time_ms, 28 | std::shared_ptr cc_factory, 29 | std::shared_ptr se_factory) 30 | { 31 | video_stream_config_.stream.abs_send_time = true; 32 | call_client_config_.transport.cc_factory = cc_factory.get(); 33 | call_client_config_.transport.se_factory = se_factory.get(); 34 | // time_controller_.reset(new webrtc::EmulationTimeController()); 35 | time_controller_.reset( 36 | new webrtc::SimulationTimeController( 37 | start_time_ms * 1e3, 38 | stop_time_ms * 1e3)); 39 | } 40 | 41 | WebrtcSessionManager::~WebrtcSessionManager() { 42 | if (m_running) { 43 | Stop(); 44 | } 45 | if (sender_client_) { 46 | delete sender_client_; 47 | sender_client_ = nullptr; 48 | } 49 | if (receiver_client_) { 50 | delete receiver_client_; 51 | receiver_client_ = nullptr; 52 | } 53 | } 54 | 55 | void WebrtcSessionManager::CreateClients() { 56 | sender_client_ = new webrtc::test::CallClient(time_controller_.get(),nullptr,call_client_config_); 57 | receiver_client_ = new webrtc::test::CallClient(time_controller_.get(),nullptr,call_client_config_); 58 | } 59 | 60 | void WebrtcSessionManager::RegisterSenderTransport(webrtc::test::TransportBase *transport,bool own) { 61 | if (sender_client_) { 62 | sender_client_->SetCustomTransport(transport,own); 63 | } 64 | } 65 | 66 | void WebrtcSessionManager::RegisterReceiverTransport(webrtc::test::TransportBase *transport,bool own) { 67 | if (receiver_client_) { 68 | receiver_client_->SetCustomTransport(transport,own); 69 | } 70 | 71 | } 72 | 73 | void WebrtcSessionManager::CreateStreamPair() { 74 | video_streams_.emplace_back( 75 | new webrtc::test::VideoStreamPair(sender_client_,receiver_client_, video_stream_config_)); 76 | } 77 | 78 | void WebrtcSessionManager::Start() { 79 | m_running = true; 80 | for (auto& stream_pair : video_streams_) { 81 | stream_pair->receive()->Start(); 82 | } 83 | for (auto& stream_pair : video_streams_) { 84 | if (video_stream_config_.autostart) { 85 | stream_pair->send()->Start(); 86 | } 87 | } 88 | } 89 | 90 | void WebrtcSessionManager::Stop() { 91 | if (!m_running) { 92 | return ; 93 | } 94 | m_running = false; 95 | for (auto& stream_pair : video_streams_) { 96 | stream_pair->send()->Stop(); 97 | } 98 | for (auto& stream_pair : video_streams_) { 99 | stream_pair->receive()->Stop(); 100 | } 101 | video_streams_.clear(); 102 | } 103 | 104 | void WebrtcSessionManager::SetFrameHxW(uint32_t height,uint32_t width) { 105 | video_stream_config_.source.generator.width = width; 106 | video_stream_config_.source.generator.height = height; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "rtc_base/logging.h" 5 | #include "rtc_base/location.h" 6 | #include "rtc_base/time_utils.h" 7 | #include "test/frame_generator.h" 8 | #include "api/test/time_controller.h" 9 | #include "test/scenario/scenario_config.h" 10 | #include "api/transport/goog_cc_factory.h" 11 | #include "test/scenario/network_node.h" 12 | #include "test/scenario/call_client.h" 13 | #include "test/scenario/video_stream.h" 14 | #include "test/scenario/transport_base.h" 15 | namespace ns3{ 16 | class WebrtcSessionManager{ 17 | public: 18 | WebrtcSessionManager( 19 | std::uint64_t start_time_ms, 20 | std::uint64_t stop_time_ms, 21 | std::shared_ptr cc_factory = nullptr, 22 | std::shared_ptr se_factory = nullptr); 23 | ~WebrtcSessionManager(); 24 | void SetFrameHxW(uint32_t height,uint32_t width); 25 | void CreateClients(); 26 | 27 | webrtc::test::VideoStreamConfig video_stream_config_; 28 | webrtc::test::CallClientConfig call_client_config_; 29 | std::unique_ptr time_controller_; 30 | webrtc::test::CallClient *sender_client_{nullptr}; 31 | webrtc::test::CallClient *receiver_client_{nullptr}; 32 | 33 | private: 34 | friend class WebrtcSender; 35 | friend class WebrtcReceiver; 36 | void RegisterSenderTransport(webrtc::test::TransportBase *transport,bool own); 37 | void RegisterReceiverTransport(webrtc::test::TransportBase *transport,bool own); 38 | void CreateStreamPair(); 39 | void Start(); 40 | void Stop(); 41 | bool m_running{false}; 42 | 43 | std::vector> video_streams_; 44 | }; 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-defines.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define WEBRTC_POSIX 3 | #define WEBRTC_LINUX -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-emu-controller.cc: -------------------------------------------------------------------------------- 1 | #include "api/task_queue/default_task_queue_factory.h" 2 | #include "rtc_base/null_socket_server.h" 3 | #include "system_wrappers/include/sleep.h" 4 | 5 | #include "webrtc-emu-controller.h" 6 | 7 | namespace webrtc { 8 | namespace { 9 | 10 | class MainThread : public rtc::Thread { 11 | public: 12 | MainThread() 13 | : Thread(std::make_unique(), false), 14 | current_setter_(this) { 15 | DoInit(); 16 | } 17 | ~MainThread() { 18 | Stop(); 19 | DoDestroy(); 20 | } 21 | 22 | private: 23 | CurrentThreadSetter current_setter_; 24 | }; 25 | 26 | } // namespace 27 | 28 | EmulationTimeController::EmulationTimeController() 29 | : task_queue_factory_(CreateDefaultTaskQueueFactory()), 30 | main_thread_(std::make_unique()) { 31 | main_thread_->SetName("Main", this); 32 | } 33 | 34 | Clock* EmulationTimeController::GetClock() { 35 | return Clock::GetRealTimeClock(); 36 | } 37 | 38 | TaskQueueFactory* EmulationTimeController::GetTaskQueueFactory() { 39 | return task_queue_factory_.get(); 40 | } 41 | 42 | std::unique_ptr EmulationTimeController::CreateProcessThread( 43 | const char* thread_name) { 44 | return ProcessThread::Create(thread_name); 45 | } 46 | 47 | std::unique_ptr EmulationTimeController::CreateThread( 48 | const std::string& name, 49 | std::unique_ptr socket_server) { 50 | if (!socket_server) 51 | socket_server = std::make_unique(); 52 | auto res = std::make_unique(std::move(socket_server)); 53 | res->SetName(name, nullptr); 54 | res->Start(); 55 | return res; 56 | } 57 | 58 | rtc::Thread* EmulationTimeController::GetMainThread() { 59 | return main_thread_.get(); 60 | } 61 | 62 | void EmulationTimeController::AdvanceTime(TimeDelta duration) { 63 | main_thread_->ProcessMessages(duration.ms()); 64 | } 65 | 66 | } // namespace webrtc 67 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-emu-controller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "api/task_queue/task_queue_factory.h" 4 | #include "api/units/time_delta.h" 5 | #include "modules/utility/include/process_thread.h" 6 | #include "api/test/time_controller.h" 7 | 8 | namespace webrtc{ 9 | 10 | class EmulationTimeController:public TimeController{ 11 | public: 12 | EmulationTimeController(); 13 | 14 | Clock* GetClock() override; 15 | TaskQueueFactory* GetTaskQueueFactory() override; 16 | std::unique_ptr CreateProcessThread( 17 | const char* thread_name) override; 18 | std::unique_ptr CreateThread( 19 | const std::string& name, 20 | std::unique_ptr socket_server) override; 21 | rtc::Thread* GetMainThread() override; 22 | void AdvanceTime(TimeDelta duration) override; 23 | 24 | private: 25 | const std::unique_ptr task_queue_factory_; 26 | const std::unique_ptr main_thread_; 27 | }; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-receiver.cc: -------------------------------------------------------------------------------- 1 | #include "webrtc-receiver.h" 2 | #include "ns3/log.h" 3 | #include "rtc_base/net_helper.h" 4 | #include "api/test/network_emulation/network_emulation_interfaces.h" 5 | 6 | namespace ns3{ 7 | 8 | NS_LOG_COMPONENT_DEFINE("WebrtcReceiver"); 9 | 10 | namespace{ 11 | const uint32_t kIpv4HeaderSize = 20; 12 | constexpr char kDummyTransportName[] = "dummy"; 13 | } 14 | 15 | WebrtcReceiver::WebrtcReceiver(WebrtcSessionManager *manager) { 16 | m_manager = manager; 17 | m_clock = manager->time_controller_->GetClock(); 18 | m_manager->RegisterReceiverTransport(this, false); 19 | m_client = m_manager->receiver_client_; 20 | m_call = m_client->GetCall(); 21 | } 22 | 23 | WebrtcReceiver::~WebrtcReceiver() {} 24 | 25 | InetSocketAddress WebrtcReceiver::GetLocalAddress() { 26 | Ptr node = GetNode(); 27 | Ptr ipv4 = node->GetObject (); 28 | Ipv4Address local_ip = ipv4->GetAddress (1, 0).GetLocal (); 29 | 30 | return InetSocketAddress{local_ip, m_bindPort}; 31 | } 32 | 33 | void WebrtcReceiver::ConfigurePeer(Ipv4Address addr, uint16_t port) { 34 | m_peerIp = addr; 35 | m_peerPort = port; 36 | } 37 | 38 | void WebrtcReceiver::Bind(uint16_t port) { 39 | m_bindPort = port; 40 | if (m_socket == NULL) { 41 | m_socket = Socket::CreateSocket (GetNode(), UdpSocketFactory::GetTypeId ()); 42 | auto local = InetSocketAddress{Ipv4Address::GetAny(), port}; 43 | auto res = m_socket->Bind (local); 44 | NS_ASSERT (res == 0); 45 | } 46 | m_socket->SetRecvCallback(MakeCallback(&WebrtcReceiver::RecvPacket, this)); 47 | m_context = GetNode()->GetId (); 48 | NotifyRouteChange(); 49 | } 50 | 51 | uint16_t WebrtcReceiver::GetBindPort() const { 52 | return m_bindPort; 53 | } 54 | 55 | bool WebrtcReceiver::SendRtp(const uint8_t* packet, 56 | size_t length, 57 | const webrtc::PacketOptions& options) { 58 | NS_ASSERT(length<1500&&length>0); 59 | int64_t send_time_ms = m_clock->TimeInMilliseconds(); 60 | rtc::SentPacket sent_packet; 61 | sent_packet.packet_id = options.packet_id; 62 | sent_packet.info.included_in_feedback = options.included_in_feedback; 63 | sent_packet.info.included_in_allocation = options.included_in_allocation; 64 | sent_packet.send_time_ms = send_time_ms; 65 | sent_packet.info.packet_size_bytes = length; 66 | sent_packet.info.packet_type = rtc::PacketType::kData; 67 | m_call->OnSentPacket(sent_packet); 68 | { 69 | rtc::Buffer buffer(packet, length); 70 | std::unique_lock lock(m_rtpMutex); 71 | m_rtpQ.push_back(std::move(buffer)); 72 | } 73 | if (m_running) { 74 | Simulator::ScheduleWithContext(m_context, Time (0), MakeEvent(&WebrtcReceiver::DeliveryPacket, this)); 75 | } 76 | 77 | return true; 78 | } 79 | 80 | bool WebrtcReceiver::SendRtcp(const uint8_t* packet, size_t length) { 81 | { 82 | NS_ASSERT(length<1500 && length>0); 83 | rtc::Buffer buffer(packet, length); 84 | std::unique_lock lock(m_rtcpMutex); 85 | m_rtcpQ.push_back(std::move(buffer)); 86 | } 87 | if (m_running) { 88 | Simulator::ScheduleWithContext(m_context, Time (0), MakeEvent(&WebrtcReceiver::DeliveryPacket, this)); 89 | } 90 | 91 | return true; 92 | } 93 | 94 | void WebrtcReceiver::StartApplication() { 95 | NS_LOG_INFO("Recv App Started"); 96 | m_running = true; 97 | } 98 | 99 | void WebrtcReceiver::StopApplication() { 100 | NS_LOG_INFO("Recv App Stopped"); 101 | m_running = false; 102 | } 103 | 104 | void WebrtcReceiver::NotifyRouteChange() { 105 | rtc::NetworkRoute route; 106 | route.connected = true; 107 | // We assume that the address will be unique in the lower bytes. 108 | route.local = rtc::RouteEndpoint::CreateWithNetworkId(static_cast(1234)); 109 | route.remote = rtc::RouteEndpoint::CreateWithNetworkId(static_cast(4321)); 110 | m_packetOverhead = webrtc::test::PacketOverhead::kDefault + 111 | kIpv4HeaderSize+cricket::kUdpHeaderSize; 112 | route.packet_overhead = m_packetOverhead; 113 | m_call->GetTransportControllerSend()->OnNetworkRouteChanged(kDummyTransportName, route); 114 | } 115 | 116 | void WebrtcReceiver::DeliveryPacket() { 117 | std::deque> sendQ; 118 | { 119 | std::unique_lock lock(m_rtpMutex); 120 | while(!m_rtpQ.empty()) { 121 | rtc::Buffer& buffer = m_rtpQ.front(); 122 | Ptr packet = Create(buffer.data(), buffer.size()); 123 | sendQ.push_back(packet); 124 | m_rtpQ.pop_front(); 125 | } 126 | } 127 | { 128 | std::unique_lock lock(m_rtcpMutex); 129 | while(!m_rtcpQ.empty()) { 130 | rtc::Buffer& buffer = m_rtcpQ.front(); 131 | Ptr packet = Create(buffer.data(), buffer.size()); 132 | sendQ.push_back(packet); 133 | m_rtcpQ.pop_front(); 134 | } 135 | } 136 | while(!sendQ.empty()) { 137 | Ptr packet = sendQ.front(); 138 | sendQ.pop_front(); 139 | SendToNetwork(packet); 140 | } 141 | } 142 | 143 | void WebrtcReceiver::SendToNetwork(Ptr p) { 144 | NS_ASSERT(p->GetSize()>0); 145 | m_socket->SendTo(p, 0, InetSocketAddress{m_peerIp, m_peerPort}); 146 | } 147 | 148 | void WebrtcReceiver::RecvPacket(Ptr socket) { 149 | Address remoteAddr; 150 | auto packet = socket->RecvFrom(remoteAddr); 151 | if (!m_knowPeer) { 152 | m_peerIp = InetSocketAddress::ConvertFrom(remoteAddr).GetIpv4 (); 153 | uint16_t port = m_peerPort; 154 | m_peerPort = InetSocketAddress::ConvertFrom(remoteAddr).GetPort (); 155 | m_knowPeer = true; 156 | NS_ASSERT(port == m_peerPort); 157 | } 158 | if (!m_running) { return; } 159 | uint32_t recv = packet->GetSize(); 160 | NS_ASSERT(recv <= 1500); 161 | uint8_t buf[1500]={'\0'}; 162 | packet->CopyData(buf, recv); 163 | if (!webrtc::RtpHeaderParser::IsRtcp(buf, recv)) { 164 | auto ssrc = webrtc::RtpHeaderParser::GetSsrc(buf, recv); 165 | if (!ssrc.has_value()) { 166 | return; 167 | } 168 | } 169 | rtc::CopyOnWriteBuffer packet_data(buf, recv); 170 | webrtc::EmulatedIpPacket emu_packet(rtc::SocketAddress(), rtc::SocketAddress(), std::move(packet_data), 171 | m_clock->CurrentTime(), m_packetOverhead); 172 | m_client->OnPacketReceived(std::move(emu_packet)); 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-receiver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ns3/event-id.h" 4 | #include "ns3/callback.h" 5 | #include "ns3/application.h" 6 | #include "ns3/socket.h" 7 | #include "ns3/network-module.h" 8 | #include "ns3/internet-module.h" 9 | #include "ns3/webrtc-config.h" 10 | #include "test/scenario/transport_base.h" 11 | #include "call/call.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace ns3 { 17 | 18 | class WebrtcReceiver : public webrtc::test::TransportBase, 19 | public Application { 20 | public: 21 | WebrtcReceiver(WebrtcSessionManager *manager); 22 | ~WebrtcReceiver() override; 23 | InetSocketAddress GetLocalAddress(); 24 | void Bind(uint16_t port); 25 | uint16_t GetBindPort() const; 26 | void ConfigurePeer(Ipv4Address addr, uint16_t port); 27 | void Construct(webrtc::Clock* sender_clock, webrtc::Call* sender_call) override{} 28 | bool SendRtp(const uint8_t* packet, 29 | size_t length, 30 | const webrtc::PacketOptions& options) override; 31 | bool SendRtcp(const uint8_t* packet, size_t length) override; 32 | 33 | private: 34 | virtual void StartApplication() override; 35 | virtual void StopApplication() override; 36 | void NotifyRouteChange(); 37 | void DeliveryPacket(); 38 | void SendToNetwork(Ptr p); 39 | void RecvPacket(Ptr socket); 40 | 41 | bool m_running{false}; 42 | WebrtcSessionManager *m_manager{nullptr}; 43 | webrtc::Clock *m_clock; 44 | uint16_t m_bindPort; 45 | Ptr m_socket; 46 | bool m_knowPeer{false}; 47 | Ipv4Address m_peerIp; 48 | uint16_t m_peerPort; 49 | webrtc::test::CallClient *m_client{nullptr}; 50 | webrtc::Call* m_call{nullptr}; 51 | uint32_t m_maxSeenSeq{0}; 52 | uint32_t m_seq{1}; 53 | std::mutex m_rtpMutex; 54 | std::deque m_rtpQ; 55 | std::mutex m_rtcpMutex; 56 | std::deque m_rtcpQ; 57 | uint32_t m_packetOverhead{0}; 58 | uint32_t m_context{0}; 59 | }; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-sender.cc: -------------------------------------------------------------------------------- 1 | #include "webrtc-sender.h" 2 | #include "ns3/log.h" 3 | #include "call/call.h" 4 | #include "rtc_base/net_helper.h" 5 | #include "rtc_base/network/sent_packet.h" 6 | #include "api/test/network_emulation/network_emulation_interfaces.h" 7 | 8 | namespace ns3{ 9 | 10 | NS_LOG_COMPONENT_DEFINE("WebrtcSender"); 11 | 12 | namespace{ 13 | const uint32_t kIpv4HeaderSize = 20; 14 | const int32_t kTraceInterval = 25; 15 | constexpr char kDummyTransportName[] = "dummy"; 16 | } 17 | 18 | WebrtcSender::WebrtcSender(WebrtcSessionManager *manager){ 19 | m_manager = manager; 20 | m_clock = manager->time_controller_->GetClock(); 21 | m_manager->RegisterSenderTransport(this, false); 22 | m_client = m_manager->sender_client_; 23 | m_call = m_client->GetCall(); 24 | m_initial_time = Simulator::Now().GetMilliSeconds(); 25 | } 26 | 27 | WebrtcSender::~WebrtcSender() {} 28 | 29 | InetSocketAddress WebrtcSender::GetLocalAddress(){ 30 | Ptr node = GetNode(); 31 | Ptr ipv4 = node->GetObject (); 32 | Ipv4Address local_ip = ipv4->GetAddress (1, 0).GetLocal (); 33 | 34 | return InetSocketAddress{local_ip, m_bindPort}; 35 | } 36 | 37 | void WebrtcSender::Bind(uint16_t port){ 38 | m_bindPort = port; 39 | if (m_socket == NULL) { 40 | m_socket = Socket::CreateSocket (GetNode (), UdpSocketFactory::GetTypeId ()); 41 | auto local = InetSocketAddress{Ipv4Address::GetAny (), port}; 42 | auto res = m_socket->Bind (local); 43 | NS_ASSERT (res == 0); 44 | } 45 | m_socket->SetRecvCallback(MakeCallback(&WebrtcSender::RecvPacket, this)); 46 | m_context = GetNode()->GetId (); 47 | NotifyRouteChange(); 48 | } 49 | 50 | uint16_t WebrtcSender::GetBindPort() const { 51 | return m_bindPort; 52 | } 53 | 54 | void WebrtcSender::ConfigurePeer(Ipv4Address addr, uint16_t port){ 55 | m_peerIp = addr; 56 | m_peerPort = port; 57 | } 58 | 59 | void WebrtcSender::SetBwTraceFuc(TraceBandwidth cb){ 60 | m_traceBw = cb; 61 | } 62 | 63 | void WebrtcSender::SetRttTraceFuc(TraceRtt cb) { 64 | m_traceRtt = cb; 65 | } 66 | 67 | bool WebrtcSender::SendRtp(const uint8_t* packet, 68 | size_t length, 69 | const webrtc::PacketOptions& options){ 70 | if(length == 0){ 71 | NS_LOG_INFO("0 packet"); 72 | return true; 73 | } 74 | NS_ASSERT(length<1500 && length>0); 75 | int64_t send_time_ms = m_clock->TimeInMilliseconds(); 76 | rtc::SentPacket sent_packet; 77 | sent_packet.packet_id = options.packet_id; 78 | sent_packet.info.included_in_feedback = options.included_in_feedback; 79 | sent_packet.info.included_in_allocation = options.included_in_allocation; 80 | sent_packet.send_time_ms = send_time_ms; 81 | sent_packet.info.packet_size_bytes = length; 82 | sent_packet.info.packet_type = rtc::PacketType::kData; 83 | m_call->OnSentPacket(sent_packet); 84 | { 85 | rtc::Buffer buffer(packet, length); 86 | std::unique_lock lock(m_rtpMutex); 87 | m_rtpQ.push_back(std::move(buffer)); 88 | } 89 | bool output = false; 90 | uint32_t now = Simulator::Now().GetMilliSeconds(); 91 | if (m_lastTraceTime == 0) { 92 | m_lastTraceTime = now; 93 | output = true; 94 | } 95 | if (now>=m_lastTraceTime+kTraceInterval) { 96 | m_lastTraceTime = now; 97 | output = true; 98 | } 99 | if (output) { 100 | // webrtc::Call::Stats stats = m_call->GetStats(); 101 | 102 | // // It's quite weird that recv_bw_bps is 0. 103 | // NS_LOG_INFO(stats.ToString(now)); 104 | 105 | // if (!m_traceBw.IsNull()) { 106 | // m_traceBw(now, stats.recv_bandwidth_bps); 107 | // } 108 | // if (!m_traceRtt.IsNull()) { 109 | // m_traceRtt(now, stats.rtt_ms); 110 | // } 111 | } 112 | if (m_running) { 113 | Simulator::ScheduleWithContext(m_context, Time (0), MakeEvent(&WebrtcSender::DeliveryPacket, this)); 114 | } 115 | 116 | return true; 117 | } 118 | 119 | bool WebrtcSender::SendRtcp(const uint8_t* packet, size_t length){ 120 | { 121 | NS_ASSERT(length<1500 && length>0); 122 | rtc::Buffer buffer(packet, length); 123 | std::unique_lock lock(m_rtcpMutex); 124 | m_rtcpQ.push_back(std::move(buffer)); 125 | } 126 | if(m_running) { 127 | Simulator::ScheduleWithContext(m_context, Time (0), MakeEvent(&WebrtcSender::DeliveryPacket, this)); 128 | } 129 | 130 | return true; 131 | } 132 | 133 | void WebrtcSender::StartApplication(){ 134 | NS_LOG_INFO("Sender app started"); 135 | m_running = true; 136 | m_manager->CreateStreamPair(); 137 | m_manager->Start(); 138 | } 139 | 140 | void WebrtcSender::StopApplication(){ 141 | NS_LOG_INFO("Sender app stopped"); 142 | m_running = false; 143 | m_manager->Stop(); 144 | } 145 | 146 | void WebrtcSender::NotifyRouteChange(){ 147 | rtc::NetworkRoute route; 148 | route.connected = true; 149 | // We assume that the address will be unique in the lower bytes. 150 | route.local = rtc::RouteEndpoint::CreateWithNetworkId(static_cast(1234)); 151 | route.remote = rtc::RouteEndpoint::CreateWithNetworkId(static_cast(4321)); 152 | m_packetOverhead = webrtc::test::PacketOverhead::kDefault + 153 | kIpv4HeaderSize+cricket::kUdpHeaderSize; 154 | route.packet_overhead = m_packetOverhead; 155 | m_call->GetTransportControllerSend()->OnNetworkRouteChanged(kDummyTransportName, route); 156 | 157 | } 158 | 159 | void WebrtcSender::DeliveryPacket(){ 160 | std::deque> sendQ; 161 | { 162 | std::unique_lock lock(m_rtpMutex); 163 | while(!m_rtpQ.empty()){ 164 | rtc::Buffer& buffer = m_rtpQ.front(); 165 | Ptr packet = Create(buffer.data(), buffer.size()); 166 | sendQ.push_back(packet); 167 | m_rtpQ.pop_front(); 168 | } 169 | } 170 | { 171 | std::unique_lock lock(m_rtcpMutex); 172 | while(!m_rtcpQ.empty()){ 173 | rtc::Buffer& buffer = m_rtcpQ.front(); 174 | Ptr packet = Create(buffer.data(), buffer.size()); 175 | sendQ.push_back(packet); 176 | m_rtcpQ.pop_front(); 177 | } 178 | } 179 | while(!sendQ.empty()){ 180 | Ptr packet = sendQ.front(); 181 | sendQ.pop_front(); 182 | SendToNetwork(packet); 183 | } 184 | } 185 | 186 | void WebrtcSender::SendToNetwork(Ptr p){ 187 | NS_ASSERT(p->GetSize()>0); 188 | m_socket->SendTo(p, 0, InetSocketAddress{m_peerIp, m_peerPort}); 189 | } 190 | 191 | void WebrtcSender::RecvPacket(Ptr socket){ 192 | if(!m_running){return;} 193 | Address remoteAddr; 194 | auto packet = socket->RecvFrom (remoteAddr); 195 | uint32_t recv = packet->GetSize (); 196 | NS_ASSERT(recv<=1500); 197 | uint8_t buf[1500] = {'\0'}; 198 | packet->CopyData(buf, recv); 199 | rtc::CopyOnWriteBuffer packet_data(buf, recv); 200 | if (!webrtc::RtpHeaderParser::IsRtcp(buf, recv)) { 201 | auto ssrc = webrtc::RtpHeaderParser::GetSsrc(buf, recv); 202 | if(!ssrc.has_value()){ 203 | NS_LOG_INFO("sender no ssrc"); 204 | return; 205 | } 206 | } 207 | webrtc::EmulatedIpPacket emu_packet(rtc::SocketAddress(), rtc::SocketAddress(), std::move(packet_data), 208 | m_clock->CurrentTime(), m_packetOverhead); 209 | m_client->OnPacketReceived(std::move(emu_packet)); 210 | } 211 | 212 | } 213 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-sender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ns3/event-id.h" 4 | #include "ns3/callback.h" 5 | #include "ns3/application.h" 6 | #include "ns3/socket.h" 7 | #include "ns3/network-module.h" 8 | #include "ns3/internet-module.h" 9 | #include "ns3/webrtc-config.h" 10 | #include "test/scenario/transport_base.h" 11 | #include "call/call.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace ns3 { 17 | 18 | class WebrtcSender : public webrtc::test::TransportBase, 19 | public Application { 20 | public: 21 | WebrtcSender(WebrtcSessionManager *manager); 22 | ~WebrtcSender() override; 23 | InetSocketAddress GetLocalAddress(); 24 | void Bind(uint16_t port); 25 | uint16_t GetBindPort() const; 26 | void ConfigurePeer(Ipv4Address addr, uint16_t port); 27 | 28 | typedef Callback TraceBandwidth; 29 | typedef Callback TraceRtt; 30 | void SetBwTraceFuc(TraceBandwidth cb); 31 | void SetRttTraceFuc(TraceRtt cb); 32 | 33 | void Construct(webrtc::Clock* sender_clock, webrtc::Call* sender_call) override{} 34 | bool SendRtp(const uint8_t* packet, 35 | size_t length, 36 | const webrtc::PacketOptions& options) override; 37 | bool SendRtcp(const uint8_t* packet, size_t length) override; 38 | 39 | private: 40 | virtual void StartApplication() override; 41 | virtual void StopApplication() override; 42 | void NotifyRouteChange(); 43 | void DeliveryPacket(); 44 | void SendToNetwork(Ptr p); 45 | void RecvPacket(Ptr socket); 46 | 47 | bool m_running{false}; 48 | WebrtcSessionManager *m_manager{nullptr}; 49 | webrtc::Clock *m_clock; 50 | uint16_t m_bindPort; 51 | Ptr m_socket; 52 | Ipv4Address m_peerIp; 53 | uint16_t m_peerPort; 54 | webrtc::test::CallClient *m_client{nullptr}; 55 | webrtc::Call* m_call{nullptr}; 56 | uint32_t m_seq{1}; 57 | std::mutex m_rtpMutex; 58 | std::deque m_rtpQ; 59 | std::mutex m_rtcpMutex; 60 | std::deque m_rtcpQ; 61 | int64_t m_lastTraceTime{0}; 62 | 63 | TraceBandwidth m_traceBw; 64 | TraceRtt m_traceRtt; 65 | 66 | uint32_t m_packetOverhead{0}; 67 | uint32_t m_initial_time{0}; 68 | uint32_t m_context{0}; 69 | }; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-simu-controller.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "webrtc-simu-controller.h" 20 | #include "absl/strings/string_view.h" 21 | #include "simulated-process-thread.h" 22 | #include "simulated-task-queue.h" 23 | #include "simulated-thread.h" 24 | #include "rtc_base/checks.h" 25 | 26 | namespace webrtc { 27 | 28 | const uint32_t kContextAny=0xffffffff; 29 | 30 | namespace { 31 | // Helper function to remove from a std container by value. 32 | template 33 | bool RemoveByValue(C* vec, typename C::value_type val) { 34 | auto it = std::find(vec->begin(), vec->end(), val); 35 | if (it == vec->end()) 36 | return false; 37 | vec->erase(it); 38 | return true; 39 | } 40 | 41 | } // namespace 42 | 43 | namespace sim_time_impl { 44 | 45 | SimulatedTimeControllerImpl::SimulatedTimeControllerImpl(int64_t start_us,int64_t stop_us) 46 | :stop_us_(stop_us), 47 | thread_id_(rtc::CurrentThreadId()){ 48 | ns3::Time next=ns3::MicroSeconds(start_us); 49 | ns3::Simulator::ScheduleWithContext(kContextAny,next,ns3::MakeEvent(&webrtc::sim_time_impl::SimulatedTimeControllerImpl::OnTaskTimer, this)); 50 | } 51 | SimulatedTimeControllerImpl::~SimulatedTimeControllerImpl() = default; 52 | Clock* SimulatedTimeControllerImpl::GetClock() { 53 | return &sim_clock_; 54 | } 55 | std::unique_ptr 56 | SimulatedTimeControllerImpl::CreateTaskQueue( 57 | absl::string_view name, 58 | TaskQueueFactory::Priority priority) const { 59 | // TODO(srte): Remove the const cast when the interface is made mutable. 60 | auto mutable_this = const_cast(this); 61 | auto task_queue = std::unique_ptr( 62 | new SimulatedTaskQueue(mutable_this, name)); 63 | ; 64 | mutable_this->Register(task_queue.get()); 65 | return task_queue; 66 | } 67 | 68 | std::unique_ptr SimulatedTimeControllerImpl::CreateProcessThread( 69 | const char* thread_name) { 70 | rtc::CritScope lock(&lock_); 71 | auto process_thread = 72 | std::make_unique(this, thread_name); 73 | Register(process_thread.get()); 74 | return process_thread; 75 | } 76 | 77 | std::unique_ptr SimulatedTimeControllerImpl::CreateThread( 78 | const std::string& name, 79 | std::unique_ptr socket_server) { 80 | auto thread = 81 | std::make_unique(this, name, std::move(socket_server)); 82 | Register(thread.get()); 83 | return thread; 84 | } 85 | 86 | void SimulatedTimeControllerImpl::YieldExecution() { 87 | if (rtc::CurrentThreadId() == thread_id_) { 88 | TaskQueueBase* yielding_from = TaskQueueBase::Current(); 89 | // Since we might continue execution on a process thread, we should reset 90 | // the thread local task queue reference. This ensures that thread checkers 91 | // won't think we are executing on the yielding task queue. It also ensure 92 | // that TaskQueueBase::Current() won't return the yielding task queue. 93 | TokenTaskQueue::CurrentTaskQueueSetter reset_queue(nullptr); 94 | // When we yield, we don't want to risk executing further tasks on the 95 | // currently executing task queue. If there's a ready task that also yields, 96 | // it's added to this set as well and only tasks on the remaining task 97 | // queues are executed. 98 | auto inserted = yielded_.insert(yielding_from); 99 | RTC_DCHECK(inserted.second); 100 | RunReadyRunners(); 101 | yielded_.erase(inserted.first); 102 | } 103 | } 104 | 105 | void SimulatedTimeControllerImpl::RunReadyRunners() { 106 | // Using a dummy thread rather than nullptr to avoid implicit thread creation 107 | // by Thread::Current(). 108 | SimulatedThread::CurrentThreadSetter set_current(dummy_thread_.get()); 109 | rtc::CritScope lock(&lock_); 110 | RTC_DCHECK_EQ(rtc::CurrentThreadId(), thread_id_); 111 | Timestamp current_time = CurrentTime(); 112 | // Clearing |ready_runners_| in case this is a recursive call: 113 | // RunReadyRunners -> Run -> Event::Wait -> Yield ->RunReadyRunners 114 | ready_runners_.clear(); 115 | 116 | // We repeat until we have no ready left to handle tasks posted by ready 117 | // runners. 118 | while (true) { 119 | for (auto* runner : runners_) { 120 | if (yielded_.find(runner->GetAsTaskQueue()) == yielded_.end() && 121 | runner->GetNextRunTime() <= current_time) { 122 | ready_runners_.push_back(runner); 123 | } 124 | } 125 | if (ready_runners_.empty()) 126 | break; 127 | while (!ready_runners_.empty()) { 128 | auto* runner = ready_runners_.front(); 129 | ready_runners_.pop_front(); 130 | // Note that the RunReady function might indirectly cause a call to 131 | // Unregister() which will recursively grab |lock_| again to remove items 132 | // from |ready_runners_|. 133 | runner->RunReady(current_time); 134 | } 135 | } 136 | } 137 | 138 | Timestamp SimulatedTimeControllerImpl::CurrentTime() { 139 | return sim_clock_.CurrentTime(); 140 | } 141 | 142 | Timestamp SimulatedTimeControllerImpl::NextRunTime() { 143 | Timestamp current_time = CurrentTime(); 144 | Timestamp next_time = Timestamp::PlusInfinity(); 145 | rtc::CritScope lock(&lock_); 146 | for (auto* runner : runners_) { 147 | Timestamp next_run_time = runner->GetNextRunTime(); 148 | if (next_run_time <= current_time) 149 | return current_time; 150 | next_time = std::min(next_time, next_run_time); 151 | } 152 | return next_time; 153 | } 154 | 155 | void SimulatedTimeControllerImpl::Register(SimulatedSequenceRunner* runner) { 156 | rtc::CritScope lock(&lock_); 157 | runners_.push_back(runner); 158 | } 159 | 160 | void SimulatedTimeControllerImpl::Unregister(SimulatedSequenceRunner* runner) { 161 | rtc::CritScope lock(&lock_); 162 | bool removed = RemoveByValue(&runners_, runner); 163 | RTC_CHECK(removed); 164 | RemoveByValue(&ready_runners_, runner); 165 | } 166 | 167 | void SimulatedTimeControllerImpl::StartYield(TaskQueueBase* yielding_from) { 168 | auto inserted = yielded_.insert(yielding_from); 169 | RTC_DCHECK(inserted.second); 170 | } 171 | 172 | void SimulatedTimeControllerImpl::StopYield(TaskQueueBase* yielding_from) { 173 | yielded_.erase(yielding_from); 174 | } 175 | void SimulatedTimeControllerImpl::OnTaskTimer(){ 176 | Timestamp current_time = CurrentTime(); 177 | Timestamp next_time=current_time; 178 | do{ 179 | RunReadyRunners(); 180 | }while((next_time=NextRunTime())<=current_time); 181 | //RTC_CHECK_NE(next_time,Timestamp::PlusInfinity()); 182 | if((CurrentTime().us()(&impl_); 194 | impl_.Register(main_thread.get()); 195 | main_thread_ = std::move(main_thread); 196 | } 197 | 198 | SimulationTimeController::~SimulationTimeController() = default; 199 | TaskQueueFactory* SimulationTimeController::GetTaskQueueFactory() { 200 | return &impl_; 201 | } 202 | 203 | std::unique_ptr 204 | SimulationTimeController::CreateProcessThread(const char* thread_name) { 205 | return impl_.CreateProcessThread(thread_name); 206 | } 207 | 208 | std::unique_ptr SimulationTimeController::CreateThread( 209 | const std::string& name, 210 | std::unique_ptr socket_server) { 211 | return impl_.CreateThread(name, std::move(socket_server)); 212 | } 213 | 214 | rtc::Thread* SimulationTimeController::GetMainThread() { 215 | return main_thread_.get(); 216 | } 217 | 218 | } // namespace webrtc 219 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/model/webrtc-simu-controller.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | #pragma once 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "absl/strings/string_view.h" 19 | #include "api/test/time_controller.h" 20 | #include "api/units/timestamp.h" 21 | #include "modules/include/module.h" 22 | #include "modules/utility/include/process_thread.h" 23 | #include "rtc_base/critical_section.h" 24 | #include "rtc_base/fake_clock.h" 25 | #include "rtc_base/platform_thread_types.h" 26 | #include "rtc_base/synchronization/yield_policy.h" 27 | #include "rtc_base/thread_checker.h" 28 | 29 | #include "ns3/core-module.h" 30 | #include "webrtc-clock.h" 31 | 32 | namespace webrtc { 33 | namespace sim_time_impl { 34 | class SimulatedSequenceRunner { 35 | public: 36 | virtual ~SimulatedSequenceRunner() = default; 37 | // Provides next run time. 38 | virtual Timestamp GetNextRunTime() const = 0; 39 | // Runs all ready tasks and modules and updates next run time. 40 | virtual void RunReady(Timestamp at_time) = 0; 41 | 42 | // All implementations also implements TaskQueueBase in some form, but if we'd 43 | // inherit from it in this interface we'd run into issues with double 44 | // inheritance. Therefore we simply allow the implementations to provide a 45 | // casted pointer to themself. 46 | virtual TaskQueueBase* GetAsTaskQueue() = 0; 47 | }; 48 | 49 | class SimulatedTimeControllerImpl : public TaskQueueFactory, 50 | public rtc::YieldInterface { 51 | public: 52 | explicit SimulatedTimeControllerImpl(int64_t start_us,int64_t stop_us); 53 | ~SimulatedTimeControllerImpl() override; 54 | Clock* GetClock(); 55 | std::unique_ptr CreateTaskQueue( 56 | absl::string_view name, 57 | Priority priority) const override; 58 | 59 | // Implements the YieldInterface by running ready tasks on all task queues, 60 | // except that if this method is called from a task, the task queue running 61 | // that task is skipped. 62 | void YieldExecution() override; 63 | // Create process thread with the name |thread_name|. 64 | std::unique_ptr CreateProcessThread(const char* thread_name); 65 | // Create thread using provided |socket_server|. 66 | std::unique_ptr CreateThread( 67 | const std::string& name, 68 | std::unique_ptr socket_server); 69 | 70 | // Runs all runners in |runners_| that has tasks or modules ready for 71 | // execution. 72 | void RunReadyRunners(); 73 | // Return |current_time_|. 74 | Timestamp CurrentTime(); 75 | // Return min of runner->GetNextRunTime() for runner in |runners_|. 76 | Timestamp NextRunTime(); 77 | // Adds |runner| to |runners_|. 78 | void Register(SimulatedSequenceRunner* runner); 79 | // Removes |runner| from |runners_|. 80 | void Unregister(SimulatedSequenceRunner* runner); 81 | 82 | // Indicates that |yielding_from| is not ready to run. 83 | void StartYield(TaskQueueBase* yielding_from); 84 | // Indicates that processing can be continued on |yielding_from|. 85 | void StopYield(TaskQueueBase* yielding_from); 86 | void OnTaskTimer(); 87 | private: 88 | int64_t stop_us_=0; 89 | const rtc::PlatformThreadId thread_id_; 90 | WebrtcSimulationClock sim_clock_; 91 | const std::unique_ptr dummy_thread_ = rtc::Thread::Create(); 92 | rtc::CriticalSection lock_; 93 | std::vector runners_ RTC_GUARDED_BY(lock_); 94 | // Used in RunReadyRunners() to keep track of ready runners that are to be 95 | // processed in a round robin fashion. the reason it's a member is so that 96 | // runners can removed from here by Unregister(). 97 | std::list ready_runners_ RTC_GUARDED_BY(lock_); 98 | 99 | // Runners on which YieldExecution has been called. 100 | std::unordered_set yielded_; 101 | }; 102 | } // namespace sim_time_impl 103 | 104 | // Used to satisfy sequence checkers for non task queue sequences. 105 | class TokenTaskQueue : public TaskQueueBase { 106 | public: 107 | // Promoted to public 108 | using CurrentTaskQueueSetter = TaskQueueBase::CurrentTaskQueueSetter; 109 | 110 | void Delete() override { RTC_NOTREACHED(); } 111 | void PostTask(std::unique_ptr /*task*/) override { 112 | RTC_NOTREACHED(); 113 | } 114 | void PostDelayedTask(std::unique_ptr /*task*/, 115 | uint32_t /*milliseconds*/) override { 116 | RTC_NOTREACHED(); 117 | } 118 | }; 119 | 120 | // TimeController implementation using completely simulated time. Task queues 121 | // and process threads created by this controller will run delayed activities 122 | // when AdvanceTime() is called. Overrides the global clock backing 123 | // rtc::TimeMillis() and rtc::TimeMicros(). Note that this is not thread safe 124 | // since it modifies global state. 125 | class SimulationTimeController : public TimeController { 126 | public: 127 | explicit SimulationTimeController(int64_t start_us,int64_t stop_us); 128 | ~SimulationTimeController() override; 129 | 130 | Clock* GetClock() override {return impl_.GetClock();} 131 | TaskQueueFactory* GetTaskQueueFactory() override; 132 | std::unique_ptr CreateProcessThread( 133 | const char* thread_name) override; 134 | std::unique_ptr CreateThread( 135 | const std::string& name, 136 | std::unique_ptr socket_server) override; 137 | rtc::Thread* GetMainThread() override; 138 | 139 | void AdvanceTime(TimeDelta duration) override{} 140 | 141 | private: 142 | sim_time_impl::SimulatedTimeControllerImpl impl_; 143 | rtc::ScopedYieldPolicy yield_policy_; 144 | std::unique_ptr main_thread_; 145 | }; 146 | } // namespace webrtc 147 | -------------------------------------------------------------------------------- /ns-app/src/ex-webrtc/wscript: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | webrtc_code_path = os.environ['ALPHARTC_DIR'] 4 | webrtc_lib_path = os.path.join(webrtc_code_path, 'out', 'Default', 'obj') 5 | webrtc_absl_path = os.path.join(webrtc_code_path, 'third_party', 'abseil-cpp') 6 | 7 | def configure(conf): 8 | conf.env['ENABLE_webrtc'] = conf.check( 9 | mandatory=True, 10 | libpath = [webrtc_lib_path], 11 | includes = [ 12 | webrtc_code_path, 13 | webrtc_absl_path], 14 | lib=[ 15 | 'webrtc', 16 | 'dl', 17 | 'zmq'], 18 | uselib_store = 'libwebrtc') 19 | conf.env.append_value("CXXFLAGS", [ 20 | "-std=c++14", 21 | "-I" + webrtc_code_path, 22 | "-I" + webrtc_absl_path, 23 | "-Wno-unused-const-variable", 24 | "-Wno-unused-private-field", 25 | ]) 26 | 27 | def build(bld): 28 | module= bld.create_ns3_module('ex-webrtc') 29 | module.env.append_value("CXXFLAGS", [ 30 | "-D__STDC_CONSTANT_MACROS", 31 | "-DWEBRTC_POSIX", 32 | "-DWEBRTC_LINUX", 33 | '-DGYM']) 34 | module.env.append_value("LIB", ["webrtc", "dl", "zmq"]) 35 | module.source = [ 36 | 'model/simulated-process-thread.cc', 37 | 'model/simulated-task-queue.cc', 38 | 'model/simulated-thread.cc', 39 | 'model/webrtc-config.cc', 40 | 'model/webrtc-receiver.cc', 41 | 'model/webrtc-sender.cc', 42 | 'model/webrtc-clock.cc', 43 | 'model/webrtc-emu-controller.cc', 44 | 'model/webrtc-simu-controller.cc', 45 | 'api/test/create_frame_generator.cc', 46 | 'api/test/network_emulation/network_emulation_interfaces.cc', 47 | 'test/encoder_settings.cc', 48 | 'test/fake_decoder.cc', 49 | 'test/fake_encoder.cc', 50 | 'test/fake_vp8_decoder.cc', 51 | 'test/fake_vp8_encoder.cc', 52 | 'test/field_trial.cc', 53 | 'test/frame_forwarder.cc', 54 | 'test/frame_generator.cc', 55 | 'test/frame_generator_capturer.cc', 56 | 'test/frame_utils.cc', 57 | 'modules/congestion_controller/goog_cc/test/goog_cc_printer.cc', 58 | 'test/rtp_header_parser.cc', 59 | 'rtc_base/task_queue_for_test.cc', 60 | 'test/test_video_capturer.cc', 61 | 'test/logging/file_log_writer.cc', 62 | 'test/logging/log_writer.cc', 63 | 'test/scenario/call_client.cc', 64 | 'test/scenario/column_printer.cc', 65 | 'test/scenario/hardware_codecs.cc', 66 | 'test/scenario/network_node.cc', 67 | 'test/scenario/performance_stats.cc', 68 | 'test/scenario/scenario_config.cc', 69 | 'test/scenario/stats_collection.cc', 70 | 'test/scenario/video_frame_matcher.cc', 71 | 'test/scenario/video_stream.cc', 72 | 'test/testsupport/file_utils.cc', 73 | 'test/testsupport/file_utils_override.cc', 74 | 'test/testsupport/ivf_video_frame_generator.cc', 75 | ] 76 | headers = bld(features='ns3header') 77 | headers.module = 'ex-webrtc' 78 | headers.source = [ 79 | 'model/webrtc-config.h', 80 | 'model/webrtc-defines.h', 81 | 'model/webrtc-receiver.h', 82 | 'model/webrtc-sender.h', 83 | 'model/webrtc-clock.h', 84 | 'model/webrtc-emu-controller.h', 85 | ] 86 | module.use.append("libwebrtc") 87 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyzmq 2 | numpy 3 | pytest 4 | pytest-timeout 5 | pytest-repeat --------------------------------------------------------------------------------