From 05c1a838c881ea502888659848d8792843b28718 Mon Sep 17 00:00:00 2001 From: Luca Muscariello Date: Sat, 25 Feb 2017 23:42:31 +0100 Subject: Initial commit: video player - viper Change-Id: Id5aa33598ce34659bad4a7a9ae5006bfb84f9bd1 Signed-off-by: Luca Muscariello --- .gitignore | 2 + .qmake.stash | 15 + AUTHORS | 6 + Adaptation/AbstractAdaptationLogic.cpp | 54 + Adaptation/AbstractAdaptationLogic.h | 58 + Adaptation/AdaptationLogicFactory.cpp | 48 + Adaptation/AdaptationLogicFactory.h | 40 + Adaptation/AlwaysLowestLogic.cpp | 71 + Adaptation/AlwaysLowestLogic.h | 48 + Adaptation/Bola.cpp | 506 + Adaptation/Bola.h | 118 + Adaptation/BufferBasedAdaptation.cpp | 141 + Adaptation/BufferBasedAdaptation.h | 64 + Adaptation/BufferBasedAdaptationWithRateBased.cpp | 206 + Adaptation/BufferBasedAdaptationWithRateBased.h | 72 + Adaptation/BufferBasedThreeThresholdAdaptation.cpp | 166 + Adaptation/BufferBasedThreeThresholdAdaptation.h | 69 + Adaptation/IAdaptationLogic.h | 115 + Adaptation/Panda.cpp | 274 + Adaptation/Panda.h | 89 + Adaptation/RateBasedAdaptation.cpp | 140 + Adaptation/RateBasedAdaptation.h | 61 + Buffer/Buffer.h | 238 + Buffer/IBufferObserver.h | 40 + Common/Common.cpp | 361 + Common/Common.h | 46 + Common/CommonExport.h | 33 + Common/Config.cpp | 1983 ++++ Common/Config.h | 463 + Common/Info.plist | 225 + Common/QOptions.cpp | 375 + Common/QOptions.h | 113 + Common/QtQuick2ApplicationViewer.cpp | 104 + Common/QtQuick2ApplicationViewer.h | 32 + Common/Viper.icns | Bin 0 -> 62464 bytes Common/ViperBuffer.cpp | 119 + Common/ViperBuffer.h | 51 + Input/DASHManager.cpp | 161 + Input/DASHManager.h | 80 + Input/DASHReceiver.cpp | 453 + Input/DASHReceiver.h | 129 + Input/ICNConnectionConsumerApi.cpp | 283 + Input/ICNConnectionConsumerApi.h | 138 + Input/IDASHManagerObserver.h | 41 + Input/IDASHReceiverObserver.h | 35 + Input/IDataReceiver.h | 34 + Input/IICNConnection.h | 44 + Input/MediaObject.cpp | 175 + Input/MediaObject.h | 78 + MPD/AbstractRepresentationStream.cpp | 84 + MPD/AbstractRepresentationStream.h | 61 + MPD/AdaptationSetHelper.cpp | 58 + MPD/AdaptationSetHelper.h | 36 + MPD/AdaptationSetStream.cpp | 82 + MPD/AdaptationSetStream.h | 50 + MPD/BaseUrlResolver.cpp | 64 + MPD/BaseUrlResolver.h | 33 + MPD/IRepresentationStream.h | 50 + MPD/RepresentationStreamFactory.cpp | 27 + MPD/RepresentationStreamFactory.h | 38 + MPD/SegmentListStream.cpp | 88 + MPD/SegmentListStream.h | 49 + MPD/SegmentTemplateStream.cpp | 158 + MPD/SegmentTemplateStream.h | 53 + MPD/SingleMediaSegmentStream.cpp | 76 + MPD/SingleMediaSegmentStream.h | 46 + MPD/TimeResolver.cpp | 148 + MPD/TimeResolver.h | 43 + Managers/IMultimediaManagerBase.h | 39 + Managers/IMultimediaManagerObserver.h | 35 + Managers/IStreamObserver.h | 46 + Managers/MultimediaManager.cpp | 629 ++ Managers/MultimediaManager.h | 140 + Managers/MultimediaStream.cpp | 208 + Managers/MultimediaStream.h | 89 + Portable/MultiThreading.cpp | 120 + Portable/MultiThreading.h | 86 + Portable/Networking.h | 38 + README.md | 69 + UI/DASHPlayer.cpp | 923 ++ UI/DASHPlayer.h | 207 + UI/DASHPlayerNoGUI.cpp | 383 + UI/DASHPlayerNoGUI.h | 93 + UI/GraphDataSource.cpp | 146 + UI/GraphDataSource.h | 62 + UI/IDASHPlayerGuiObserver.h | 35 + UI/IDASHPlayerNoGuiObserver.h | 29 + UI/ViperGui.cpp | 414 + UI/ViperGui.h | 128 + Websocket/WebSocketService.cpp | 94 + Websocket/WebSocketService.h | 42 + Websocket/communication-protocol.cpp | 242 + Websocket/communication-protocol.h | 85 + Websocket/connection-pool.cpp | 52 + Websocket/connection-pool.h | 40 + Websocket/json.h | 10842 +++++++++++++++++++ Websocket/query.cpp | 230 + Websocket/query.h | 121 + Websocket/tcp-server.cpp | 137 + Websocket/tcp-server.h | 59 + Websocket/websocket-server.cpp | 95 + Websocket/websocket-server.h | 51 + android/AndroidManifest.xml | 87 + android/gradle.properties | 9 + android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes android/gradle/wrapper/gradle-wrapper.properties | 6 + android/gradlew | 164 + android/gradlew.bat | 90 + android/res/drawable-hdpi/icon.png | Bin 0 -> 2449 bytes android/res/drawable-ldpi/icon.png | Bin 0 -> 1285 bytes android/res/drawable-mdpi/icon.png | Bin 0 -> 1777 bytes android/res/values/libs.xml | 25 + android/src/org/player/viper/ViperActivity.java | 51 + debug.h | 24 + libdash/Authors.txt | 10 + libdash/CMakeLists.txt | 174 + libdash/include/IAdaptationSet.h | 367 + libdash/include/IBaseUrl.h | 69 + libdash/include/IChunk.h | 88 + libdash/include/IConnection.h | 69 + libdash/include/IContentComponent.h | 83 + libdash/include/IDASHManager.h | 45 + libdash/include/IDASHMetrics.h | 48 + libdash/include/IDescriptor.h | 68 + libdash/include/IDownloadObserver.h | 58 + libdash/include/IDownloadObserver.h.save | 58 + libdash/include/IDownloadableChunk.h | 93 + libdash/include/IHTTPTransaction.h | 56 + libdash/include/IMPD.h | 207 + libdash/include/IMPDElement.h | 77 + libdash/include/IMetrics.h | 62 + libdash/include/IMultipleSegmentBase.h | 66 + libdash/include/INode.h | 97 + libdash/include/IPeriod.h | 156 + libdash/include/IProgramInformation.h | 69 + libdash/include/IRange.h | 53 + libdash/include/IRepresentation.h | 159 + libdash/include/IRepresentationBase.h | 181 + libdash/include/ISegment.h | 86 + libdash/include/ISegmentBase.h | 86 + libdash/include/ISegmentList.h | 58 + libdash/include/ISegmentTemplate.h | 148 + libdash/include/ISegmentTimeline.h | 50 + libdash/include/ISegmentURL.h | 94 + libdash/include/ISubRepresentation.h | 70 + libdash/include/ISubset.h | 54 + libdash/include/ITCPConnection.h | 40 + libdash/include/IThroughputMeasurement.h | 38 + libdash/include/ITimeline.h | 66 + libdash/include/IURLType.h | 61 + libdash/include/config.h | 37 + libdash/include/libdash.h | 31 + libdash/license.txt | 13 + libdash/mainpage.dox | 93 + libdash/source/defaults.mk | 7 + libdash/source/dllmain.cpp | 33 + libdash/source/helpers/Block.h | 59 + libdash/source/helpers/BlockStream.cpp | 290 + libdash/source/helpers/BlockStream.h | 55 + libdash/source/helpers/Path.cpp | 118 + libdash/source/helpers/Path.h | 33 + libdash/source/helpers/String.cpp | 53 + libdash/source/helpers/String.h | 31 + libdash/source/helpers/SyncedBlockStream.cpp | 250 + libdash/source/helpers/SyncedBlockStream.h | 57 + libdash/source/helpers/Time.cpp | 33 + libdash/source/helpers/Time.h | 35 + libdash/source/libdash.cpp | 20 + libdash/source/manager/DASHManager.cpp | 45 + libdash/source/manager/DASHManager.h | 35 + libdash/source/metrics/HTTPTransaction.cpp | 122 + libdash/source/metrics/HTTPTransaction.h | 68 + libdash/source/metrics/TCPConnection.cpp | 62 + libdash/source/metrics/TCPConnection.h | 49 + libdash/source/metrics/ThroughputMeasurement.cpp | 46 + libdash/source/metrics/ThroughputMeasurement.h | 43 + libdash/source/mpd/AbstractMPDElement.cpp | 41 + libdash/source/mpd/AbstractMPDElement.h | 41 + libdash/source/mpd/AdaptationSet.cpp | 343 + libdash/source/mpd/AdaptationSet.h | 138 + libdash/source/mpd/BaseUrl.cpp | 60 + libdash/source/mpd/BaseUrl.h | 49 + libdash/source/mpd/ContentComponent.cpp | 98 + libdash/source/mpd/ContentComponent.h | 62 + libdash/source/mpd/Descriptor.cpp | 39 + libdash/source/mpd/Descriptor.h | 43 + libdash/source/mpd/MPD.cpp | 212 + libdash/source/mpd/MPD.h | 107 + libdash/source/mpd/Metrics.cpp | 51 + libdash/source/mpd/Metrics.h | 51 + libdash/source/mpd/MultipleSegmentBase.cpp | 60 + libdash/source/mpd/MultipleSegmentBase.h | 51 + libdash/source/mpd/Period.cpp | 137 + libdash/source/mpd/Period.h | 79 + libdash/source/mpd/ProgramInformation.cpp | 67 + libdash/source/mpd/ProgramInformation.h | 52 + libdash/source/mpd/Range.cpp | 40 + libdash/source/mpd/Range.h | 42 + libdash/source/mpd/Representation.cpp | 116 + libdash/source/mpd/Representation.h | 75 + libdash/source/mpd/RepresentationBase.cpp | 175 + libdash/source/mpd/RepresentationBase.h | 89 + libdash/source/mpd/Segment.cpp | 138 + libdash/source/mpd/Segment.h | 69 + libdash/source/mpd/SegmentBase.cpp | 78 + libdash/source/mpd/SegmentBase.h | 56 + libdash/source/mpd/SegmentList.cpp | 50 + libdash/source/mpd/SegmentList.h | 47 + libdash/source/mpd/SegmentTemplate.cpp | 152 + libdash/source/mpd/SegmentTemplate.h | 61 + libdash/source/mpd/SegmentTimeline.cpp | 32 + libdash/source/mpd/SegmentTimeline.h | 40 + libdash/source/mpd/SegmentURL.cpp | 90 + libdash/source/mpd/SegmentURL.h | 56 + libdash/source/mpd/SubRepresentation.cpp | 55 + libdash/source/mpd/SubRepresentation.h | 50 + libdash/source/mpd/Subset.cpp | 30 + libdash/source/mpd/Subset.h | 41 + libdash/source/mpd/Timeline.cpp | 49 + libdash/source/mpd/Timeline.h | 46 + libdash/source/mpd/URLType.cpp | 57 + libdash/source/mpd/URLType.h | 48 + libdash/source/network/AbstractChunk.cpp | 271 + libdash/source/network/AbstractChunk.h | 100 + libdash/source/network/DownloadStateManager.cpp | 100 + libdash/source/network/DownloadStateManager.h | 50 + libdash/source/portable/MultiThreading.cpp | 112 + libdash/source/portable/MultiThreading.h | 70 + libdash/source/portable/Networking.h | 27 + libdash/source/sublibs.mk | 23 + libdash/source/targetver.h | 18 + libdash/source/xml/DOMHelper.cpp | 54 + libdash/source/xml/DOMHelper.h | 35 + libdash/source/xml/DOMParser.cpp | 160 + libdash/source/xml/DOMParser.h | 56 + libdash/source/xml/Node.cpp | 1029 ++ libdash/source/xml/Node.h | 105 + main.cpp | 225 + player.conf | 38 + qml/Viper/Button.qml | 89 + qml/Viper/ControlPanel.qml | 461 + qml/Viper/DelegateItem.qml | 65 + qml/Viper/GraphPanel.qml | 267 + qml/Viper/OpenMpd.qml | 113 + qml/Viper/OptionConnections.qml | 673 ++ qml/Viper/Options.qml | 1710 +++ qml/Viper/ProgressBar.qml | 124 + qml/Viper/Slider.qml | 117 + qml/Viper/VideoCodec.qml | 150 + qml/Viper/main.qml | 945 ++ qml/Viper/utils.js | 100 + qml/images/Slider_bar.png | Bin 0 -> 1129 bytes qml/images/Slider_handle.png | Bin 0 -> 2784 bytes qml/images/Triangle-.png | Bin 0 -> 2630 bytes qml/images/b.jpeg | Bin 0 -> 3505 bytes qml/images/b.png | Bin 0 -> 15241 bytes qml/images/buffering.png | Bin 0 -> 10397 bytes qml/images/fullscreen-selected.png | Bin 0 -> 1726 bytes qml/images/fullscreen.png | Bin 0 -> 1514 bytes qml/images/fullscreen.svg | 13 + qml/images/graph-selected.png | Bin 0 -> 44245 bytes qml/images/graph.png | Bin 0 -> 42097 bytes qml/images/open-selected.png | Bin 0 -> 40342 bytes qml/images/open.png | Bin 0 -> 22792 bytes qml/images/option-connections-selected.png | Bin 0 -> 85424 bytes qml/images/option-connections.png | Bin 0 -> 78054 bytes qml/images/options-selected.png | Bin 0 -> 43617 bytes qml/images/options.png | Bin 0 -> 38165 bytes qml/images/pause.svg | 14 + qml/images/play.svg | 13 + qml/images/qt-logo.png | Bin 0 -> 9186 bytes qml/images/repeat-selected.png | Bin 0 -> 4638 bytes qml/images/repeat.png | Bin 0 -> 4136 bytes qml/images/stop.svg | 13 + viper.pro | 316 + viper.qrc | 35 + websocketpp/base64/base64.hpp | 178 + websocketpp/client.hpp | 33 + websocketpp/close.hpp | 342 + websocketpp/common/asio.hpp | 131 + websocketpp/common/asio_ssl.hpp | 39 + websocketpp/common/chrono.hpp | 68 + websocketpp/common/connection_hdl.hpp | 52 + websocketpp/common/cpp11.hpp | 162 + websocketpp/common/functional.hpp | 105 + websocketpp/common/md5.hpp | 448 + websocketpp/common/memory.hpp | 89 + websocketpp/common/network.hpp | 106 + websocketpp/common/platforms.hpp | 46 + websocketpp/common/random.hpp | 82 + websocketpp/common/regex.hpp | 59 + websocketpp/common/stdint.hpp | 73 + websocketpp/common/system_error.hpp | 84 + websocketpp/common/thread.hpp | 84 + websocketpp/common/time.hpp | 56 + websocketpp/common/type_traits.hpp | 65 + websocketpp/concurrency/basic.hpp | 46 + websocketpp/concurrency/none.hpp | 80 + websocketpp/config/asio.hpp | 77 + websocketpp/config/asio_client.hpp | 77 + websocketpp/config/asio_no_tls.hpp | 73 + websocketpp/config/asio_no_tls_client.hpp | 73 + websocketpp/config/boost_config.hpp | 72 + websocketpp/config/core.hpp | 285 + websocketpp/config/core_client.hpp | 294 + websocketpp/config/debug.hpp | 286 + websocketpp/config/debug_asio.hpp | 77 + websocketpp/config/debug_asio_no_tls.hpp | 73 + websocketpp/config/minimal_client.hpp | 72 + websocketpp/config/minimal_server.hpp | 312 + websocketpp/connection.hpp | 1651 +++ websocketpp/connection_base.hpp | 38 + websocketpp/endpoint.hpp | 700 ++ websocketpp/endpoint_base.hpp | 38 + websocketpp/error.hpp | 277 + websocketpp/extensions/extension.hpp | 102 + .../extensions/permessage_deflate/disabled.hpp | 128 + .../extensions/permessage_deflate/enabled.hpp | 752 ++ websocketpp/frame.hpp | 861 ++ websocketpp/http/constants.hpp | 308 + websocketpp/http/impl/parser.hpp | 196 + websocketpp/http/impl/request.hpp | 191 + websocketpp/http/impl/response.hpp | 266 + websocketpp/http/parser.hpp | 619 ++ websocketpp/http/request.hpp | 124 + websocketpp/http/response.hpp | 188 + websocketpp/impl/connection_impl.hpp | 2372 ++++ websocketpp/impl/endpoint_impl.hpp | 269 + websocketpp/impl/utilities_impl.hpp | 87 + websocketpp/logger/basic.hpp | 199 + websocketpp/logger/levels.hpp | 203 + websocketpp/logger/stub.hpp | 119 + websocketpp/logger/syslog.hpp | 146 + websocketpp/message_buffer/alloc.hpp | 105 + websocketpp/message_buffer/message.hpp | 340 + websocketpp/message_buffer/pool.hpp | 229 + websocketpp/processors/base.hpp | 299 + websocketpp/processors/hybi00.hpp | 462 + websocketpp/processors/hybi07.hpp | 78 + websocketpp/processors/hybi08.hpp | 83 + websocketpp/processors/hybi13.hpp | 1056 ++ websocketpp/processors/processor.hpp | 407 + websocketpp/random/none.hpp | 60 + websocketpp/random/random_device.hpp | 80 + websocketpp/roles/client_endpoint.hpp | 173 + websocketpp/roles/server_endpoint.hpp | 190 + websocketpp/server.hpp | 33 + websocketpp/sha1/sha1.hpp | 189 + websocketpp/transport/asio/base.hpp | 232 + websocketpp/transport/asio/connection.hpp | 1204 ++ websocketpp/transport/asio/endpoint.hpp | 1147 ++ websocketpp/transport/asio/security/base.hpp | 159 + websocketpp/transport/asio/security/none.hpp | 370 + websocketpp/transport/asio/security/tls.hpp | 484 + websocketpp/transport/base/connection.hpp | 238 + websocketpp/transport/base/endpoint.hpp | 77 + websocketpp/transport/debug/base.hpp | 104 + websocketpp/transport/debug/connection.hpp | 412 + websocketpp/transport/debug/endpoint.hpp | 140 + websocketpp/transport/iostream/base.hpp | 133 + websocketpp/transport/iostream/connection.hpp | 714 ++ websocketpp/transport/iostream/endpoint.hpp | 222 + websocketpp/transport/stub/base.hpp | 95 + websocketpp/transport/stub/connection.hpp | 286 + websocketpp/transport/stub/endpoint.hpp | 140 + websocketpp/uri.hpp | 355 + websocketpp/utf8_validator.hpp | 154 + websocketpp/utilities.hpp | 182 + websocketpp/version.hpp | 61 + 369 files changed, 66868 insertions(+) create mode 100644 .gitignore create mode 100644 .qmake.stash create mode 100644 AUTHORS create mode 100644 Adaptation/AbstractAdaptationLogic.cpp create mode 100644 Adaptation/AbstractAdaptationLogic.h create mode 100644 Adaptation/AdaptationLogicFactory.cpp create mode 100644 Adaptation/AdaptationLogicFactory.h create mode 100644 Adaptation/AlwaysLowestLogic.cpp create mode 100644 Adaptation/AlwaysLowestLogic.h create mode 100644 Adaptation/Bola.cpp create mode 100644 Adaptation/Bola.h create mode 100644 Adaptation/BufferBasedAdaptation.cpp create mode 100644 Adaptation/BufferBasedAdaptation.h create mode 100644 Adaptation/BufferBasedAdaptationWithRateBased.cpp create mode 100644 Adaptation/BufferBasedAdaptationWithRateBased.h create mode 100644 Adaptation/BufferBasedThreeThresholdAdaptation.cpp create mode 100644 Adaptation/BufferBasedThreeThresholdAdaptation.h create mode 100644 Adaptation/IAdaptationLogic.h create mode 100644 Adaptation/Panda.cpp create mode 100644 Adaptation/Panda.h create mode 100644 Adaptation/RateBasedAdaptation.cpp create mode 100644 Adaptation/RateBasedAdaptation.h create mode 100644 Buffer/Buffer.h create mode 100644 Buffer/IBufferObserver.h create mode 100644 Common/Common.cpp create mode 100644 Common/Common.h create mode 100644 Common/CommonExport.h create mode 100644 Common/Config.cpp create mode 100644 Common/Config.h create mode 100644 Common/Info.plist create mode 100644 Common/QOptions.cpp create mode 100644 Common/QOptions.h create mode 100755 Common/QtQuick2ApplicationViewer.cpp create mode 100755 Common/QtQuick2ApplicationViewer.h create mode 100644 Common/Viper.icns create mode 100644 Common/ViperBuffer.cpp create mode 100644 Common/ViperBuffer.h create mode 100644 Input/DASHManager.cpp create mode 100644 Input/DASHManager.h create mode 100644 Input/DASHReceiver.cpp create mode 100644 Input/DASHReceiver.h create mode 100644 Input/ICNConnectionConsumerApi.cpp create mode 100644 Input/ICNConnectionConsumerApi.h create mode 100644 Input/IDASHManagerObserver.h create mode 100644 Input/IDASHReceiverObserver.h create mode 100644 Input/IDataReceiver.h create mode 100644 Input/IICNConnection.h create mode 100644 Input/MediaObject.cpp create mode 100644 Input/MediaObject.h create mode 100644 MPD/AbstractRepresentationStream.cpp create mode 100644 MPD/AbstractRepresentationStream.h create mode 100644 MPD/AdaptationSetHelper.cpp create mode 100644 MPD/AdaptationSetHelper.h create mode 100644 MPD/AdaptationSetStream.cpp create mode 100644 MPD/AdaptationSetStream.h create mode 100644 MPD/BaseUrlResolver.cpp create mode 100644 MPD/BaseUrlResolver.h create mode 100644 MPD/IRepresentationStream.h create mode 100644 MPD/RepresentationStreamFactory.cpp create mode 100644 MPD/RepresentationStreamFactory.h create mode 100644 MPD/SegmentListStream.cpp create mode 100644 MPD/SegmentListStream.h create mode 100644 MPD/SegmentTemplateStream.cpp create mode 100644 MPD/SegmentTemplateStream.h create mode 100644 MPD/SingleMediaSegmentStream.cpp create mode 100644 MPD/SingleMediaSegmentStream.h create mode 100644 MPD/TimeResolver.cpp create mode 100644 MPD/TimeResolver.h create mode 100644 Managers/IMultimediaManagerBase.h create mode 100644 Managers/IMultimediaManagerObserver.h create mode 100644 Managers/IStreamObserver.h create mode 100644 Managers/MultimediaManager.cpp create mode 100644 Managers/MultimediaManager.h create mode 100644 Managers/MultimediaStream.cpp create mode 100644 Managers/MultimediaStream.h create mode 100644 Portable/MultiThreading.cpp create mode 100644 Portable/MultiThreading.h create mode 100644 Portable/Networking.h create mode 100644 README.md create mode 100644 UI/DASHPlayer.cpp create mode 100644 UI/DASHPlayer.h create mode 100644 UI/DASHPlayerNoGUI.cpp create mode 100644 UI/DASHPlayerNoGUI.h create mode 100644 UI/GraphDataSource.cpp create mode 100644 UI/GraphDataSource.h create mode 100644 UI/IDASHPlayerGuiObserver.h create mode 100644 UI/IDASHPlayerNoGuiObserver.h create mode 100644 UI/ViperGui.cpp create mode 100644 UI/ViperGui.h create mode 100644 Websocket/WebSocketService.cpp create mode 100644 Websocket/WebSocketService.h create mode 100644 Websocket/communication-protocol.cpp create mode 100644 Websocket/communication-protocol.h create mode 100644 Websocket/connection-pool.cpp create mode 100644 Websocket/connection-pool.h create mode 100644 Websocket/json.h create mode 100644 Websocket/query.cpp create mode 100644 Websocket/query.h create mode 100644 Websocket/tcp-server.cpp create mode 100644 Websocket/tcp-server.h create mode 100644 Websocket/websocket-server.cpp create mode 100644 Websocket/websocket-server.h create mode 100644 android/AndroidManifest.xml create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100755 android/gradlew create mode 100644 android/gradlew.bat create mode 100755 android/res/drawable-hdpi/icon.png create mode 100755 android/res/drawable-ldpi/icon.png create mode 100755 android/res/drawable-mdpi/icon.png create mode 100644 android/res/values/libs.xml create mode 100755 android/src/org/player/viper/ViperActivity.java create mode 100644 debug.h create mode 100644 libdash/Authors.txt create mode 100644 libdash/CMakeLists.txt create mode 100644 libdash/include/IAdaptationSet.h create mode 100644 libdash/include/IBaseUrl.h create mode 100644 libdash/include/IChunk.h create mode 100644 libdash/include/IConnection.h create mode 100644 libdash/include/IContentComponent.h create mode 100644 libdash/include/IDASHManager.h create mode 100644 libdash/include/IDASHMetrics.h create mode 100644 libdash/include/IDescriptor.h create mode 100644 libdash/include/IDownloadObserver.h create mode 100644 libdash/include/IDownloadObserver.h.save create mode 100644 libdash/include/IDownloadableChunk.h create mode 100644 libdash/include/IHTTPTransaction.h create mode 100644 libdash/include/IMPD.h create mode 100644 libdash/include/IMPDElement.h create mode 100644 libdash/include/IMetrics.h create mode 100644 libdash/include/IMultipleSegmentBase.h create mode 100644 libdash/include/INode.h create mode 100644 libdash/include/IPeriod.h create mode 100644 libdash/include/IProgramInformation.h create mode 100644 libdash/include/IRange.h create mode 100644 libdash/include/IRepresentation.h create mode 100644 libdash/include/IRepresentationBase.h create mode 100644 libdash/include/ISegment.h create mode 100644 libdash/include/ISegmentBase.h create mode 100644 libdash/include/ISegmentList.h create mode 100644 libdash/include/ISegmentTemplate.h create mode 100644 libdash/include/ISegmentTimeline.h create mode 100644 libdash/include/ISegmentURL.h create mode 100644 libdash/include/ISubRepresentation.h create mode 100644 libdash/include/ISubset.h create mode 100644 libdash/include/ITCPConnection.h create mode 100644 libdash/include/IThroughputMeasurement.h create mode 100644 libdash/include/ITimeline.h create mode 100644 libdash/include/IURLType.h create mode 100644 libdash/include/config.h create mode 100644 libdash/include/libdash.h create mode 100644 libdash/license.txt create mode 100644 libdash/mainpage.dox create mode 100644 libdash/source/defaults.mk create mode 100644 libdash/source/dllmain.cpp create mode 100644 libdash/source/helpers/Block.h create mode 100644 libdash/source/helpers/BlockStream.cpp create mode 100644 libdash/source/helpers/BlockStream.h create mode 100644 libdash/source/helpers/Path.cpp create mode 100644 libdash/source/helpers/Path.h create mode 100644 libdash/source/helpers/String.cpp create mode 100644 libdash/source/helpers/String.h create mode 100644 libdash/source/helpers/SyncedBlockStream.cpp create mode 100644 libdash/source/helpers/SyncedBlockStream.h create mode 100644 libdash/source/helpers/Time.cpp create mode 100644 libdash/source/helpers/Time.h create mode 100644 libdash/source/libdash.cpp create mode 100644 libdash/source/manager/DASHManager.cpp create mode 100644 libdash/source/manager/DASHManager.h create mode 100644 libdash/source/metrics/HTTPTransaction.cpp create mode 100644 libdash/source/metrics/HTTPTransaction.h create mode 100644 libdash/source/metrics/TCPConnection.cpp create mode 100644 libdash/source/metrics/TCPConnection.h create mode 100644 libdash/source/metrics/ThroughputMeasurement.cpp create mode 100644 libdash/source/metrics/ThroughputMeasurement.h create mode 100644 libdash/source/mpd/AbstractMPDElement.cpp create mode 100644 libdash/source/mpd/AbstractMPDElement.h create mode 100644 libdash/source/mpd/AdaptationSet.cpp create mode 100644 libdash/source/mpd/AdaptationSet.h create mode 100644 libdash/source/mpd/BaseUrl.cpp create mode 100644 libdash/source/mpd/BaseUrl.h create mode 100644 libdash/source/mpd/ContentComponent.cpp create mode 100644 libdash/source/mpd/ContentComponent.h create mode 100644 libdash/source/mpd/Descriptor.cpp create mode 100644 libdash/source/mpd/Descriptor.h create mode 100644 libdash/source/mpd/MPD.cpp create mode 100644 libdash/source/mpd/MPD.h create mode 100644 libdash/source/mpd/Metrics.cpp create mode 100644 libdash/source/mpd/Metrics.h create mode 100644 libdash/source/mpd/MultipleSegmentBase.cpp create mode 100644 libdash/source/mpd/MultipleSegmentBase.h create mode 100644 libdash/source/mpd/Period.cpp create mode 100644 libdash/source/mpd/Period.h create mode 100644 libdash/source/mpd/ProgramInformation.cpp create mode 100644 libdash/source/mpd/ProgramInformation.h create mode 100644 libdash/source/mpd/Range.cpp create mode 100644 libdash/source/mpd/Range.h create mode 100644 libdash/source/mpd/Representation.cpp create mode 100644 libdash/source/mpd/Representation.h create mode 100644 libdash/source/mpd/RepresentationBase.cpp create mode 100644 libdash/source/mpd/RepresentationBase.h create mode 100644 libdash/source/mpd/Segment.cpp create mode 100644 libdash/source/mpd/Segment.h create mode 100644 libdash/source/mpd/SegmentBase.cpp create mode 100644 libdash/source/mpd/SegmentBase.h create mode 100644 libdash/source/mpd/SegmentList.cpp create mode 100644 libdash/source/mpd/SegmentList.h create mode 100644 libdash/source/mpd/SegmentTemplate.cpp create mode 100644 libdash/source/mpd/SegmentTemplate.h create mode 100644 libdash/source/mpd/SegmentTimeline.cpp create mode 100644 libdash/source/mpd/SegmentTimeline.h create mode 100644 libdash/source/mpd/SegmentURL.cpp create mode 100644 libdash/source/mpd/SegmentURL.h create mode 100644 libdash/source/mpd/SubRepresentation.cpp create mode 100644 libdash/source/mpd/SubRepresentation.h create mode 100644 libdash/source/mpd/Subset.cpp create mode 100644 libdash/source/mpd/Subset.h create mode 100644 libdash/source/mpd/Timeline.cpp create mode 100644 libdash/source/mpd/Timeline.h create mode 100644 libdash/source/mpd/URLType.cpp create mode 100644 libdash/source/mpd/URLType.h create mode 100644 libdash/source/network/AbstractChunk.cpp create mode 100644 libdash/source/network/AbstractChunk.h create mode 100644 libdash/source/network/DownloadStateManager.cpp create mode 100644 libdash/source/network/DownloadStateManager.h create mode 100644 libdash/source/portable/MultiThreading.cpp create mode 100644 libdash/source/portable/MultiThreading.h create mode 100644 libdash/source/portable/Networking.h create mode 100644 libdash/source/sublibs.mk create mode 100644 libdash/source/targetver.h create mode 100644 libdash/source/xml/DOMHelper.cpp create mode 100644 libdash/source/xml/DOMHelper.h create mode 100644 libdash/source/xml/DOMParser.cpp create mode 100644 libdash/source/xml/DOMParser.h create mode 100644 libdash/source/xml/Node.cpp create mode 100644 libdash/source/xml/Node.h create mode 100644 main.cpp create mode 100644 player.conf create mode 100755 qml/Viper/Button.qml create mode 100755 qml/Viper/ControlPanel.qml create mode 100755 qml/Viper/DelegateItem.qml create mode 100755 qml/Viper/GraphPanel.qml create mode 100755 qml/Viper/OpenMpd.qml create mode 100755 qml/Viper/OptionConnections.qml create mode 100755 qml/Viper/Options.qml create mode 100755 qml/Viper/ProgressBar.qml create mode 100755 qml/Viper/Slider.qml create mode 100755 qml/Viper/VideoCodec.qml create mode 100755 qml/Viper/main.qml create mode 100755 qml/Viper/utils.js create mode 100755 qml/images/Slider_bar.png create mode 100755 qml/images/Slider_handle.png create mode 100755 qml/images/Triangle-.png create mode 100755 qml/images/b.jpeg create mode 100755 qml/images/b.png create mode 100755 qml/images/buffering.png create mode 100755 qml/images/fullscreen-selected.png create mode 100755 qml/images/fullscreen.png create mode 100755 qml/images/fullscreen.svg create mode 100755 qml/images/graph-selected.png create mode 100755 qml/images/graph.png create mode 100755 qml/images/open-selected.png create mode 100755 qml/images/open.png create mode 100644 qml/images/option-connections-selected.png create mode 100644 qml/images/option-connections.png create mode 100755 qml/images/options-selected.png create mode 100755 qml/images/options.png create mode 100755 qml/images/pause.svg create mode 100755 qml/images/play.svg create mode 100755 qml/images/qt-logo.png create mode 100755 qml/images/repeat-selected.png create mode 100755 qml/images/repeat.png create mode 100755 qml/images/stop.svg create mode 100644 viper.pro create mode 100644 viper.qrc create mode 100644 websocketpp/base64/base64.hpp create mode 100644 websocketpp/client.hpp create mode 100644 websocketpp/close.hpp create mode 100644 websocketpp/common/asio.hpp create mode 100644 websocketpp/common/asio_ssl.hpp create mode 100644 websocketpp/common/chrono.hpp create mode 100644 websocketpp/common/connection_hdl.hpp create mode 100644 websocketpp/common/cpp11.hpp create mode 100644 websocketpp/common/functional.hpp create mode 100644 websocketpp/common/md5.hpp create mode 100644 websocketpp/common/memory.hpp create mode 100644 websocketpp/common/network.hpp create mode 100644 websocketpp/common/platforms.hpp create mode 100644 websocketpp/common/random.hpp create mode 100644 websocketpp/common/regex.hpp create mode 100644 websocketpp/common/stdint.hpp create mode 100644 websocketpp/common/system_error.hpp create mode 100644 websocketpp/common/thread.hpp create mode 100644 websocketpp/common/time.hpp create mode 100644 websocketpp/common/type_traits.hpp create mode 100644 websocketpp/concurrency/basic.hpp create mode 100644 websocketpp/concurrency/none.hpp create mode 100644 websocketpp/config/asio.hpp create mode 100644 websocketpp/config/asio_client.hpp create mode 100644 websocketpp/config/asio_no_tls.hpp create mode 100644 websocketpp/config/asio_no_tls_client.hpp create mode 100644 websocketpp/config/boost_config.hpp create mode 100644 websocketpp/config/core.hpp create mode 100644 websocketpp/config/core_client.hpp create mode 100644 websocketpp/config/debug.hpp create mode 100644 websocketpp/config/debug_asio.hpp create mode 100644 websocketpp/config/debug_asio_no_tls.hpp create mode 100644 websocketpp/config/minimal_client.hpp create mode 100644 websocketpp/config/minimal_server.hpp create mode 100644 websocketpp/connection.hpp create mode 100644 websocketpp/connection_base.hpp create mode 100644 websocketpp/endpoint.hpp create mode 100644 websocketpp/endpoint_base.hpp create mode 100644 websocketpp/error.hpp create mode 100644 websocketpp/extensions/extension.hpp create mode 100644 websocketpp/extensions/permessage_deflate/disabled.hpp create mode 100644 websocketpp/extensions/permessage_deflate/enabled.hpp create mode 100644 websocketpp/frame.hpp create mode 100644 websocketpp/http/constants.hpp create mode 100644 websocketpp/http/impl/parser.hpp create mode 100644 websocketpp/http/impl/request.hpp create mode 100644 websocketpp/http/impl/response.hpp create mode 100644 websocketpp/http/parser.hpp create mode 100644 websocketpp/http/request.hpp create mode 100644 websocketpp/http/response.hpp create mode 100644 websocketpp/impl/connection_impl.hpp create mode 100644 websocketpp/impl/endpoint_impl.hpp create mode 100644 websocketpp/impl/utilities_impl.hpp create mode 100644 websocketpp/logger/basic.hpp create mode 100644 websocketpp/logger/levels.hpp create mode 100644 websocketpp/logger/stub.hpp create mode 100644 websocketpp/logger/syslog.hpp create mode 100644 websocketpp/message_buffer/alloc.hpp create mode 100644 websocketpp/message_buffer/message.hpp create mode 100644 websocketpp/message_buffer/pool.hpp create mode 100644 websocketpp/processors/base.hpp create mode 100644 websocketpp/processors/hybi00.hpp create mode 100644 websocketpp/processors/hybi07.hpp create mode 100644 websocketpp/processors/hybi08.hpp create mode 100644 websocketpp/processors/hybi13.hpp create mode 100644 websocketpp/processors/processor.hpp create mode 100644 websocketpp/random/none.hpp create mode 100644 websocketpp/random/random_device.hpp create mode 100644 websocketpp/roles/client_endpoint.hpp create mode 100644 websocketpp/roles/server_endpoint.hpp create mode 100644 websocketpp/server.hpp create mode 100644 websocketpp/sha1/sha1.hpp create mode 100644 websocketpp/transport/asio/base.hpp create mode 100644 websocketpp/transport/asio/connection.hpp create mode 100644 websocketpp/transport/asio/endpoint.hpp create mode 100644 websocketpp/transport/asio/security/base.hpp create mode 100644 websocketpp/transport/asio/security/none.hpp create mode 100644 websocketpp/transport/asio/security/tls.hpp create mode 100644 websocketpp/transport/base/connection.hpp create mode 100644 websocketpp/transport/base/endpoint.hpp create mode 100644 websocketpp/transport/debug/base.hpp create mode 100644 websocketpp/transport/debug/connection.hpp create mode 100644 websocketpp/transport/debug/endpoint.hpp create mode 100644 websocketpp/transport/iostream/base.hpp create mode 100644 websocketpp/transport/iostream/connection.hpp create mode 100644 websocketpp/transport/iostream/endpoint.hpp create mode 100644 websocketpp/transport/stub/base.hpp create mode 100644 websocketpp/transport/stub/connection.hpp create mode 100644 websocketpp/transport/stub/endpoint.hpp create mode 100644 websocketpp/uri.hpp create mode 100644 websocketpp/utf8_validator.hpp create mode 100644 websocketpp/utilities.hpp create mode 100644 websocketpp/version.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3edf202c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build* +*.pro.user* diff --git a/.qmake.stash b/.qmake.stash new file mode 100644 index 00000000..d717b0a6 --- /dev/null +++ b/.qmake.stash @@ -0,0 +1,15 @@ +QMAKE_DEFAULT_INCDIRS = \ + /usr/include/c++/5 \ + /usr/include/x86_64-linux-gnu/c++/5 \ + /usr/include/c++/5/backward \ + /usr/lib/gcc/x86_64-linux-gnu/5/include \ + /usr/local/include \ + /usr/lib/gcc/x86_64-linux-gnu/5/include-fixed \ + /usr/include/x86_64-linux-gnu \ + /usr/include +QMAKE_DEFAULT_LIBDIRS = \ + /usr/lib/gcc/x86_64-linux-gnu/5 \ + /usr/lib/x86_64-linux-gnu \ + /usr/lib \ + /lib/x86_64-linux-gnu \ + /lib diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..6a299e3a --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Viper authors are listed below + + Jacques Samain + Angelo Mantellini + +Copyright (c) 2016-2017 Cisco and/or its affiliates. diff --git a/Adaptation/AbstractAdaptationLogic.cpp b/Adaptation/AbstractAdaptationLogic.cpp new file mode 100644 index 00000000..4f578958 --- /dev/null +++ b/Adaptation/AbstractAdaptationLogic.cpp @@ -0,0 +1,54 @@ +/* + * AbstractAdaptationLogic.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AbstractAdaptationLogic.h" + +using namespace libdash::framework::adaptation; +using namespace dash::mpd; + +AbstractAdaptationLogic::AbstractAdaptationLogic(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid) : + mpd (mpd), + period (period), + adaptationSet (adaptationSet), + representation (NULL), + isVideo (isVid) +{ +} + +AbstractAdaptationLogic::~AbstractAdaptationLogic() +{ +} + +uint32_t AbstractAdaptationLogic::getPosition() +{ + return 0; +} + +void AbstractAdaptationLogic::setPosition(uint32_t segmentNumber) +{ + this->segmentNumber = segmentNumber; +} + +IRepresentation* AbstractAdaptationLogic::getRepresentation() +{ + return this->representation; +} + +void AbstractAdaptationLogic::setRepresentation(IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + this->period = period; + this->adaptationSet = adaptationSet; + this->representation = representation; +} + +void AbstractAdaptationLogic::dLTimeUpdate(double time) +{ +} diff --git a/Adaptation/AbstractAdaptationLogic.h b/Adaptation/AbstractAdaptationLogic.h new file mode 100644 index 00000000..24fab91d --- /dev/null +++ b/Adaptation/AbstractAdaptationLogic.h @@ -0,0 +1,58 @@ +/* + * AbstractAdaptationLogic.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_ABSTRACTADAPTATIONLOGIC_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_ABSTRACTADAPTATIONLOGIC_H_ + +#include "IAdaptationLogic.h" +#include "IMPD.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class AbstractAdaptationLogic : public IAdaptationLogic +{ +public: + AbstractAdaptationLogic(dash::mpd::IMPD *mpd, dash::mpd::IPeriod* period, dash::mpd::IAdaptationSet *adaptationSet, bool isVideo); + virtual ~AbstractAdaptationLogic(); + + virtual uint32_t getPosition(); + virtual void setPosition(uint32_t segmentNumber); + virtual dash::mpd::IRepresentation* getRepresentation (); + virtual void setRepresentation(dash::mpd::IPeriod *period, + dash::mpd::IAdaptationSet *adaptationSet, + dash::mpd::IRepresentation *representation); + + virtual LogicType getType() = 0; + virtual bool isUserDependent() = 0; + virtual bool isRateBased() = 0; + virtual bool isBufferBased() = 0; + virtual void bitrateUpdate(uint64_t, uint32_t) = 0; + virtual void bufferUpdate(uint32_t, int) = 0; + virtual void onEOS(bool value)= 0; + virtual void dLTimeUpdate(double) = 0; + + virtual void checkedByDASHReceiver() = 0; +protected: + dash::mpd::IMPD *mpd; + dash::mpd::IPeriod *period; + dash::mpd::IAdaptationSet *adaptationSet; + dash::mpd::IRepresentation *representation; + uint32_t segmentNumber; + bool isVideo; +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_ABSTRACTADAPTATIONLOGIC_H_ */ diff --git a/Adaptation/AdaptationLogicFactory.cpp b/Adaptation/AdaptationLogicFactory.cpp new file mode 100644 index 00000000..6901a597 --- /dev/null +++ b/Adaptation/AdaptationLogicFactory.cpp @@ -0,0 +1,48 @@ +/* + * AdaptationLogicFactory.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AdaptationLogicFactory.h" +#include + +using namespace libdash::framework::adaptation; +using namespace dash::mpd; + +IAdaptationLogic* AdaptationLogicFactory::create(LogicType logic, IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet,bool isVid, struct AdaptationParameters* paramsForAdaptation) +{ + Debug("Adaptation Logic for %s: ", isVid ? "video" : "audio"); + switch(logic) + { + case AlwaysLowest: + Debug("Always lowest\n"); + return new AlwaysLowestLogic(mpd, period, adaptationSet, isVid, paramsForAdaptation); + case RateBased: + Debug("Rate based\n"); + return new RateBasedAdaptation(mpd,period,adaptationSet, isVid, paramsForAdaptation); + case BufferBased: + Debug("Buffer based\n"); + return new BufferBasedAdaptation(mpd,period,adaptationSet, isVid, paramsForAdaptation); + case BufferRateBased: + Debug("Buffer Rate based\n"); + return new BufferBasedAdaptationWithRateBased(mpd,period,adaptationSet, isVid, paramsForAdaptation); + case BufferBasedThreeThreshold: + Debug("Buffer based 3 threshold\n"); + return new BufferBasedThreeThresholdAdaptation(mpd,period,adaptationSet, isVid, paramsForAdaptation); + case Panda: + Debug("Panda\n"); + return new PandaAdaptation(mpd, period, adaptationSet, isVid, paramsForAdaptation); + case Bola: + Debug("Bola\n"); + return new BolaAdaptation(mpd, period, adaptationSet, isVid, paramsForAdaptation); + default: + Debug("default => return Always Lowest\n"); + return new AlwaysLowestLogic(mpd, period, adaptationSet, isVid, paramsForAdaptation); + } +} diff --git a/Adaptation/AdaptationLogicFactory.h b/Adaptation/AdaptationLogicFactory.h new file mode 100644 index 00000000..0f1616a7 --- /dev/null +++ b/Adaptation/AdaptationLogicFactory.h @@ -0,0 +1,40 @@ +/* + * AdaptationLogicFactory.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_ADAPTATIONLOGICFACTORY_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_ADAPTATIONLOGICFACTORY_H_ + +#include "IAdaptationLogic.h" +#include "AlwaysLowestLogic.h" +#include "RateBasedAdaptation.h" +#include "BufferBasedAdaptation.h" +#include "BufferBasedAdaptationWithRateBased.h" +#include "BufferBasedThreeThresholdAdaptation.h" +#include "Panda.h" +#include "Bola.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class AdaptationLogicFactory +{ +public: + static IAdaptationLogic* create(libdash::framework::adaptation::LogicType logic, + dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_ADAPTATIONLOGICFACTORY_H_ */ diff --git a/Adaptation/AlwaysLowestLogic.cpp b/Adaptation/AlwaysLowestLogic.cpp new file mode 100644 index 00000000..54409bfd --- /dev/null +++ b/Adaptation/AlwaysLowestLogic.cpp @@ -0,0 +1,71 @@ +/* + * AlwaysLowestLogic.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AlwaysLowestLogic.h" +#include + +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace dash::mpd; + +AlwaysLowestLogic::AlwaysLowestLogic(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic(mpd, period, adaptationSet, isVid) +{ + this->representation = this->adaptationSet->GetRepresentation().at(0); +} + +AlwaysLowestLogic::~AlwaysLowestLogic() +{ +} + +LogicType AlwaysLowestLogic::getType() +{ + return adaptation::AlwaysLowest; +} + +bool AlwaysLowestLogic::isUserDependent() +{ + return false; +} + +bool AlwaysLowestLogic::isRateBased() +{ + return false; +} + +bool AlwaysLowestLogic::isBufferBased() +{ + return false; +} + +void AlwaysLowestLogic::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ +} + +void AlwaysLowestLogic::bufferUpdate(uint32_t bufferfill, int maxC) +{ +} + +void AlwaysLowestLogic::setMultimediaManager(viper::managers::IMultimediaManagerBase *mmM) +{ +} + +void AlwaysLowestLogic::onEOS(bool value) +{ +} + +void AlwaysLowestLogic::dLTimeUpdate(double time) +{ +} + +void AlwaysLowestLogic::checkedByDASHReceiver() +{ +} diff --git a/Adaptation/AlwaysLowestLogic.h b/Adaptation/AlwaysLowestLogic.h new file mode 100644 index 00000000..782a8b29 --- /dev/null +++ b/Adaptation/AlwaysLowestLogic.h @@ -0,0 +1,48 @@ +/* + * AlwaysLowestLogic.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_ALWAYSLOWESTLOGIC_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_ALWAYSLOWESTLOGIC_H_ + +#include "IMPD.h" +#include "AbstractAdaptationLogic.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class AlwaysLowestLogic : public AbstractAdaptationLogic +{ +public: + AlwaysLowestLogic(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~AlwaysLowestLogic(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t, uint32_t); + virtual void dLTimeUpdate(double time); + virtual void bufferUpdate(uint32_t, int); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *mmM); + virtual void onEOS(bool value); + virtual void checkedByDASHReceiver(); +private: + + +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_ALWAYSLOWESTLOGIC_H_ */ diff --git a/Adaptation/Bola.cpp b/Adaptation/Bola.cpp new file mode 100644 index 00000000..6477e0f5 --- /dev/null +++ b/Adaptation/Bola.cpp @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Bola.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const double MINIMUM_BUFFER_LEVEL_SPACING = 5.0; // The minimum space required between buffer levels (in seconds). +const uint32_t THROUGHPUT_SAMPLES = 3; // Number of samples considered for throughput estimate. +const double SAFETY_FACTOR = 0.9; // Safety factor used with bandwidth estimate. + +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +using std::bind; +using std::placeholders::_1; +using std::placeholders::_2; + + +using duration_in_seconds = std::chrono::duration >; + +BolaAdaptation::BolaAdaptation(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic (mpd, period, adaptationSet, isVid) +{ + this->bufferMaxSizeSeconds =(double) params->segmentBufferSize * params->segmentDuration; + this->alphaRate = params->Bola_Alpha; + this->bufferTargetSeconds = params->Bola_bufferTargetSeconds; + + // Set Bola init STATE + this->initState = true; + this->bolaState = STARTUP; + + this->lastDownloadTimeInstant = 0.0; + this->currentDownloadTimeInstant = 0.0; + //this->lastSegmentDownloadTime = 0.0; + this->currentQuality = 0; + + this->bufferTargetPerc = (uint32_t) ( round(this->bufferTargetSeconds / this->bufferMaxSizeSeconds)*100 ); + + /// Retrieve available bitrates + std::vector representations = this->adaptationSet->GetRepresentation(); + + this->availableBitrates.clear(); + Debug("BOLA Available Bitrates...\n"); + for(size_t i = 0; i < representations.size(); i++) + { + this->availableBitrates.push_back((uint64_t)(representations.at(i)->GetBandwidth())); + Debug("%d - %I64u bps\n", i+1, this->availableBitrates[i]); + } + // Check if they are in increasing order (i.e., bitrate[0] <= bitrate[1], etc.) + + this->bitrateCount = this->availableBitrates.size(); + + // We check if we have only one birate value or if the bitrate list is irregular (i.e., it is not strictly increasing) + if (this->bitrateCount < 2 || this->availableBitrates[0] >= this->availableBitrates[1] || this->availableBitrates[this->bitrateCount - 2] >= this->availableBitrates[this->bitrateCount - 1]) { + this->bolaState = ONE_BITRATE; + // return 0; // Check if exit with a message is necessary + } + + // Check if the following is correct + this->totalDuration = TimeResolver::getDurationInSec(this->mpd->GetMediaPresentationDuration()); +// this->segmentDuration = (double) (representations.at(0)->GetSegmentTemplate()->GetDuration() / representations.at(0)->GetSegmentTemplate()->GetTimescale() ); + this->segmentDuration = 2.0; + Debug("Total Duration - BOLA:\t%f\nSegment Duration - BOLA:\t%f\n",this->totalDuration, this->segmentDuration); + // if not correct --> segmentDuration = 2.0; + + // Compute the BOLA Buffer Target + this->bolaBufferTargetSeconds = this->bufferTargetSeconds; + if (this->bolaBufferTargetSeconds < this->segmentDuration + MINIMUM_BUFFER_LEVEL_SPACING) + { + this->bolaBufferTargetSeconds = this->segmentDuration + MINIMUM_BUFFER_LEVEL_SPACING; + } + Debug("BOLA Buffer Target Seconds:\t%f\n",this->bolaBufferTargetSeconds); + + // Compute UTILTY vector, Vp, and gp + Debug("BOLA Utility Values...\n"); + for (uint32_t i = 0; i < this->bitrateCount; ++i) { + this->utilityVector.push_back( log(((double)this->availableBitrates[i] * (1./(double)this->availableBitrates[0])))); + Debug("%d - %f\n", i+1, this->utilityVector[i]); + } + + this->Vp = (this->bolaBufferTargetSeconds - this->segmentDuration) / this->utilityVector[this->bitrateCount - 1]; + this->gp = 1.0 + this->utilityVector[this->bitrateCount - 1] / (this->bolaBufferTargetSeconds / this->segmentDuration - 1.0); + + Debug("BOLA Parameters:\tVp: %f\tgp: %f\n",this->Vp, this->gp); + /* If bufferTargetSeconds (not bolaBufferTargetSecond) is large enough, we might guarantee that Bola will never rebuffer + * unless the network bandwidth drops below the lowest encoded bitrate level. For this to work, Bola needs to use the real buffer + * level without the additional virtualBuffer. Also, for this to work efficiently, we need to make sure that if the buffer level + * drops to one fragment during a download, the current download does not have more bits remaining than the size of one fragment + * at the lowest quality*/ + + this->maxRtt = 0.2; // Is this reasonable? + if(this->bolaBufferTargetSeconds == this->bufferTargetSeconds) { + this->safetyGuarantee = true; + } + if (this->safetyGuarantee) { + Debug("BOLA SafetyGuarantee...\n"); + // we might need to adjust Vp and gp + double VpNew = this->Vp; + double gpNew = this->gp; + for (uint32_t i = 1; i < this->bitrateCount; ++i) { + double threshold = VpNew * (gpNew - this->availableBitrates[0] * this->utilityVector[i] / (this->availableBitrates[i] - this->availableBitrates[0])); + double minThreshold = this->segmentDuration * (2.0 - this->availableBitrates[0] / this->availableBitrates[i]) + maxRtt; + if (minThreshold >= this->bufferTargetSeconds) { + safetyGuarantee = false; + break; + } + if (threshold < minThreshold) { + VpNew = VpNew * (this->bufferTargetSeconds - minThreshold) / (this->bufferTargetSeconds - threshold); + gpNew = minThreshold / VpNew + this->utilityVector[i] * this->availableBitrates[0] / (this->availableBitrates[i] - this->availableBitrates[0]); + } + } + if (safetyGuarantee && (this->bufferTargetSeconds - this->segmentDuration) * VpNew / this->Vp < MINIMUM_BUFFER_LEVEL_SPACING) { + safetyGuarantee = false; + } + if (safetyGuarantee) { + this->Vp = VpNew; + this->gp = gpNew; + } + } + + Debug("BOLA New Parameters:\tVp: %f\tgp: %f\n",this->Vp, this->gp); + + // Capping of the virtual buffer (when using it) + this->bolaBufferMaxSeconds = this->Vp * (this->utilityVector[this->bitrateCount - 1] + this->gp); + Debug("BOLA Max Buffer Seconds:\t%f\n",this->bolaBufferMaxSeconds); + + this->virtualBuffer = 0.0; // Check if we should use either the virtualBuffer or the safetyGuarantee + + this->instantBw = 0; + this->averageBw = 0; + this->batchBw = 0; // Computed every THROUGHPUT_SAMPLES samples (average) + this->batchBwCount = 0; + + this->multimediaManager = NULL; + this->lastBufferFill = 0; // (?) + this->bufferEOS = false; + this->shouldAbort = false; + this->isCheckedForReceiver = false; + + this->representation = representations.at(0); + this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + + Debug("BOLA Init Params - \tAlpha: %f \t BufferTarget: %f\n",this->alphaRate, this->bufferTargetSeconds); + Debug("BOLA Init Current BitRate - %I64u\n",this->currentBitrate); + Debug("Buffer Adaptation BOLA: STARTED\n"); +} + +BolaAdaptation::~BolaAdaptation() +{ +} + +LogicType BolaAdaptation::getType() +{ + return adaptation::BufferBased; +} + +bool BolaAdaptation::isUserDependent() +{ + return false; +} + +bool BolaAdaptation::isRateBased() +{ + return true; +} +bool BolaAdaptation::isBufferBased() +{ + return true; +} + +void BolaAdaptation::setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void BolaAdaptation::notifyBitrateChange() +{ + if(this->multimediaManager) + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); + //Should Abort is done here to avoid race condition with DASHReceiver::DoBuffering() + if(this->shouldAbort) + { + this->multimediaManager->shouldAbort(this->isVideo); + } + this->shouldAbort = false; +} + +uint64_t BolaAdaptation::getBitrate() +{ + return this->currentBitrate; +} + +int BolaAdaptation::getQualityFromThroughput(uint64_t bps) { + int q = 0; + for (int i = 0; i < this->availableBitrates.size(); ++i) { + if (this->availableBitrates[i] > bps) { + break; + } + q = i; + } + return q; +} + +int BolaAdaptation::getQualityFromBufferLevel(double bufferLevelSec) { + int quality = this->bitrateCount - 1; + double score = 0.0; + for (int i = 0; i < this->bitrateCount; ++i) { + double s = (this->utilityVector[i] + this->gp - bufferLevelSec / this->Vp) / this->availableBitrates[i]; + if (s > score) { + score = s; + quality = i; + } + } + return quality; +} + +void BolaAdaptation::setBitrate(uint32_t bufferFill) +{ + // *** NB *** Insert Log messages + + if(this->initState) + { + this->initState = false; + + if(this->bolaState != ONE_BITRATE) + { + if(this->batchBw != 0) // Check the current estimated throughput (batch mean) + this->currentQuality = getQualityFromThroughput(this->batchBw*SAFETY_FACTOR); + //else --> quality unchanged + } + //this->representation = this->availableBitrates[this->currentQuality]; + //this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + this->representation = this->adaptationSet->GetRepresentation().at(this->currentQuality); + this->currentBitrate = (uint64_t) this->availableBitrates[this->currentQuality]; + Debug("INIT - Current Bitrate:\t%I64u\n", this->currentBitrate); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->averageBw , this->currentQuality); + this->lastBufferFill = bufferFill; + return; + } + + if(this->bolaState == ONE_BITRATE) { + this->currentQuality = 0; + //this->representation = this->availableBitrates[this->currentQuality]; + //this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + this->representation = this->adaptationSet->GetRepresentation().at(this->currentQuality); + this->currentBitrate = (uint64_t) this->availableBitrates[this->currentQuality]; + Debug("ONE BITRATE - Current Bitrate:\t%I64u\n", this->currentBitrate); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->averageBw , this->currentQuality); + this->lastBufferFill = bufferFill; + return; + } + + // Obtain bufferFill in seconds; + double bufferLevelSeconds = (double)( (bufferFill * this->bufferMaxSizeSeconds) *1./100); + int bolaQuality = getQualityFromBufferLevel(bufferLevelSeconds); + + Debug("REGULAR - Buffer Level Seconds:\t%f; Bola Quality:\t%d\n", bufferLevelSeconds, bolaQuality); + + + if (bufferLevelSeconds <= 0.1) { + // rebuffering occurred, reset virtual buffer + this->virtualBuffer = 0.0; + } + + // We check if the safetyGuarantee should be used. if not, we use the virtual buffer + // STILL NOT COMPLETE; Find a way to retrieved time since the last download + if (!this->safetyGuarantee) // we can use virtualBuffer + { + // find out if there was delay because of lack of availability or because bolaBufferTarget > bufferTarget + // TODO + //double timeSinceLastDownload = getDelayFromLastFragmentInSeconds(); // Define function + double timeSinceLastDownload = this->currentDownloadTimeInstant - this->lastDownloadTimeInstant; + + Debug("VirtualBuffer - Time Since Last Download:\t%f\n", timeSinceLastDownload); + + if (timeSinceLastDownload > 0.0) { + this->virtualBuffer += timeSinceLastDownload; + } + if ( (bufferLevelSeconds + this->virtualBuffer) > this->bolaBufferMaxSeconds) { + this->virtualBuffer = this->bolaBufferMaxSeconds - bufferLevelSeconds; + } + if (this->virtualBuffer < 0.0) { + this->virtualBuffer = 0.0; + } + + Debug("VirtualBuffer - Virtual Buffer Value:\t%f\n", this->virtualBuffer); + + // Update currentDownloadTimeInstant + this->lastDownloadTimeInstant = this->currentDownloadTimeInstant; + + // Update bolaQuality using virtualBuffer: bufferLevel might be artificially low because of lack of availability + + int bolaQualityVirtual = getQualityFromBufferLevel(bufferLevelSeconds + this->virtualBuffer); + Debug("VirtualBuffer - Bola Quality Virtual:\t%d\n", bolaQualityVirtual); + if (bolaQualityVirtual > bolaQuality) { + // May use quality higher than that indicated by real buffer level. + // In this case, make sure there is enough throughput to download a fragment before real buffer runs out. + int maxQuality = bolaQuality; + while (maxQuality < bolaQualityVirtual && (this->availableBitrates[maxQuality + 1] * this->segmentDuration) / (this->currentBitrate * SAFETY_FACTOR) < bufferLevelSeconds) + { + ++maxQuality; + } + // TODO: maybe we can use a more conservative level here, but this should be OK + Debug("VirtualBuffer - Bola Quality Virtual HIGHER than Bola Quality - Max Quality:\t%d\n", maxQuality); + if (maxQuality > bolaQuality) + { + // We can (and will) download at a quality higher than that indicated by real buffer level. + if (bolaQualityVirtual <= maxQuality) { + // we can download fragment indicated by real+virtual buffer without rebuffering + bolaQuality = bolaQualityVirtual; + } else { + // downloading fragment indicated by real+virtual rebuffers, use lower quality + bolaQuality = maxQuality; + // deflate virtual buffer to match quality + double targetBufferLevel = this->Vp * (this->gp + this->utilityVector[bolaQuality]); + if (bufferLevelSeconds + this->virtualBuffer > targetBufferLevel) { + this->virtualBuffer = targetBufferLevel - bufferLevelSeconds; + if (this->virtualBuffer < 0.0) { // should be false + this->virtualBuffer = 0.0; + } + } + } + } + } + } + + + if (this->bolaState == STARTUP || this->bolaState == STARTUP_NO_INC) { + // in startup phase, use some throughput estimation + + int quality = getQualityFromThroughput(this->batchBw*SAFETY_FACTOR); + + if (this->batchBw <= 0.0) { + // something went wrong - go to steady state + this->bolaState = STEADY; + } + if (this->bolaState == STARTUP && quality < this->currentQuality) { + // Since the quality is decreasing during startup, it will not be allowed to increase again. + this->bolaState = STARTUP_NO_INC; + } + if (this->bolaState == STARTUP_NO_INC && quality > this->currentQuality) { + // In this state the quality is not allowed to increase until steady state. + quality = this->currentQuality; + } + if (quality <= bolaQuality) { + // Since the buffer is full enough for steady state operation to match startup operation, switch over to steady state. + this->bolaState = STEADY; + } + if (this->bolaState != STEADY) { + // still in startup mode + this->currentQuality = quality; + //this->representation = this->availableBitrates[this->currentQuality]; + //this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + this->representation = this->adaptationSet->GetRepresentation().at(this->currentQuality); + this->currentBitrate = (uint64_t) this->availableBitrates[this->currentQuality]; + Debug("STILL IN STARTUP - Current Bitrate:\t%I64u\n", this->currentBitrate); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->averageBw , this->currentQuality); + this->lastBufferFill = bufferFill; + return; + } + } + + // Steady State + + // In order to avoid oscillation, the "BOLA-O" variant is implemented. + // When network bandwidth lies between two encoded bitrate levels, stick to the lowest one. + double delaySeconds = 0.0; + if (bolaQuality > this->currentQuality) { + Debug("STEADY -- BOLA QUALITY:\t%d - HIGHER than - CURRENT QUALITY:\t%I64u\n", bolaQuality, this->currentBitrate); + // do not multiply throughput by bandwidthSafetyFactor here; + // we are not using throughput estimation but capping bitrate to avoid oscillations + int quality = getQualityFromThroughput(this->batchBw); + if (bolaQuality > quality) { + // only intervene if we are trying to *increase* quality to an *unsustainable* level + if (quality < this->currentQuality) { + // The aim is only to avoid oscillations - do not drop below current quality + quality = this->currentQuality; + } else { + // We are dropping to an encoded bitrate which is a little less than the network bandwidth + // since bitrate levels are discrete. Quality 'quality' might lead to buffer inflation, + // so we deflate the buffer to the level that 'quality' gives positive utility. + double targetBufferLevel = this->Vp * (this->utilityVector[quality] + this->gp); + delaySeconds = bufferLevelSeconds - targetBufferLevel; + } + bolaQuality = quality; + } + } + + if (delaySeconds > 0.0) { + // first reduce virtual buffer + if (delaySeconds > this->virtualBuffer) { + delaySeconds -= this->virtualBuffer; + this->virtualBuffer = 0.0; + } else { + this->virtualBuffer -= delaySeconds; + delaySeconds = 0.0; + } + } + if (delaySeconds > 0.0) { + // TODO Check the scope of this function. Is it a delayed request? + // streamProcessor.getScheduleController().setTimeToLoadDelay(1000.0 * delaySeconds); + // NEED TO CHECK THIS + Debug("STEADY -- DELAY DOWNLOAD OF:\t%f\n", delaySeconds); + this->multimediaManager->setTargetDownloadingTime(this->isVideo, delaySeconds); + } + + this->currentQuality = bolaQuality; + //this->representation = this->availableBitrates[this->currentQuality]; + this->representation = this->adaptationSet->GetRepresentation().at(this->currentQuality); + this->currentBitrate = (uint64_t) this->availableBitrates[this->currentQuality]; + Debug("STEADY - Current Bitrate:\t%I64u\n", this->currentBitrate); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->averageBw , this->currentQuality); + this->lastBufferFill = bufferFill; +} + +void BolaAdaptation::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ + this->instantBw = bps; + + // Avg bandwidth estimate with EWMA + if(this->averageBw == 0) + { + this->averageBw = bps; + } + else + { + this->averageBw = this->alphaRate*this->averageBw + (1 - this->alphaRate)*bps; + } + + // Avg bandwidth estimate with batch mean of THROUGHPUT_SAMPLES sample + this->batchBwCount++; + this->batchBwSamples.push_back(bps); + + if(this->batchBwCount++ == THROUGHPUT_SAMPLES) + { + for(int i=0; ibatchBw += this->batchBwSamples[i]; + + this->batchBw /= THROUGHPUT_SAMPLES; + + Debug("BATCH BW:\t%I64u\n", this->batchBw); + + this->batchBwCount=0; + this->batchBwSamples.clear(); + } +} + +void BolaAdaptation::dLTimeUpdate(double time) +{ + auto m_now = std::chrono::system_clock::now(); + auto m_now_sec = std::chrono::time_point_cast(m_now); + + auto now_value = m_now_sec.time_since_epoch(); + double dl_instant = now_value.count(); + //this->lastSegmentDownloadTime = time; + //this->currentDownloadTimeInstant = std::chrono::duration_cast(system_clock::now()).count(); + this->currentDownloadTimeInstant = dl_instant; +} + +void BolaAdaptation::onEOS(bool value) +{ + this->bufferEOS = value; +} + +void BolaAdaptation::checkedByDASHReceiver() +{ + this->isCheckedForReceiver = false; +} +void BolaAdaptation::bufferUpdate(uint32_t bufferFill, int maxC) +{ + this->setBitrate(bufferFill); + this->notifyBitrateChange(); +} diff --git a/Adaptation/Bola.h b/Adaptation/Bola.h new file mode 100644 index 00000000..29011664 --- /dev/null +++ b/Adaptation/Bola.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_BOLA_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_BOLA_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class BolaAdaptation : public AbstractAdaptationLogic +{ +public: + BolaAdaptation (dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~BolaAdaptation(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void dLTimeUpdate(double time); + virtual void bufferUpdate(uint32_t bufferFill, int maxC); + void setBitrate(uint32_t bufferFill); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void checkedByDASHReceiver(); + + int getQualityFromThroughput(uint64_t bps); + int getQualityFromBufferLevel(double bufferLevelSec); + +private: + enum BolaState + { + ONE_BITRATE, // If one bitrate (or init failed), always NO_CHANGE + STARTUP, // Download fragments at most recently measured throughput + STARTUP_NO_INC, // If quality increased then decreased during startup, then quality cannot be increased. + STEADY // The buffer is primed (should be above bufferTarget) + }; + + bool initState; + double bufferMaxSizeSeconds; // Usually set to 30s + double bufferTargetSeconds; // It is passed as an init parameter. + // It states the difference between STARTUP and STEADY + // 12s following dash.js implementation + + double bolaBufferTargetSeconds; // BOLA introduces a virtual buffer level in order to make quality decisions + // as it was filled (instead of the actual bufferTargetSeconds) + + double bolaBufferMaxSeconds; // When using the virtual buffer, it must be capped. + + uint32_t bufferTargetPerc; // Computed considering a bufferSize = 30s + double totalDuration; // Total video duration in seconds (taken from MPD) + double segmentDuration; // Segment duration in seconds + + std::vector availableBitrates; + std::vector utilityVector; + uint32_t bitrateCount; // Number of available bitrates + BolaState bolaState; // Keeps track of Bola state + + // Bola Vp and gp (multiplied by the segment duration 'p') + // They are dimensioned such that log utility would always prefer + // - the lowest bitrate when bufferLevel = segmentDuration + // - the highest bitrate when bufferLevel = bufferTarget + double Vp; + double gp; + + bool safetyGuarantee; + double maxRtt; + + double virtualBuffer; + + uint64_t currentBitrate; + int currentQuality; + uint64_t batchBw; + int batchBwCount; + std::vector batchBwSamples; + uint64_t instantBw; + uint64_t averageBw; + + double lastDownloadTimeInstant; + double currentDownloadTimeInstant; + double lastSegmentDownloadTime; + + uint32_t lastBufferFill; + bool bufferEOS; + bool shouldAbort; + double alphaRate; + bool isCheckedForReceiver; + + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_BOLA_H_ */ diff --git a/Adaptation/BufferBasedAdaptation.cpp b/Adaptation/BufferBasedAdaptation.cpp new file mode 100644 index 00000000..18d4a592 --- /dev/null +++ b/Adaptation/BufferBasedAdaptation.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BufferBasedAdaptation.h" +#include + + +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +BufferBasedAdaptation::BufferBasedAdaptation (IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic (mpd, period, adaptationSet, isVid) +{ + this->reservoirThreshold = params->BufferBased_reservoirThreshold; + this->maxThreshold = params->BufferBased_maxThreshold; + + std::vector representations = this->adaptationSet->GetRepresentation(); + + this->representation = this->adaptationSet->GetRepresentation().at(0); + this->multimediaManager = NULL; + this->lastBufferFill = 0; + this->bufferEOS = false; + this->shouldAbort = false; + Debug("BufferBasedParams:\t%f\t%f\n", (double)reservoirThreshold/100, (double)maxThreshold/100); + Debug("Buffer Adaptation: STARTED\n"); +} + +BufferBasedAdaptation::~BufferBasedAdaptation () +{ +} + +LogicType BufferBasedAdaptation::getType() +{ + return adaptation::BufferBased; +} + +bool BufferBasedAdaptation::isUserDependent() +{ + return false; +} + +bool BufferBasedAdaptation::isRateBased() +{ + return false; +} +bool BufferBasedAdaptation::isBufferBased() +{ + return true; +} + +void BufferBasedAdaptation::setMultimediaManager (viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void BufferBasedAdaptation::notifyBitrateChange() +{ + if(this->multimediaManager) + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); + + if(this->shouldAbort) + { + this->multimediaManager->shouldAbort(this->isVideo); + } + this->shouldAbort = false; +} + +uint64_t BufferBasedAdaptation::getBitrate() +{ + return this->currentBitrate; +} + +void BufferBasedAdaptation::setBitrate(uint32_t bufferFill) +{ + std::vector representations; + representations = this->adaptationSet->GetRepresentation(); + size_t i = 0; + + if(representations.size() == 1) + { + i = 0; + } + else + { + while(bufferFill > this->reservoirThreshold + i * (this->maxThreshold - this->reservoirThreshold)/(representations.size()-1)) + { + i++; + } + } + if((size_t)i >= (size_t)(representations.size())) + i = representations.size() - 1; + this->representation = representations.at(i); + if( 0 && !this->bufferEOS && this->lastBufferFill > this->reservoirThreshold && bufferFill <= this->reservoirThreshold) + { + this->shouldAbort = true; + } + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, choice: %lu, should_trigger_abort: %s\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, i, this->shouldAbort ? "YES" : "NO"); + this->lastBufferFill = bufferFill; + +} + +void BufferBasedAdaptation::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ +} + +void BufferBasedAdaptation::dLTimeUpdate(double time) +{ +} + +void BufferBasedAdaptation::onEOS(bool value) +{ + this->bufferEOS = value; +} + +void BufferBasedAdaptation::checkedByDASHReceiver() +{ +} + +void BufferBasedAdaptation::bufferUpdate(uint32_t bufferFill, int maxC) +{ + this->setBitrate(bufferFill); + this->notifyBitrateChange(); +} diff --git a/Adaptation/BufferBasedAdaptation.h b/Adaptation/BufferBasedAdaptation.h new file mode 100644 index 00000000..32ad8045 --- /dev/null +++ b/Adaptation/BufferBasedAdaptation.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATION_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATION_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class BufferBasedAdaptation : public AbstractAdaptationLogic +{ +public: + BufferBasedAdaptation(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~BufferBasedAdaptation(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void bufferUpdate(uint32_t bufferFill, int maxC); + virtual void dLTimeUpdate(double time); + void setBitrate(uint32_t bufferFill); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void checkedByDASHReceiver(); + +private: + uint64_t currentBitrate; + std::vector availableBitrates; + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; + uint32_t reservoirThreshold; + uint32_t maxThreshold; + uint32_t lastBufferFill; + bool bufferEOS; + bool shouldAbort; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATION_H_ */ diff --git a/Adaptation/BufferBasedAdaptationWithRateBased.cpp b/Adaptation/BufferBasedAdaptationWithRateBased.cpp new file mode 100644 index 00000000..3a1895c0 --- /dev/null +++ b/Adaptation/BufferBasedAdaptationWithRateBased.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BufferBasedAdaptationWithRateBased.h" +#include + +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +BufferBasedAdaptationWithRateBased::BufferBasedAdaptationWithRateBased(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic(mpd, period, adaptationSet, isVid) +{ + this->alphaRate = params->Adaptech_Alpha; + this->reservoirThreshold = params->Adaptech_FirstThreshold; + this->maxThreshold = params->Adaptech_SecondThreshold; + this->switchUpThreshold = params->Adaptech_SwitchUpThreshold; + this->slackParam = params->Adaptech_SlackParameter; + + std::vector representations = this->adaptationSet->GetRepresentation(); + + this->m_count = 0; + this->instantBw = 0; + this->averageBw = 0; + this->representation = this->adaptationSet->GetRepresentation().at(0); + this->multimediaManager = NULL; + this->lastBufferFill = 0; + this->bufferEOS = false; + this->shouldAbort = false; + this->isCheckedForReceiver = false; + this->myQuality = 0; + Debug("BufferRateBasedParams:\talpha:%f\tfirst threshold: %f\tsecond threshold: %f\tswitch-up margin: %d\tSlack: %f\n",this->alphaRate, (double)reservoirThreshold/100, (double)maxThreshold/100, this->switchUpThreshold, this->slackParam); + Debug("Buffer Adaptation: STARTED\n"); +} +BufferBasedAdaptationWithRateBased::~BufferBasedAdaptationWithRateBased () +{ +} + +LogicType BufferBasedAdaptationWithRateBased::getType() +{ + return adaptation::BufferBased; +} + +bool BufferBasedAdaptationWithRateBased::isUserDependent() +{ + return false; +} + +bool BufferBasedAdaptationWithRateBased::isRateBased() +{ + return true; +} +bool BufferBasedAdaptationWithRateBased::isBufferBased() +{ + return true; +} + +void BufferBasedAdaptationWithRateBased::setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void BufferBasedAdaptationWithRateBased::notifyBitrateChange() +{ + if(this->multimediaManager) + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); + //Should Abort is done here to avoid race condition with DASHReceiver::DoBuffering() + if(this->shouldAbort) + { + this->multimediaManager->shouldAbort(this->isVideo); + } + this->shouldAbort = false; +} + +uint64_t BufferBasedAdaptationWithRateBased::getBitrate() +{ + return this->currentBitrate; +} + +void BufferBasedAdaptationWithRateBased::setBitrate(uint32_t bufferFill) +{ + uint32_t phi1, phi2; + std::vector representations; + representations = this->adaptationSet->GetRepresentation(); + size_t i = 0; + + Debug("bufferlevel: %u, instant rate %lu, average rate %lu\n", bufferFill, this->instantBw, this->averageBw); + phi1 = 0; + phi2 = 0; + while(i < representations.size()) + { + if(phi1 == 0 && representations.at(i)->GetBandwidth() > slackParam * this->instantBw) + { + phi1 = representations.at((i == 0) ? i : i -1)->GetBandwidth(); + } + if(phi2 == 0 && representations.at(i)->GetBandwidth() > slackParam * this->averageBw) + { + phi2 = representations.at((i == 0) ? i : i -1)->GetBandwidth(); + } + i++; + } + + if(!phi1) + phi1 = representations.at(representations.size() - 1)->GetBandwidth(); + + if(!phi2) + phi2 = representations.at(representations.size() - 1)->GetBandwidth(); + + if(bufferFill < this->reservoirThreshold) + { + this->m_count = 0; + this->myQuality = 0; + } + else + { + if(bufferFill < this->maxThreshold) + { + this->m_count = 0; + if(this->currentBitrate > phi1) + { + if(this->myQuality > 0) + { + this->myQuality--; + } + } + else + { + if(this->currentBitrate < phi1) + { + if(this->myQuality < representations.size() - 1) + { + this->myQuality++; + } + } + } + } + else + { // bufferFill > this->maxThreshold + if(this->currentBitrate < phi2) + { + m_count++; + + if(m_count >= switchUpThreshold && this->myQuality < representations.size() - 1) + { + this->m_count = 0; + this->myQuality++; + } + } + } + } + this->representation = representations.at(this->myQuality); + this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->averageBw , this->myQuality); +} + +void BufferBasedAdaptationWithRateBased::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ + Debug("rate estimation: %lu\n", bps); + this->instantBw = bps; + if(this->averageBw == 0) + { + this->averageBw = bps; + } + else + { + this->averageBw = this->alphaRate*this->averageBw + (1 - this->alphaRate)*bps; + } +} + +void BufferBasedAdaptationWithRateBased::onEOS(bool value) +{ + this->bufferEOS = value; +} + +void BufferBasedAdaptationWithRateBased::checkedByDASHReceiver() +{ + this->isCheckedForReceiver = false; +} +void BufferBasedAdaptationWithRateBased::bufferUpdate(uint32_t bufferFill, int maxC) +{ + Debug("buffer update: %u\n", bufferFill); + this->setBitrate(bufferFill); + this->notifyBitrateChange(); +} + +void BufferBasedAdaptationWithRateBased::dLTimeUpdate(double time) +{ +} + diff --git a/Adaptation/BufferBasedAdaptationWithRateBased.h b/Adaptation/BufferBasedAdaptationWithRateBased.h new file mode 100644 index 00000000..5c787d30 --- /dev/null +++ b/Adaptation/BufferBasedAdaptationWithRateBased.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONRATE_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONRATE_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class BufferBasedAdaptationWithRateBased : public AbstractAdaptationLogic +{ +public: + BufferBasedAdaptationWithRateBased(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~BufferBasedAdaptationWithRateBased(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void bufferUpdate(uint32_t bufferFill, int maxC); + virtual void dLTimeUpdate(double time); + void setBitrate(uint32_t bufferFill); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void checkedByDASHReceiver(); + +private: + uint64_t currentBitrate; + std::vector availableBitrates; + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; + uint32_t reservoirThreshold; + uint32_t maxThreshold; + uint32_t lastBufferFill; + int m_count; + int switchUpThreshold; + bool bufferEOS; + bool shouldAbort; + double alphaRate; + uint64_t averageBw; + uint64_t instantBw; + int myQuality; + double slackParam; + bool isCheckedForReceiver; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONRATE_H_ */ diff --git a/Adaptation/BufferBasedThreeThresholdAdaptation.cpp b/Adaptation/BufferBasedThreeThresholdAdaptation.cpp new file mode 100644 index 00000000..021e7b7c --- /dev/null +++ b/Adaptation/BufferBasedThreeThresholdAdaptation.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BufferBasedThreeThresholdAdaptation.h" +#include + +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +BufferBasedThreeThresholdAdaptation::BufferBasedThreeThresholdAdaptation(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic(mpd, period, adaptationSet, isVid) +{ + this->firstThreshold = params->BufferThreeThreshold_FirstThreshold; + this->secondThreshold = params->BufferThreeThreshold_SecondThreshold; + this->thirdThreshold = params->BufferThreeThreshold_ThirdThreshold; + this->slackParam = params->BufferThreeThreshold_slackParameter; + std::vector representations = this->adaptationSet->GetRepresentation(); + this->representation = this->adaptationSet->GetRepresentation().at(0); + this->multimediaManager = NULL; + this->lastBufferFill = 0; + this->bufferEOS = false; + this->shouldAbort = false; + this->isCheckedForReceiver = false; + Debug("BufferRateBasedParams:\t%f\t%f\t%f\n",(double)this->firstThreshold/100, (double)secondThreshold/100, (double)thirdThreshold/100); + Debug("Buffer Adaptation: STARTED\n"); +} + +BufferBasedThreeThresholdAdaptation::~BufferBasedThreeThresholdAdaptation() +{ +} + +LogicType BufferBasedThreeThresholdAdaptation::getType() +{ + return adaptation::BufferBasedThreeThreshold; +} + +bool BufferBasedThreeThresholdAdaptation::isUserDependent() +{ + return false; +} + +bool BufferBasedThreeThresholdAdaptation::isRateBased() +{ + return true; +} + +bool BufferBasedThreeThresholdAdaptation::isBufferBased() +{ + return true; +} + +void BufferBasedThreeThresholdAdaptation::setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void BufferBasedThreeThresholdAdaptation::notifyBitrateChange() +{ + if(this->multimediaManager) + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); + //Should Abort is done here to avoid race condition with DASHReceiver::DoBuffering() + if(this->shouldAbort) + { + this->multimediaManager->shouldAbort(this->isVideo); + } + this->shouldAbort = false; +} + +uint64_t BufferBasedThreeThresholdAdaptation::getBitrate() +{ + return this->currentBitrate; +} + +void BufferBasedThreeThresholdAdaptation::setBitrate(uint32_t bufferFill) +{ + uint32_t phi1, phi2; + std::vector representations; + representations = this->adaptationSet->GetRepresentation(); + size_t i = 0; + + if(this->isCheckedForReceiver) + { + return; + } + this->isCheckedForReceiver = true; + + + if(bufferFill < this->firstThreshold) + { + this->myQuality = 0; + } + else + { + if(bufferFill < this->secondThreshold) + { + if(this->currentBitrate >= this->instantBw) + { + if(this->myQuality > 0) + { + this->myQuality--; + } + } + } + else + { + if(bufferFill < this->thirdThreshold) + { + } + else + {// bufferLevel > thirdThreshold + if(this->currentBitrate <= this->instantBw) + { + if(this->myQuality < representations.size() - 1) + this->myQuality++; + } + } + } + } + this->representation = representations.at(this->myQuality); + this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferFill/100 , (double)bufferFill/100, this->instantBw, this->myQuality); +} + +void BufferBasedThreeThresholdAdaptation::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ + this->instantBw = bps; +} + +void BufferBasedThreeThresholdAdaptation::onEOS(bool value) +{ + this->bufferEOS = value; +} + +void BufferBasedThreeThresholdAdaptation::checkedByDASHReceiver() +{ + this->isCheckedForReceiver = false; +} +void BufferBasedThreeThresholdAdaptation::bufferUpdate(uint32_t bufferFill, int maxC) +{ + this->setBitrate(bufferFill); + this->notifyBitrateChange(); +} + +void BufferBasedThreeThresholdAdaptation::dLTimeUpdate(double time) +{ +} + + diff --git a/Adaptation/BufferBasedThreeThresholdAdaptation.h b/Adaptation/BufferBasedThreeThresholdAdaptation.h new file mode 100644 index 00000000..62160bc3 --- /dev/null +++ b/Adaptation/BufferBasedThreeThresholdAdaptation.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONTHREE_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONTHREE_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class BufferBasedThreeThresholdAdaptation : public AbstractAdaptationLogic +{ +public: + BufferBasedThreeThresholdAdaptation(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~BufferBasedThreeThresholdAdaptation(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void bufferUpdate(uint32_t bufferFill, int maxC); + virtual void dLTimeUpdate(double time); + void setBitrate(uint32_t bufferFill); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void checkedByDASHReceiver(); + +private: + uint64_t currentBitrate; + std::vector availableBitrates; + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; + uint32_t secondThreshold; + uint32_t thirdThreshold; + uint32_t lastBufferFill; + bool bufferEOS; + bool shouldAbort; + uint32_t firstThreshold; + uint64_t instantBw; + int myQuality; + double slackParam; + bool isCheckedForReceiver; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_BUFFERBASEDADAPTATIONTHREE_H_ */ diff --git a/Adaptation/IAdaptationLogic.h b/Adaptation/IAdaptationLogic.h new file mode 100644 index 00000000..bfe980c7 --- /dev/null +++ b/Adaptation/IAdaptationLogic.h @@ -0,0 +1,115 @@ +/* + * IAdaptationLogic.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_IADAPTATIONLOGIC_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_IADAPTATIONLOGIC_H_ +#include "../Input/MediaObject.h" +#include "../Input/DASHReceiver.h" +#include "IRepresentation.h" +#include "../Managers/IMultimediaManagerBase.h" + +namespace libdash +{ +namespace framework +{ +namespace input +{ +class DASHReceiver; +} +namespace adaptation +{ +//#define START __LINE__ +//ADAPTATIONLOGIC Count is an hack to have the number of adaptation logic that we can use +#define FOREACH_ADAPTATIONLOGIC(ADAPTATIONLOGIC) \ + ADAPTATIONLOGIC(AlwaysLowest) \ + ADAPTATIONLOGIC(RateBased) \ + ADAPTATIONLOGIC(BufferBased) \ + ADAPTATIONLOGIC(BufferRateBased) \ + ADAPTATIONLOGIC(BufferBasedThreeThreshold) \ + ADAPTATIONLOGIC(Panda) \ + ADAPTATIONLOGIC(Bola) \ + ADAPTATIONLOGIC(Count) \ + + +#define GENERATE_ENUM(ENUM) ENUM, +#define GENERATE_STRING(STRING) #STRING, + +enum LogicType { + FOREACH_ADAPTATIONLOGIC(GENERATE_ENUM) +}; + +static const char *LogicType_string[] = { + FOREACH_ADAPTATIONLOGIC(GENERATE_STRING) +}; + +class IAdaptationLogic +{ +public: + virtual ~IAdaptationLogic() {} + + virtual uint32_t getPosition() = 0; + virtual void setPosition(uint32_t segmentNumber) = 0; + virtual dash::mpd::IRepresentation* getRepresentation() = 0; + virtual void setRepresentation(dash::mpd::IPeriod *period, + dash::mpd::IAdaptationSet *adaptationSet, + dash::mpd::IRepresentation *representation)= 0; + virtual LogicType getType() = 0; + virtual bool isUserDependent()= 0; + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum) = 0; + virtual void dLTimeUpdate(double time) = 0; + virtual void bufferUpdate(uint32_t bufferfillstate, int maxC) = 0; + virtual bool isRateBased() = 0; + virtual bool isBufferBased() = 0; + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *mmManager)= 0; + virtual void onEOS(bool value) = 0; + virtual void checkedByDASHReceiver() = 0; +}; + +struct AdaptationParameters +{ + int segmentBufferSize; + double segmentDuration; + + //RATE BASED + double Rate_Alpha; + + //BUFFER BASED + int BufferBased_reservoirThreshold; + int BufferBased_maxThreshold; + + //BOLA + double Bola_Alpha; + double Bola_bufferTargetSeconds; + + //ADAPTECH + double Adaptech_Alpha; + int Adaptech_FirstThreshold; + int Adaptech_SecondThreshold; + int Adaptech_SwitchUpThreshold; + double Adaptech_SlackParameter; + + //BUFFER THREE THRESHOLDS + int BufferThreeThreshold_FirstThreshold; + int BufferThreeThreshold_SecondThreshold; + int BufferThreeThreshold_ThirdThreshold; + double BufferThreeThreshold_slackParameter; + //PANDA + double Panda_Alpha; + double Panda_Beta; + double Panda_Bmin; + double Panda_K; + double Panda_W; + double Panda_Epsilon; +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_IADAPTATIONLOGIC_H_ */ diff --git a/Adaptation/Panda.cpp b/Adaptation/Panda.cpp new file mode 100644 index 00000000..cb2ec660 --- /dev/null +++ b/Adaptation/Panda.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Panda.h" +#include + + +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +PandaAdaptation::PandaAdaptation(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic (mpd, period, adaptationSet, isVid) +{ + this->param_Alpha = params->Panda_Alpha; + this->param_Beta = params->Panda_Beta; + this->param_Bmin = params->Panda_Bmin; + this->param_K = params->Panda_K; + this->param_W = params->Panda_W; + this->param_Epsilon = params->Panda_Epsilon; + + this->segmentDuration = params->segmentDuration; + this->bufferMaxSizeSeconds = params->segmentBufferSize * this->segmentDuration; + this->targetBw = 0; + this->targetInterTime = 0.0; + + this->averageBw = 0; + this->smoothBw = 0; + this->instantBw = 0; + this->targetBw = 0; + + this->targetInterTime = 0.0; + this->interTime = 0.0; + + this->alpha_ewma = 0.8; + + this->bufferLevel = 0; + this->bufferLevelSeconds = 0.0; + + this->downloadTime = 0.0; + + this->isVideo = isVid; + this->mpd = mpd; + this->adaptationSet = adaptationSet; + this->period = period; + this->multimediaManager = NULL; + this->representation = NULL; + this->currentBitrate = 0; + this->current = 0; + + // Retrieve the available bitrates + std::vector representations = this->adaptationSet->GetRepresentation(); + + this->availableBitrates.clear(); + Debug("PANDA Available Bitrates...\n"); + for(size_t i = 0; i < representations.size(); i++) + { + this->availableBitrates.push_back((uint64_t)(representations.at(i)->GetBandwidth())); + Debug("%d - %I64u bps\n", i+1, this->availableBitrates[i]); + } + + this->representation = this->adaptationSet->GetRepresentation().at(0); + this->currentBitrate = (uint64_t) this->representation->GetBandwidth(); + + Debug("Panda parameters: K= %f, Bmin = %f, alpha = %f, beta = %f, W = %f\n", param_K, param_Bmin, param_Alpha, param_Beta, param_W); +} + +PandaAdaptation::~PandaAdaptation() { +} + +LogicType PandaAdaptation::getType() +{ + return adaptation::Panda; +} + +bool PandaAdaptation::isUserDependent() +{ + return false; +} + +bool PandaAdaptation::isRateBased() +{ + return true; +} + +bool PandaAdaptation::isBufferBased() +{ + return true; +} + +void PandaAdaptation::setMultimediaManager (viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void PandaAdaptation::notifyBitrateChange() +{ + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); +} + +uint64_t PandaAdaptation::getBitrate() +{ + return this->currentBitrate; +} + +void PandaAdaptation::quantizer() +{ + this->deltaUp = this->param_Epsilon * (double)this->smoothBw; + this->deltaDown = 0.0; + + Debug("** DELTA UP:\t%f\n", this->deltaUp); + + uint64_t smoothBw_UP = this->smoothBw - this->deltaUp; + uint64_t smoothBw_DOWN = this->smoothBw - this->deltaDown; + + Debug("** Smooth-BW UP:\t%d\t Smooth-BW DOWN:\t%d\n", smoothBw_UP, smoothBw_DOWN); + + std::vector representations; + representations = this->adaptationSet->GetRepresentation(); + uint32_t numQualLevels = representations.size(); + + // We have to find bitrateMin and bitrateMax + uint64_t bitrateDown, bitrateUp; + + // DOWN + uint32_t iDown = 0; + uint32_t i_d,i_u; + for (i_d = 0; i_d < this->availableBitrates.size(); ++i_d) { + if (this->availableBitrates[i_d] > smoothBw_DOWN) { + break; + } + } + if(i_d > 0) + iDown = i_d-1; + else + iDown = 0; + + bitrateDown = (uint64_t) representations.at(iDown)->GetBandwidth(); + Debug("** Bitrate DOWN:\t%d\t at Quality:\t%d\n", bitrateDown, iDown); + + // UP + uint32_t iUp = 0; + for (i_u = 0; i_u < this->availableBitrates.size(); ++i_u) { + if (this->availableBitrates[i_u] > smoothBw_UP) { + break; + } + } + if(i_u > 0) + iUp = i_u-1; + else + iUp = 0; + + bitrateUp = (uint64_t) representations.at(iUp)->GetBandwidth(); + Debug("** Bitrate UP:\t%d\t at Quality:\t%d\n", bitrateUp, iUp); + + Debug("** Current RATE:\t%d\n Current QUALITY:\t%d\n", this->currentBitrate, this->current); + + + // Next bitrate computation + if(this->currentBitrate < bitrateUp) + { + this->currentBitrate = bitrateUp; + this->current = iUp; + } + else if(this->currentBitrate <= bitrateDown && this->currentBitrate >= bitrateUp) + { + Debug("** CURRENT UNCHANGED **\n"); + } + else + { + this->currentBitrate = bitrateDown; + this->current = iDown; + } + this->representation = this->adaptationSet->GetRepresentation().at(this->current); +} + +void PandaAdaptation::setBitrate(uint64_t bps) +{ + + // 1. Calculating the targetBW + if(this->targetBw) + { + //this->targetBw = this->targetBw + param_K * this->interTime * (param_W - ((this->targetBw - bps + this->param_W) > 0 ? this->targetBw - bps + this->param_W: 0)); + if ((double)this->targetBw - (double)bps + this->param_W > 0) + this->targetBw = this->targetBw + (uint64_t)(param_K * this->interTime * (param_W - ((double)this->targetBw - (double)bps + this->param_W))); + else + this->targetBw = this->targetBw + (uint64_t)(param_K * this->interTime * param_W); + } + else + this->targetBw = bps; + + Debug("** INSTANTANEOUS BW:\t%d\n", bps); + Debug("** CLASSIC EWMA BW:\t%d\n", this->averageBw); + Debug("** PANDA TARGET BW:\t%d\n", this->targetBw); + + // 2. Calculating the smoothBW + if(this->interTime) + this->smoothBw = (uint64_t)((double)this->smoothBw - this->param_Alpha * this->interTime * ((double)this->smoothBw - (double)this->targetBw)); + else + this->smoothBw = this->targetBw; + + Debug("** PANDA SMOOTH BW:\t%d\n", this->smoothBw); + + // 3. Quantization + this->quantizer(); + Debug("ADAPTATION_LOGIC:\tFor %s:\tlast_buffer: %f\tbuffer_level: %f, instantaneousBw: %lu, AverageBW: %lu, choice: %d\n",isVideo ? "video" : "audio",(double)lastBufferLevel/100 , (double)bufferLevel/100, this->instantBw, this->averageBw , this->current); + this->lastBufferLevel = this->bufferLevel; + + // 4. Computing the "actual inter time" + this->bufferLevelSeconds = (double)((this->bufferLevel * this->bufferMaxSizeSeconds) *1./100); + this->targetInterTime = ((double)this->currentBitrate * segmentDuration) * 1./this->smoothBw + param_Beta * (this->bufferLevelSeconds - param_Bmin); + Debug("** TARGET INTER TIME:\t%f\n", this->targetInterTime); + Debug("** DOWNLOAD TIME:\t%f\n", this->downloadTime); + this->targetInterTime = (this->targetInterTime > 0) ? this->targetInterTime : 0.0; + this->interTime = this->targetInterTime > this->downloadTime ? this->targetInterTime : this->downloadTime; + this->interTime = this->interTime > 3 ? 3 : this->interTime; + + Debug("** ACTUAL INTER TIME:\t%f\n", this->interTime); + this->multimediaManager->setTargetDownloadingTime(this->isVideo, interTime); +} + +void PandaAdaptation::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ + this->instantBw = bps; + + // Avg bandwidth estimate with EWMA + if(this->averageBw == 0) + { + this->averageBw = bps; + } + else + { + this->averageBw = this->alpha_ewma*this->averageBw + (1 - this->alpha_ewma)*bps; + } + + this->setBitrate(bps); + this->notifyBitrateChange(); +} + +void PandaAdaptation::dLTimeUpdate(double time) +{ + this->downloadTime = time; +} + +void PandaAdaptation::bufferUpdate(uint32_t bufferfill, int maxC) +{ + Debug("bufferlvl: %d\n", bufferfill); + this->bufferLevel = bufferfill; +} + +void PandaAdaptation::onEOS(bool value) +{ +} + +void PandaAdaptation::checkedByDASHReceiver() +{ +} diff --git a/Adaptation/Panda.h b/Adaptation/Panda.h new file mode 100644 index 00000000..000131a1 --- /dev/null +++ b/Adaptation/Panda.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_PANDA_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_PANDA_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class PandaAdaptation : public AbstractAdaptationLogic +{ +public: + PandaAdaptation(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~PandaAdaptation(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void dLTimeUpdate(double time); + virtual void bufferUpdate(uint32_t bufferFill, int maxC); + void setBitrate(uint64_t bufferFill); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void checkedByDASHReceiver(); + + void quantizer(); +private: + uint64_t currentBitrate; + + std::vector availableBitrates; + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; + + uint64_t averageBw; // Classic EWMA + uint64_t instantBw; + uint64_t smoothBw; // Panda paper smoothed y[n] + uint64_t targetBw; // Panda paper x[n] bw estimation + + double param_Alpha; + double alpha_ewma; + double param_Epsilon; + double param_K; + double param_W; + double param_Beta; + double param_Bmin; + + double interTime; // Actual inter time + double targetInterTime; // Target inter time + double downloadTime; + + uint32_t bufferLevel; + uint32_t lastBufferLevel; + double bufferMaxSizeSeconds; // Usually set to 60s + double bufferLevelSeconds; // Current buffer level [s] + + double segmentDuration; + double deltaUp; + double deltaDown; + size_t current; +}; + +} /* namespace adaptation */ +} /* namespace framework */ +} /* namespace libdash */ + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_PANDA_H_ */ diff --git a/Adaptation/RateBasedAdaptation.cpp b/Adaptation/RateBasedAdaptation.cpp new file mode 100644 index 00000000..73d4ee9b --- /dev/null +++ b/Adaptation/RateBasedAdaptation.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RateBasedAdaptation.h" +#include +using namespace dash::mpd; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::mpd; + +RateBasedAdaptation::RateBasedAdaptation(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params) : + AbstractAdaptationLogic(mpd, period, adaptationSet, isVid) +{ + std::vector representations = this->adaptationSet->GetRepresentation(); + + this->availableBitrates.clear(); + for(size_t i = 0; i < representations.size(); i++) + { + this->availableBitrates.push_back((uint64_t)(representations.at(i)->GetBandwidth())); + } + this->currentBitrate = this->availableBitrates.at(0); + this->representation = representations.at(0); + this->multimediaManager = NULL; + this->alpha = params->Rate_Alpha; + Debug("RateBasedParams:\t%f\n",alpha); + this->averageBw = 0; +} + +RateBasedAdaptation::~RateBasedAdaptation() +{ +} + +LogicType RateBasedAdaptation::getType() +{ + return adaptation::RateBased; +} + +bool RateBasedAdaptation::isUserDependent() +{ + return false; +} + +bool RateBasedAdaptation::isRateBased() +{ + return true; +} + +bool RateBasedAdaptation::isBufferBased() +{ + return false; +} + +void RateBasedAdaptation::setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager) +{ + this->multimediaManager = _mmManager; +} + +void RateBasedAdaptation::notifyBitrateChange() +{ + if(this->multimediaManager->isStarted() && !this->multimediaManager->isStopping()) + if(this->isVideo) + this->multimediaManager->setVideoQuality(this->period, this->adaptationSet, this->representation); + else + this->multimediaManager->setAudioQuality(this->period, this->adaptationSet, this->representation); +} + +uint64_t RateBasedAdaptation::getBitrate() +{ + return this->currentBitrate; +} + +void RateBasedAdaptation::setBitrate(uint64_t bps) +{ + std::vector representations; + representations = this->adaptationSet->GetRepresentation(); + size_t i = 0; + this->ewma(bps); + for(i = 0;i < representations.size();i++) + { + if(representations.at(i)->GetBandwidth() > this->averageBw) + { + if(i > 0) + i--; + break; + } + } + if((size_t)i == (size_t)(representations.size())) + i = i-1; + + Debug("ADAPTATION_LOGIC:\tFor %s:\tBW_estimation(ewma): %lu, choice: %lu\n", (this->isVideo ? "video" : "audio"), this->averageBw, i); + this->representation = representations.at(i); + this->currentBitrate = this->representation->GetBandwidth(); +} + +void RateBasedAdaptation::bitrateUpdate(uint64_t bps, uint32_t segNum) +{ + Debug("Rate Based adaptation: speed received: %lu\n", bps); + this->setBitrate(bps); + this->notifyBitrateChange(); +} + +void RateBasedAdaptation::ewma(uint64_t bps) +{ + if(averageBw) + { + averageBw = alpha*averageBw + (1-alpha)*bps; + } + else + { + averageBw = bps; + } +} + +void RateBasedAdaptation::onEOS(bool value) +{ +} + +void RateBasedAdaptation::checkedByDASHReceiver() +{ +} + +void RateBasedAdaptation::bufferUpdate(uint32_t bufferfill, int maxC) +{ +} + +void RateBasedAdaptation::dLTimeUpdate(double time) +{ +} diff --git a/Adaptation/RateBasedAdaptation.h b/Adaptation/RateBasedAdaptation.h new file mode 100644 index 00000000..cbf7471c --- /dev/null +++ b/Adaptation/RateBasedAdaptation.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBDASH_FRAMEWORK_ADAPTATION_RATEBASEDADAPTATION_H_ +#define LIBDASH_FRAMEWORK_ADAPTATION_RATEBASEDADAPTATION_H_ + +#include "AbstractAdaptationLogic.h" +#include "../MPD/AdaptationSetStream.h" +#include "../Input/IDASHReceiverObserver.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class RateBasedAdaptation : public AbstractAdaptationLogic +{ +public: + RateBasedAdaptation(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, bool isVid, struct AdaptationParameters *params); + virtual ~RateBasedAdaptation(); + + virtual LogicType getType(); + virtual bool isUserDependent(); + virtual bool isRateBased(); + virtual bool isBufferBased(); + virtual void bitrateUpdate(uint64_t bps, uint32_t segNum); + virtual void bufferUpdate(uint32_t bufferfill, int maxC); + virtual void dLTimeUpdate(double time); + void setBitrate(uint64_t bps); + uint64_t getBitrate(); + virtual void setMultimediaManager(viper::managers::IMultimediaManagerBase *_mmManager); + void notifyBitrateChange(); + void onEOS(bool value); + void ewma(uint64_t bps); + void checkedByDASHReceiver(); +private: + uint64_t currentBitrate; + std::vector availableBitrates; + viper::managers::IMultimediaManagerBase *multimediaManager; + dash::mpd::IRepresentation *representation; + double alpha; + uint64_t averageBw; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_RATEBASEDADAPTATION_H_ */ diff --git a/Buffer/Buffer.h b/Buffer/Buffer.h new file mode 100644 index 00000000..1a95821c --- /dev/null +++ b/Buffer/Buffer.h @@ -0,0 +1,238 @@ +/* + * Buffer.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_BUFFER_BUFFER_H_ +#define LIBDASH_FRAMEWORK_BUFFER_BUFFER_H_ + +#include "../Portable/MultiThreading.h" +#include "IBufferObserver.h" +#include +#include + +namespace libdash +{ +namespace framework +{ + +namespace buffer +{ +template +class Buffer +{ +public: + Buffer(uint32_t maxcapacity, BufferType type); + virtual ~Buffer(); + + bool pushBack(T *element); + T* front(); + T* getFront(); + void popFront(); + void clearTail(); + void clear(); + void setEOS(bool value); + uint32_t length(); + uint32_t capacity(); + void attachObserver(IBufferObserver *observer); + void notify(); + +private: + std::deque objects; + std::vector observer; + bool eos; + uint32_t maxcapacity; + mutable CRITICAL_SECTION monitorMutex; + mutable CONDITION_VARIABLE full; + mutable CONDITION_VARIABLE empty; + BufferType type; +}; +} +} +} + +using namespace libdash::framework::buffer; + +template +Buffer::Buffer(uint32_t maxcapacity, BufferType type) : + type (type), + eos (false), + maxcapacity (maxcapacity) +{ + InitializeConditionVariable (&this->full); + InitializeConditionVariable (&this->empty); + InitializeCriticalSection (&this->monitorMutex); +} + +template Buffer::~Buffer() +{ + this->clear(); + + DeleteConditionVariable (&this->full); + DeleteConditionVariable (&this->empty); + DeleteCriticalSection (&this->monitorMutex); +} + +template bool Buffer::pushBack(T *object) +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->objects.size() >= this->maxcapacity && !this->eos) + SleepConditionVariableCS(&this->empty, &this->monitorMutex, INFINITE); + + if(this->objects.size() >= this->maxcapacity) + { + LeaveCriticalSection(&this->monitorMutex); + return false; + } + + this->objects.push_back(object); + + WakeAllConditionVariable(&this->full); + LeaveCriticalSection(&this->monitorMutex); + this->notify(); + return true; +} + +template T* Buffer::front() +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->objects.size() == 0 && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->objects.size() == 0) + { + LeaveCriticalSection(&this->monitorMutex); + return NULL; + } + + T *object = this->objects.front(); + + LeaveCriticalSection(&this->monitorMutex); + + return object; +} + +template T* Buffer::getFront() +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->objects.size() == 0 && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->objects.size() == 0) + { + LeaveCriticalSection(&this->monitorMutex); + return NULL; + } + + T *object = this->objects.front(); + this->objects.pop_front(); + this->objects.shrink_to_fit(); + WakeAllConditionVariable(&this->empty); + LeaveCriticalSection(&this->monitorMutex); + this->notify(); + + return object; +} + +template uint32_t Buffer::length() +{ + EnterCriticalSection(&this->monitorMutex); + + uint32_t ret = this->objects.size(); + + LeaveCriticalSection(&this->monitorMutex); + + return ret; +} + +template void Buffer::popFront() +{ + EnterCriticalSection(&this->monitorMutex); + + this->objects.pop_front(); + this->objects.shrink_to_fit(); + + WakeAllConditionVariable(&this->empty); + LeaveCriticalSection(&this->monitorMutex); + this->Notify(); +} + +template void Buffer::setEOS(bool value) +{ + EnterCriticalSection(&this->monitorMutex); + + this->eos = value; + WakeAllConditionVariable(&this->empty); + WakeAllConditionVariable(&this->full); + LeaveCriticalSection(&this->monitorMutex); +} + +template void Buffer::attachObserver(IBufferObserver *observer) +{ + this->observer.push_back(observer); +} + +template void Buffer::notify() +{ + for(size_t i = 0; i < this->observer.size(); i++) + this->observer.at(i)->onBufferStateChanged(this->type, (uint32_t)((double)this->objects.size()/(double)this->maxcapacity*100.0), this->maxcapacity); +} + +template void Buffer::clearTail() +{ + EnterCriticalSection(&this->monitorMutex); + + int size = this->objects.size() - 1; + + if (size < 1) + { + LeaveCriticalSection(&this->monitorMutex); + return; + } + + T* object = this->objects.front(); + this->objects.pop_front(); + for(int i=0; i < size; i++) + { + delete this->objects.front(); + this->objects.pop_front(); + } + this->objects.shrink_to_fit(); + + this->objects.push_back(object); + WakeAllConditionVariable(&this->empty); + WakeAllConditionVariable(&this->full); + LeaveCriticalSection(&this->monitorMutex); + this->Notify(); +} + +template void Buffer::clear() +{ + EnterCriticalSection(&this->monitorMutex); + + for(int i = 0; i < this->objects.size(); i++) + delete this->objects[i]; + + this->objects.clear(); + this->objects.shrink_to_fit(); + WakeAllConditionVariable(&this->empty); + WakeAllConditionVariable(&this->full); + LeaveCriticalSection(&this->monitorMutex); + this->notify(); +} + +template uint32_t Buffer::capacity() +{ + return this->maxcapacity; +} + +#endif /* LIBDASH_FRAMEWORK_BUFFER_BUFFER_H_ */ diff --git a/Buffer/IBufferObserver.h b/Buffer/IBufferObserver.h new file mode 100644 index 00000000..c3accfd5 --- /dev/null +++ b/Buffer/IBufferObserver.h @@ -0,0 +1,40 @@ +/* + * IBufferObserver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_BUFFER_IBUFFEROBSERVER_H_ +#define LIBDASH_FRAMEWORK_BUFFER_IBUFFEROBSERVER_H_ + +#include + +namespace libdash +{ +namespace framework +{ +namespace buffer +{ +enum BufferType +{ + AUDIO, + VIDEO, + SUBTITLE +}; + +class IBufferObserver +{ +public: + virtual ~IBufferObserver () {} + + virtual void onBufferStateChanged(BufferType type, uint32_t fillstateInPercent, int maxC) = 0; +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_BUFFER_IBUFFEROBSERVER_H_ */ diff --git a/Common/Common.cpp b/Common/Common.cpp new file mode 100644 index 00000000..56600ebe --- /dev/null +++ b/Common/Common.cpp @@ -0,0 +1,361 @@ +/****************************************************************************** + QtAV Player Demo: this file is part of QtAV examples + Copyright (C) 2012-2017 Wang Bin +* This file is part of QtAV (from 2014) + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include "Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +#include +#else +#include +#endif +#include + +#ifdef Q_OS_WINRT +#include +#include +#include +#include +#include +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::ApplicationModel::Activation; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Storage; +using namespace ABI::Windows::Storage::Pickers; + +#define COM_LOG_COMPONENT "WinRT" +#define COM_ENSURE(f, ...) COM_CHECK(f, return __VA_ARGS__;) +#define COM_WARN(f) COM_CHECK(f) +#define COM_CHECK(f, ...) \ + do { \ + HRESULT hr = f; \ + if (FAILED(hr)) { \ + qWarning() << QString::fromLatin1(COM_LOG_COMPONENT " error@%1. " #f ": (0x%2) %3").arg(__LINE__).arg(hr, 0, 16).arg(qt_error_string(hr)); \ + __VA_ARGS__ \ + } \ + } while (0) + +QString UrlFromFileArgs(IInspectable *args) +{ + ComPtr fileArgs; + COM_ENSURE(args->QueryInterface(fileArgs.GetAddressOf()), QString()); + ComPtr> files; + COM_ENSURE(fileArgs->get_Files(&files), QString()); + ComPtr item; + COM_ENSURE(files->GetAt(0, &item), QString()); + HString path; + COM_ENSURE(item->get_Path(path.GetAddressOf()), QString()); + + quint32 pathLen; + const wchar_t *pathStr = path.GetRawBuffer(&pathLen); + const QString filePath = QString::fromWCharArray(pathStr, pathLen); + qDebug() << "file path: " << filePath; + item->AddRef(); //ensure we can access it later. TODO: how to release? + return QString::fromLatin1("winrt:@%1:%2").arg((qint64)(qptrdiff)item.Get()).arg(filePath); +} +#endif + +Q_GLOBAL_STATIC(QFile, fileLogger) +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +class QMessageLogContext {}; +typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &); +QtMsgHandler qInstallMessageHandler(QtMessageHandler h) { + static QtMessageHandler hh; + hh = h; + struct MsgHandlerWrapper { + static void handler(QtMsgType type, const char *msg) { + static QMessageLogContext ctx; + hh(type, ctx, QString::fromUtf8(msg)); + } + }; + return qInstallMsgHandler(MsgHandlerWrapper::handler); +} +#endif +void Logger(QtMsgType type, const QMessageLogContext &, const QString& qmsg) +{ + const QByteArray msgArray = qmsg.toUtf8(); + const char* msg = msgArray.constData(); + switch (type) { + case QtDebugMsg: + printf("Debug: %s\n", msg); + fileLogger()->write(QByteArray("Debug: ")); + break; + case QtWarningMsg: + printf("Warning: %s\n", msg); + fileLogger()->write(QByteArray("Warning: ")); + break; + case QtCriticalMsg: + fprintf(stderr, "Critical: %s\n", msg); + fileLogger()->write(QByteArray("Critical: ")); + break; + case QtFatalMsg: + fprintf(stderr, "Fatal: %s\n", msg); + fileLogger()->write(QByteArray("Fatal: ")); + abort(); + } + fflush(0); + fileLogger()->write(msgArray); + fileLogger()->write(QByteArray("\n")); + fileLogger()->flush(); +} + +QOptions get_common_options() +{ + static QOptions ops = QOptions().addDescription(QString::fromLatin1("Options for QtAV players")) + .add(QString::fromLatin1("common options")) + ("help,h", QLatin1String("print this")) + ("ao", QString(), QLatin1String("audio output. Can be ordered combination of available backends (-ao help). Leave empty to use the default setting. Set 'null' to disable audio.")) + ("-egl", QLatin1String("Use EGL. Only works for Qt>=5.5+XCB")) + ("-gl", QLatin1String("OpenGL backend for Qt>=5.4(windows). can be 'desktop', 'opengles' and 'software'")) + ("x", 0, QString()) + ("y", 0, QLatin1String("y")) + ("-width", 800, QLatin1String("width of player")) + ("height", 450, QLatin1String("height of player")) + ("fullscreen", QLatin1String("fullscreen")) + ("decoder", QLatin1String("FFmpeg"), QLatin1String("use a given decoder")) + ("decoders,-vd", QLatin1String("cuda;vaapi;vda;dxva;cedarv;ffmpeg"), QLatin1String("decoder name list in priority order separated by ';'")) + ("file,f", QString(), QLatin1String("file or url to play")) + ("language", QString(), QLatin1String("language on UI. can be 'system' and locale name e.g. zh_CN")) + ("log", QString(), QLatin1String("log level. can be 'off', 'fatal', 'critical', 'warning', 'debug', 'all'")) + ("logfile" +#if defined(Q_OS_IOS) + , appDataDir().append(QString::fromLatin1("/log-%1.txt")) +#elif defined(Q_OS_WINRT) || defined(Q_OS_ANDROID) + , QString() +#else + , QString::fromLatin1("log-%1.txt") +#endif + , QString::fromLatin1("log to file. Set empty to disable log file (-logfile '')")) + ; + return ops; +} + +void do_common_options_before_qapp(const QOptions& options) +{ +#ifdef Q_OS_LINUX + QSettings cfg(Config::defaultConfigFile(), QSettings::IniFormat); + const bool set_egl = cfg.value("opengl/egl").toBool(); + //https://bugreports.qt.io/browse/QTBUG-49529 + // it's too late if qApp is created. but why ANGLE is not? + if (options.value(QString::fromLatin1("egl")).toBool() || set_egl) { //FIXME: Config is constructed too early because it requires qApp + // only apply to current run. no config change + qputenv("QT_XCB_GL_INTEGRATION", "xcb_egl"); + } else { + qputenv("QT_XCB_GL_INTEGRATION", "xcb_glx"); + } + qDebug() << "QT_XCB_GL_INTEGRATION: " << qgetenv("QT_XCB_GL_INTEGRATION"); +#endif //Q_OS_LINUX +} + +void do_common_options(const QOptions &options, const QString& appName) +{ + if (options.value(QString::fromLatin1("help")).toBool()) { + options.print(); + exit(0); + } + // has no effect if qInstallMessageHandler() called + //qSetMessagePattern("%{function} @%{line}: %{message}"); +#if !defined(Q_OS_WINRT) && !defined(Q_OS_ANDROID) + QString app(appName); + if (app.isEmpty() && qApp) + app = qApp->applicationName(); + QString logfile(options.option(QString::fromLatin1("logfile")).value().toString().arg(app)); + if (!logfile.isEmpty()) { + if (QDir(logfile).isRelative()) { + QString log_path(QString::fromLatin1("%1/%2").arg(qApp->applicationDirPath()).arg(logfile)); + QFile f(log_path); + if (!f.open(QIODevice::WriteOnly)) { + log_path = QString::fromLatin1("%1/%2").arg(appDataDir()).arg(logfile); + qDebug() << "executable dir is not writable. log to " << log_path; + } + logfile = log_path; + } + qDebug() << "set log file: " << logfile; + fileLogger()->setFileName(logfile); + if (fileLogger()->open(QIODevice::WriteOnly)) { + qDebug() << "Logger"; + qInstallMessageHandler(Logger); + } else { + qWarning() << "Failed to open log file '" << fileLogger()->fileName() << "': " << fileLogger()->errorString(); + } + } +#endif + QByteArray level(options.value(QString::fromLatin1("log")).toByteArray()); + if (level.isEmpty()) + level = Config::instance().logLevel().toLatin1(); + if (!level.isEmpty()) + qputenv("QTAV_LOG", level); +} + +void load_qm(const QStringList &names, const QString& lang) +{ + QString l(Config::instance().language()); + if (!lang.isEmpty()) + l = lang; + if (l.toLower() == QLatin1String("system")) + l = QLocale::system().name(); + QStringList qms(names); + qms << QLatin1String("QtAV") << QLatin1String("qt"); + foreach(QString qm, qms) { + QTranslator *ts = new QTranslator(qApp); + QString path = qApp->applicationDirPath() + QLatin1String("/i18n/") + qm + QLatin1String("_") + l; + //qDebug() << "loading qm: " << path; + if (ts->load(path)) { + qApp->installTranslator(ts); + } else { + path = QString::fromUtf8(":/i18n/%1_%2").arg(qm).arg(l); + //qDebug() << "loading qm: " << path; + if (ts->load(path)) + qApp->installTranslator(ts); + else + delete ts; + } + } + QTranslator qtts; + if (qtts.load(QLatin1String("qt_") + QLocale::system().name())) + qApp->installTranslator(&qtts); +} + +void set_opengl_backend(const QString& glopt, const QString &appname) +{ + QString gl = appname.toLower().replace(QLatin1String("\\"), QLatin1String("/")); + int idx = gl.lastIndexOf(QLatin1String("/")); + if (idx >= 0) + gl = gl.mid(idx + 1); + idx = gl.lastIndexOf(QLatin1String(".")); + if (idx > 0) + gl = gl.left(idx); + if (gl.indexOf(QLatin1String("-desktop")) > 0) + gl = QLatin1String("desktop"); + else if (gl.indexOf(QLatin1String("-es")) > 0 || gl.indexOf(QLatin1String("-angle")) > 0) + gl = gl.mid(gl.indexOf(QLatin1String("-es")) + 1); + else if (gl.indexOf(QLatin1String("-sw")) > 0 || gl.indexOf(QLatin1String("-software")) > 0) + gl = QLatin1String("software"); + else + gl = glopt.toLower(); + if (gl.isEmpty()) { + switch (Config::instance().openGLType()) { + case Config::Desktop: + gl = QLatin1String("desktop"); + break; + case Config::OpenGLES: + gl = QLatin1String("es"); + break; + case Config::Software: + gl = QLatin1String("software"); + break; + default: + break; + } + } + if (gl == QLatin1String("es") || gl == QLatin1String("angle") || gl == QLatin1String("opengles")) { + gl = QLatin1String("es_"); + gl.append(Config::instance().getANGLEPlatform().toLower()); + } +#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) + if (gl.startsWith(QLatin1String("es"))) { + qApp->setAttribute(Qt::AA_UseOpenGLES); +#ifdef QT_OPENGL_DYNAMIC + qputenv("QT_OPENGL", "angle"); +#endif +#ifdef Q_OS_WIN + if (gl.endsWith(QLatin1String("d3d11"))) + qputenv("QT_ANGLE_PLATFORM", "d3d11"); + else if (gl.endsWith(QLatin1String("d3d9"))) + qputenv("QT_ANGLE_PLATFORM", "d3d9"); + else if (gl.endsWith(QLatin1String("warp"))) + qputenv("QT_ANGLE_PLATFORM", "warp"); +#endif + } else if (gl == QLatin1String("desktop")) { + qApp->setAttribute(Qt::AA_UseDesktopOpenGL); + } else if (gl == QLatin1String("software")) { + qApp->setAttribute(Qt::AA_UseSoftwareOpenGL); + } +#endif +} + + +QString appDataDir() +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + return QDesktopServices::storageLocation(QDesktopServices::DataLocation); +#else +#if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) + return QStandardPaths::writableLocation(QStandardPaths::DataLocation); +#else + return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); +#endif //5.4.0 +#endif // 5.0.0 +} + +AppEventFilter::AppEventFilter(QObject *player, QObject *parent) + : QObject(parent) + , m_player(player) +{} + +bool AppEventFilter::eventFilter(QObject *obj, QEvent *ev) +{ + //qDebug() << __FUNCTION__ << " watcher: " << obj << ev; + if (obj != qApp) + return false; + if (ev->type() == QEvent::WinEventAct) { + // winrt file open/pick. since qt5.6.1 + qDebug("QEvent::WinEventAct"); +#ifdef Q_OS_WINRT + class QActivationEvent : public QEvent { + public: + void* args() const {return d;} //IInspectable* + }; + QActivationEvent *ae = static_cast(ev); + const QString url(UrlFromFileArgs((IInspectable*)ae->args())); + if (!url.isEmpty()) { + qDebug() << "winrt url: " << url; + if (m_player) + QMetaObject::invokeMethod(m_player, "play", Q_ARG(QUrl, QUrl(url))); + } + return true; +#endif + } + if (ev->type() != QEvent::FileOpen) + return false; + QFileOpenEvent *foe = static_cast(ev); + if (m_player) + QMetaObject::invokeMethod(m_player, "play", Q_ARG(QUrl, QUrl(foe->url()))); + return true; +} + +static void initResources() { + //Q_INIT_RESOURCE(theme); +} + +namespace { + struct ResourceLoader { + public: + ResourceLoader() { initResources(); } + } qrc; +} + + diff --git a/Common/Common.h b/Common/Common.h new file mode 100644 index 00000000..84991d8a --- /dev/null +++ b/Common/Common.h @@ -0,0 +1,46 @@ +/****************************************************************************** + QtAV Player Demo: this file is part of QtAV examples + Copyright (C) 2012-2017 Wang Bin +* This file is part of QtAV (from 2014) + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#ifndef COMMON_H +#define COMMON_H +#include +#include +#include +#include "QOptions.h" +#include "Config.h" + +QOptions COMMON_EXPORT get_common_options(); +void COMMON_EXPORT do_common_options_before_qapp(const QOptions& options); +/// help, log file, ffmpeg log level +void COMMON_EXPORT do_common_options(const QOptions& options, const QString& appName = QString()); +void COMMON_EXPORT load_qm(const QStringList& names, const QString &lang = QLatin1String("system")); +// if appname ends with 'desktop', 'es', 'angle', software', 'sw', set by appname. otherwise set by command line option glopt, or Config file +// TODO: move to do_common_options_before_qapp +void COMMON_EXPORT set_opengl_backend(const QString& glopt = QString::fromLatin1("auto"), const QString& appname = QString()); + +QString COMMON_EXPORT appDataDir(); + +class COMMON_EXPORT AppEventFilter : public QObject +{ +public: + AppEventFilter(QObject *player = 0, QObject* parent = 0); + QUrl url() const; + virtual bool eventFilter(QObject *obj, QEvent *ev); +private: + QObject *m_player; +}; + +#endif // COMMON_H diff --git a/Common/CommonExport.h b/Common/CommonExport.h new file mode 100644 index 00000000..0307a079 --- /dev/null +++ b/Common/CommonExport.h @@ -0,0 +1,33 @@ +/****************************************************************************** + QtAV Player Demo: this file is part of QtAV examples + Copyright (C) 2012-2016 Wang Bin +* This file is part of QtAV (from 2014) + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#ifndef COMMON_EXPORT_H +#define COMMON_EXPORT_H + +#include + +#ifdef BUILD_COMMON_STATIC +#define COMMON_EXPORT +#else +#if defined(BUILD_COMMON_LIB) +# undef COMMON_EXPORT +# define COMMON_EXPORT Q_DECL_EXPORT +#else +# undef COMMON_EXPORT +# define COMMON_EXPORT //Q_DECL_IMPORT //only for vc? link to static lib error +#endif +#endif //BUILD_COMMON_STATIC +#endif // COMMON_EXPORT_H diff --git a/Common/Config.cpp b/Common/Config.cpp new file mode 100644 index 00000000..04d999c5 --- /dev/null +++ b/Common/Config.cpp @@ -0,0 +1,1983 @@ +/****************************************************************************** + QtAV Player Demo: this file is part of QtAV examples + Copyright (C) 2012-2016 Wang Bin +* This file is part of QtAV (from 2014) + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include "Config.h" +#include +#include +#include +#include +#include +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +#include +#else +#include +#endif +#include +#include +#include +#include "Common.h" + +class Config::Data +{ +public: + Data() { + is_loading = false; + if (!Data::name.isEmpty()) { + //this->configPath = appDataDir(); + file = appDataDir() + QString::fromLatin1("/") + Data::name + QString::fromLatin1(".ini"); + } else { + //this->configPath = appDataDir() + QString::fromLatin1("/") + qApp->applicationName(); + file = appDataDir() + QString::fromLatin1("/") + qApp->applicationName() + QString::fromLatin1(".ini"); + } + if (!QDir(appDataDir()).exists()) { + if (!QDir().mkpath(appDataDir())) { + qWarning() << "Failed to create appDataDir: " << appDataDir(); + } + } + + moveOldCfg(); + } + + void moveOldCfg() { + // for old config data migration + QString dir_old = qApp->applicationDirPath() + QString::fromLatin1("/data"); + if (!QDir(dir_old).exists()) { + dir_old = QDir::homePath() + QString::fromLatin1("/.QtAV"); + } + if (QDir(dir_old).exists()) { + if (!QFile(file).exists()) { + QString old = dir_old + QString::fromLatin1("/") + qApp->applicationName() + QString::fromLatin1(".ini"); + if (QFile(old).exists()) { + QFile::copy(old, file); + QFile::remove(old); + } + old = dir_old + QString::fromLatin1("/playlist.qds"); + if (QFile(old).exists()) { + if (!QFile::copy(old, appDataDir() + QString::fromLatin1("/playlist.qds"))) + qWarning("error to move old playlist data"); + QFile::remove(old); + } + old = dir_old + QString::fromLatin1("/history.qds"); + if (QFile(old).exists()) { + if (!QFile::copy(old, appDataDir() + QString::fromLatin1("/history.qds"))) + qWarning("error to move old history data"); + QFile::remove(old); + } + } + } + } + + void save() { + if (is_loading) + return; + qDebug() << "sync config to " << file; + QSettings settings(file, QSettings::IniFormat); + // TODO: why crash on mac qt5.4 if call on aboutToQuit() + settings.setValue(QString::fromLatin1("log"), log); + settings.setValue(QString::fromLatin1("language"), lang); + settings.setValue(QString::fromLatin1("last_file"), last_file); + settings.setValue(QString::fromLatin1("timeout"), timeout); + settings.setValue(QString::fromLatin1("abort_timeout"), abort_timeout); + settings.setValue(QString::fromLatin1("force_fps"), force_fps); + settings.beginGroup(QString::fromLatin1("decoder")); + settings.beginGroup(QString::fromLatin1("video")); + settings.setValue(QString::fromLatin1("priority"), video_decoders.join(QString::fromLatin1(" "))); + settings.endGroup(); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("capture")); + settings.setValue(QString::fromLatin1("zeroCopy"), zero_copy); + settings.setValue(QString::fromLatin1("dir"), capture_dir); + settings.setValue(QString::fromLatin1("format"), capture_fmt); + settings.setValue(QString::fromLatin1("quality"), capture_quality); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("subtitle")); + settings.setValue(QString::fromLatin1("enabled"), subtitle_enabled); + settings.setValue(QString::fromLatin1("autoLoad"), subtitle_autoload); + settings.setValue(QString::fromLatin1("engines"), subtitle_engines); + settings.setValue(QString::fromLatin1("delay"), subtitle_delay); + settings.setValue(QString::fromLatin1("font"), subtitle_font); + settings.setValue(QString::fromLatin1("color"), subtitle_color); + settings.setValue(QString::fromLatin1("outline_color"), subtitle_outline_color); + settings.setValue(QString::fromLatin1("outline"), subtitle_outline); + settings.setValue(QString::fromLatin1("bottom margin"), subtilte_bottom_margin); + settings.beginGroup(QString::fromLatin1("ass")); + settings.setValue(QString::fromLatin1("font_file"), ass_font_file); + settings.setValue(QString::fromLatin1("force_font_file"), ass_force_font_file); + settings.setValue(QString::fromLatin1("fonts_dir"), ass_fonts_dir); + settings.endGroup(); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("preview")); + settings.setValue(QString::fromLatin1("enabled"), preview_enabled); + settings.setValue(QString::fromLatin1("width"), preview_w); + settings.setValue(QString::fromLatin1("height"), preview_h); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("avformat")); + settings.setValue(QString::fromLatin1("enable"), avformat_on); + settings.setValue(QString::fromLatin1("avioflags"), direct ? QString::fromLatin1("direct") : QString::fromLatin1("0")); + settings.setValue(QString::fromLatin1("probesize"), probe_size); + settings.setValue(QString::fromLatin1("analyzeduration"), analyze_duration); + settings.setValue(QString::fromLatin1("extra"), avformat_extra); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("avfilterVideo")); + settings.setValue(QString::fromLatin1("enable"), avfilterVideo_on); + settings.setValue(QString::fromLatin1("options"), avfilterVideo); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("avfilterAudio")); + settings.setValue(QString::fromLatin1("enable"), avfilterAudio_on); + settings.setValue(QString::fromLatin1("options"), avfilterAudio); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("opengl")); + settings.setValue(QString::fromLatin1("egl"), egl); + const char* glname = Config::staticMetaObject.enumerator(Config::staticMetaObject.indexOfEnumerator("OpenGLType")).valueToKey(opengl); + settings.setValue(QString::fromLatin1("type"), QString::fromLatin1(glname)); + settings.setValue(QString::fromLatin1("angle_platform"), angle_dx); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("shader")); + settings.setValue(QString::fromLatin1("enable"), user_shader); + settings.setValue(QString::fromLatin1("fbo"), fbo); + settings.setValue(QString::fromLatin1("fragHeader"), frag_header); + settings.setValue(QString::fromLatin1("fragSample"), frag_sample); + settings.setValue(QString::fromLatin1("fragPostProcess"), frag_pp); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("buffer")); + settings.setValue(QString::fromLatin1("value"), buffer_value); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("backend")); + settings.setValue(QString::fromLatin1("icn_prefix"), icn_prefix); + settings.setValue(QString::fromLatin1("http_prefix"), http_prefix); + settings.setValue(QString::fromLatin1("icn_suffix"), icn_suffix); + settings.setValue(QString::fromLatin1("http_suffix"), http_suffix); + settings.setValue(QString::fromLatin1("segment_buffer_size"), segment_buffer_size); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("playback")); + settings.setValue(QString::fromLatin1("last_played"), last_played); + settings.setValue(QString::fromLatin1("adaptation_logic"), adaptation_logic); + settings.setValue(QString::fromLatin1("icn"), icn); + settings.endGroup(); + + + settings.beginGroup(QString::fromLatin1("rate_conf")); + settings.setValue(QString::fromLatin1("rate_alpha"), rate_alpha); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("buffer_rate_based_conf")); + settings.setValue(QString::fromLatin1("adaptech_first_threshold"), adaptech_first_threshold); + settings.setValue(QString::fromLatin1("adaptech_second_threshold"), adaptech_second_threshold); + settings.setValue(QString::fromLatin1("adaptech_switch_up_margin"), adaptech_switch_up_margin); + settings.setValue(QString::fromLatin1("adaptech_slack_parameter"), adaptech_slack_parameter); + settings.setValue(QString::fromLatin1("adaptech_alpha"), adaptech_alpha); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("buffer_three_threshold_conf")); + settings.setValue(QString::fromLatin1("buffer_3Threshold_first"), buffer_3Threshold_first); + settings.setValue(QString::fromLatin1("buffer_3Threshold_second"), buffer_3Threshold_second); + settings.setValue(QString::fromLatin1("buffer_3Threshold_third"), buffer_3Threshold_third); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("panda_conf")); + settings.setValue(QString::fromLatin1("panda_param_alpha"), panda_param_alpha); + settings.setValue(QString::fromLatin1("panda_param_beta"), panda_param_beta); + settings.setValue(QString::fromLatin1("panda_param_Bmin"), panda_param_Bmin); + settings.setValue(QString::fromLatin1("panda_param_K"), panda_param_K); + settings.setValue(QString::fromLatin1("panda_param_W"), panda_param_W); + settings.setValue(QString::fromLatin1("panda_param_epsilon"), panda_param_epsilon); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("bola_conf")); + settings.setValue(QString::fromLatin1("bola_buffer_target"), bola_buffer_target); + settings.setValue(QString::fromLatin1("bola_alpha"), bola_alpha); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("status_conf")); + settings.setValue(QString::fromLatin1("repeat"), repeat); + settings.setValue(QString::fromLatin1("graph"), graph); + settings.setValue(QString::fromLatin1("full_screen"), full_screen); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("consumer_conf")); + settings.setValue(QString::fromLatin1("autotune"), autotune); + settings.setValue(QString::fromLatin1("lifetime"), lifetime); + settings.setValue(QString::fromLatin1("retrasmisisons"), retransmissions); + settings.setValue(QString::fromLatin1("alpha"), alpha); + settings.setValue(QString::fromLatin1("beta"), beta); + settings.setValue(QString::fromLatin1("drop"), drop); + settings.setValue(QString::fromLatin1("beta_wifi"), beta_wifi); + settings.setValue(QString::fromLatin1("drop_wifi"), drop_wifi); + settings.setValue(QString::fromLatin1("delay_wifi"), delay_wifi); + settings.setValue(QString::fromLatin1("beta_lte"), beta_lte); + settings.setValue(QString::fromLatin1("drop_lte"), drop_lte); + settings.setValue(QString::fromLatin1("delay_lte"), delay_lte); + settings.setValue(QString::fromLatin1("batching_parameter"), batching_parameter); + settings.setValue(QString::fromLatin1("rate_estimator"), rate_estimator); + settings.endGroup(); + + + qDebug() << "sync end"; + } + + QString file; + bool is_loading; + + qreal force_fps; + QStringList video_decoders; + bool zero_copy; + + QString last_file; + + QString capture_dir; + QString capture_fmt; + int capture_quality; + + bool avformat_on; + bool direct; + unsigned int probe_size; + int analyze_duration; + QString avformat_extra; + bool avfilterVideo_on; + QString avfilterVideo; + bool avfilterAudio_on; + QString avfilterAudio; + + QStringList subtitle_engines; + bool subtitle_autoload; + bool subtitle_enabled; + QFont subtitle_font; + QColor subtitle_color, subtitle_outline_color; + bool subtitle_outline; + int subtilte_bottom_margin; + qreal subtitle_delay; + + bool ass_force_font_file; + QString ass_font_file; + QString ass_fonts_dir; + + bool preview_enabled; + int preview_w, preview_h; + + bool egl; + Config::OpenGLType opengl; + QString angle_dx; + bool abort_timeout; + qreal timeout; + int buffer_value; + QString log; + QString lang; + + QVariantList history; + + bool user_shader; + bool fbo; + QString frag_header; + QString frag_sample; + QString frag_pp; + QString icn_prefix; + QString http_prefix; + QString icn_suffix; + QString http_suffix; + qreal segment_buffer_size; + QString last_played; + QString adaptation_logic; + bool icn; + qreal rate_alpha; + qreal buffer_reservoir_threshold; + qreal buffer_max_threshold; + qreal adaptech_first_threshold; + qreal adaptech_second_threshold; + qreal adaptech_switch_up_margin; + qreal adaptech_slack_parameter; + qreal adaptech_alpha; + qreal buffer_3Threshold_first; + qreal buffer_3Threshold_second; + qreal buffer_3Threshold_third; + qreal panda_param_alpha; + qreal panda_param_beta; + qreal panda_param_Bmin; + qreal panda_param_K; + qreal panda_param_W; + qreal panda_param_epsilon; + qreal bola_buffer_target; + qreal bola_alpha; + bool repeat; + bool graph; + bool full_screen; + bool autotune; + int lifetime; + int retransmissions; + qreal alpha; + qreal beta; + qreal drop; + qreal beta_wifi; + qreal drop_wifi; + int delay_wifi; + qreal beta_lte; + qreal drop_lte; + int delay_lte; + int batching_parameter; + int rate_estimator; + static QString name; +}; + +QString Config::Data::name; + +Config& Config::instance() +{ + static Config cfg; + return cfg; +} + +void Config::setName(const QString &name) +{ + Config::Data::name = name; +} + +QString Config::getName() +{ + return Config::Data::name; +} + +QString Config::defaultConfigFile() +{ + return appDataDir() + QString::fromLatin1("/") + Data::name + QString::fromLatin1(".ini");; +} + +Config::Config(QObject *parent) + : QObject(parent) + , mpData(new Data()) +{ + // DO NOT call save() in dtor because it's a singleton and may be deleted later than qApp, QFont is not valid + connect(qApp, SIGNAL(aboutToQuit()), SLOT(save())); //FIXME: what if qapp not ready + reload(); +} + +Config::~Config() +{ + delete mpData; +} + +QString Config::defaultDir() +{ + return appDataDir(); +} + +bool Config::reset() +{ + QFile cf(mpData->file); + if (!cf.remove()) { + qWarning() << "Failed to remove config file: " << cf.errorString(); + return false; + } + reload(); + save(); + return true; +} + +void Config::reload() +{ + QSqlDatabase db(QSqlDatabase::database()); + if (!db.isOpen()) { + db = QSqlDatabase::addDatabase(QString::fromUtf8("QSQLITE")); + db.setDatabaseName(appDataDir().append(QString("/%1.db").arg(mpData->name))); + if (!db.open()) + qWarning("error open db"); + db.exec("CREATE TABLE IF NOT EXISTS history (url TEXT primary key, start BIGINT, duration BIGINT)"); + } + QSqlQuery query(db.exec(QString::fromUtf8("SELECT * FROM history"))); + while (query.next()) { + QVariantMap var; + var[QString::fromUtf8("url")] = query.value(0).toString(); + var[QString::fromUtf8("start")] = query.value(1).toLongLong(); + var[QString::fromUtf8("duration")] = query.value(2).toLongLong(); + mpData->history.append(var); + } + mpData->is_loading = true; + QSettings settings(mpData->file, QSettings::IniFormat); + setLogLevel(settings.value(QString::fromLatin1("log"), QString()).toString()); + setLanguage(settings.value(QString::fromLatin1("language"), +#if QT_VERSION == QT_VERSION_CHECK(5, 6, 0) && defined(Q_OS_WINPHONE) //qt bug + QString::fromLatin1("en_US") +#else + QString::fromLatin1("system") +#endif + ).toString()); + setLastFile(settings.value(QString::fromLatin1("last_file"), QString()).toString()); + setTimeout(settings.value(QString::fromLatin1("timeout"), 30.0).toReal()); + setAbortOnTimeout(settings.value(QString::fromLatin1("abort_timeout"), true).toBool()); + setForceFrameRate(settings.value(QString::fromLatin1("force_fps"), 0.0).toReal()); + settings.beginGroup(QString::fromLatin1("decoder")); + settings.beginGroup(QString::fromLatin1("video")); + QString decs_default(QString::fromLatin1("FFmpeg")); + setDecoderPriorityNames(settings.value(QString::fromLatin1("priority"), decs_default).toString().split(QString::fromLatin1(" "), QString::SkipEmptyParts)); + setZeroCopy(settings.value(QString::fromLatin1("zeroCopy"), true).toBool()); + settings.endGroup(); //video + settings.endGroup(); //decoder + + settings.beginGroup(QString::fromLatin1("capture")); + setCaptureDir(settings.value(QString::fromLatin1("dir"), QString()).toString()); + if (captureDir().isEmpty()) { +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + setCaptureDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); +#else + setCaptureDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); +#endif + } + setCaptureFormat(settings.value(QString::fromLatin1("format"), QString::fromLatin1("png")).toString()); + setCaptureQuality(settings.value(QString::fromLatin1("quality"), 100).toInt()); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("subtitle")); + setSubtitleAutoLoad(settings.value(QString::fromLatin1("autoLoad"), true).toBool()); + setSubtitleEnabled(settings.value(QString::fromLatin1("enabled"), true).toBool()); + setSubtitleEngines(settings.value(QString::fromLatin1("engines"), QStringList() << QString::fromLatin1("FFmpeg") << QString::fromLatin1("LibASS")).toStringList()); + setSubtitleDelay(settings.value(QString::fromLatin1("delay"), 0.0).toInt()); + QFont f; + f.setPointSize(20); + f.setBold(true); + setSubtitleFont(settings.value(QString::fromLatin1("font"), f).value()); + setSubtitleColor(settings.value(QString::fromLatin1("color"), QColor("white")).value()); + setSubtitleOutlineColor(settings.value(QString::fromLatin1("outline_color"), QColor("blue")).value()); + setSubtitleOutline(settings.value(QString::fromLatin1("outline"), true).toBool()); + setSubtitleBottomMargin(settings.value(QString::fromLatin1("bottom margin"), 8).toInt()); + settings.beginGroup(QString::fromLatin1("ass")); + setAssFontFile(settings.value(QString::fromLatin1("font_file"), QString()).toString()); + setAssFontFileForced(settings.value(QString::fromLatin1("force_font_file"), false).toBool()); + setAssFontsDir(settings.value(QString::fromLatin1("fonts_dir"), QString()).toString()); + settings.endGroup(); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("preview")); + setPreviewEnabled(settings.value(QString::fromLatin1("enabled"), true).toBool()); + setPreviewWidth(settings.value(QString::fromLatin1("width"), 160).toInt()); + setPreviewHeight(settings.value(QString::fromLatin1("height"), 90).toInt()); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("avformat")); + setAvformatOptionsEnabled(settings.value(QString::fromLatin1("enable"), false).toBool()); + reduceBuffering(settings.value(QString::fromLatin1("avioflags"), 0).toString() == QLatin1String("direct")); + probeSize(settings.value(QString::fromLatin1("probesize"), 5000000).toUInt()); + analyzeDuration(settings.value(QString::fromLatin1("analyzeduration"), 5000000).toInt()); + avformatExtra(settings.value(QString::fromLatin1("extra"), QString()).toString()); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("avfilterVideo")); + avfilterVideoEnable(settings.value(QString::fromLatin1("enable"), true).toBool()); + avfilterVideoOptions(settings.value(QString::fromLatin1("options"), QString()).toString()); + settings.endGroup(); + settings.beginGroup(QString::fromLatin1("avfilterAudio")); + avfilterAudioEnable(settings.value(QString::fromLatin1("enable"), true).toBool()); + avfilterAudioOptions(settings.value(QString::fromLatin1("options"), QString()).toString()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("opengl")); + setEGL(settings.value(QString::fromLatin1("egl"), false).toBool()); + const QString glname = settings.value(QString::fromLatin1("type"), QString::fromLatin1("OpenGLES")).toString(); + setOpenGLType((Config::OpenGLType)Config::staticMetaObject.enumerator(Config::staticMetaObject.indexOfEnumerator("OpenGLType")).keysToValue(glname.toLatin1().constData())); + // d3d11 bad performance (gltexsubimage2d) + setANGLEPlatform(settings.value(QString::fromLatin1("angle_platform"), QString::fromLatin1("d3d9")).toString()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("shader")); + setUserShaderEnabled(settings.value(QString::fromLatin1("enable"), false).toBool()); + setIntermediateFBO(settings.value(QString::fromLatin1("fbo"), false).toBool()); + setFragHeader(settings.value(QString::fromLatin1("fragHeader"), QString()).toString()); + setFragSample(settings.value(QString::fromLatin1("fragSample"), QString::fromLatin1("// horizontal mirror effect\n" + "vec4 sample2d(sampler2D tex, vec2 pos, int p) {\n" + " return texture(tex, vec2(1.0-pos.x, pos.y));\n" + "}")).toString()); + setFragPostProcess(settings.value(QString::fromLatin1("fragPostProcess"), QString::fromLatin1("//negate color effect\n" + "gl_FragColor.rgb = vec3(1.0-gl_FragColor.r, 1.0-gl_FragColor.g, 1.0-gl_FragColor.b);")).toString()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("buffer")); + setBufferValue(settings.value(QString::fromLatin1("value"), -1).toInt()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("rate_conf")); + setRateAlpha(settings.value(QString::fromLatin1("rate_alpha"), 0.8).toReal()); + settings.endGroup(); + + + settings.beginGroup(QString::fromLatin1("buffer_based_conf")); + setBufferReservoirThreshold(settings.value(QString::fromLatin1("buffer_reservoir_threshold"), 20).toReal()); + setBufferMaxThreshold(settings.value(QString::fromLatin1("buffer_max_threshold"), 80).toReal()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("buffer_rate_based_conf")); + setAdaptechFirstThreshold(settings.value(QString::fromLatin1("adaptech_first_threshold"), 30).toReal()); + setAdaptechSecondThreshold(settings.value(QString::fromLatin1("adaptech_second_threshold"), 70).toReal()); + setAdaptechSwitchUpMargin(settings.value(QString::fromLatin1("adaptech_switch_up_margin"), 5).toReal()); + setAdaptechSlackParameter(settings.value(QString::fromLatin1("adaptech_slack_parameter"), 0.8).toReal()); + setAdaptechAlpha(settings.value(QString::fromLatin1("adaptech_alpha"), 0.8).toReal()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("buffer_three_threshold_conf")); + setBufferThreeThresholdFirst(settings.value(QString::fromLatin1("buffer_3Threshold_first"), 15).toReal()); + setBufferThreeThresholdSecond(settings.value(QString::fromLatin1("buffer_3Threshold_second"), 35).toReal()); + setBufferThreeThresholdThird(settings.value(QString::fromLatin1("buffer_3Threshold_third"), 75).toReal()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("backend")); + setIcnPrefix(settings.value(QString::fromLatin1("icn_prefix"), QString::fromLatin1("ccnx:/webserver/get/")).toString()); + setHttpPrefix(settings.value(QString::fromLatin1("http_prefix"), QString::fromLatin1("http://10.60.17.153:8080/")).toString()); + setIcnSuffix(settings.value(QString::fromLatin1("icn_suffix"), QString::fromLatin1("/mpd")).toString()); + setHttpSuffix(settings.value(QString::fromLatin1("http_suffix"), QString::fromLatin1("/mpd")).toString()); + + setSegmentBufferSize(settings.value(QString::fromLatin1("segment_buffer_size"), 20).toReal()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("playback")); + setLastPlayed(settings.value(QString::fromLatin1("last_played"), QString::fromLatin1("sintel")).toString()); + setAdaptationLogic(settings.value(QString::fromLatin1("adaptation_logic"), QString::fromLatin1("Buffer Based")).toString()); + setIcn(settings.value(QString::fromLatin1("icn"), true).toBool()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("panda_conf")); + setPandaParamAlpha(settings.value(QString::fromLatin1("panda_param_alpha"), 0.4).toReal()); + setPandaParamBeta(settings.value(QString::fromLatin1("panda_param_beta"), 0.6).toReal()); + setPandaParamBMin(settings.value(QString::fromLatin1("panda_param_Bmin"), 67).toReal()); + setPandaParamK(settings.value(QString::fromLatin1("panda_param_K"), 0.5).toReal()); + setPandaParamW(settings.value(QString::fromLatin1("panda_param_W"), 270000).toReal()); + setPandaParamEpsilon(settings.value(QString::fromLatin1("panda_param_epsilon"), 0.19).toReal()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("bola_conf")); + setBolaBufferTarget(settings.value(QString::fromLatin1("bola_buffer_target"), 23).toReal()); + setBolaAlpha(settings.value(QString::fromLatin1("bola_alpha"), 0.8).toReal()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("status_conf")); + setRepeat(settings.value(QString::fromLatin1("repeat"), false).toBool()); + setGraph(settings.value(QString::fromLatin1("graph"), false).toBool()); + setFullScreen(settings.value(QString::fromLatin1("full_screen"), false).toBool()); + settings.endGroup(); + + settings.beginGroup(QString::fromLatin1("consumer_conf")); + setAutotune(settings.value(QString::fromLatin1("autotune"), false).toBool()); + setLifetime(settings.value(QString::fromLatin1("lifetime"), 500).toInt()); + setRetransmissions(settings.value(QString::fromLatin1("retrasmisisons"), 128).toInt()); + setAlpha(settings.value(QString::fromLatin1("alpha"), 0.95).toReal()); + setBeta(settings.value(QString::fromLatin1("beta"), 0.99).toReal()); + setDrop(settings.value(QString::fromLatin1("drop"), 0.003).toReal()); + setBetaWifi(settings.value(QString::fromLatin1("beta_wifi"), 0.99).toReal()); + setDropWifi(settings.value(QString::fromLatin1("drop_wifi"), 0.6).toReal()); + setDelayWifi(settings.value(QString::fromLatin1("delay_wifi"), 200).toInt()); + setBetaLte(settings.value(QString::fromLatin1("beta_lte"), 0.99).toReal()); + setDropLte(settings.value(QString::fromLatin1("drop_lte"), 0.003).toReal()); + setDelayLte(settings.value(QString::fromLatin1("delay_lte"), 9000).toInt()); + setBatchingParameter(settings.value(QString::fromLatin1("batching_parameter"), 200).toInt()); + setRateEstimator(settings.value(QString::fromLatin1("rate_estimator"), 0).toInt()); + settings.endGroup(); + mpData->is_loading = false; +} + +qreal Config::forceFrameRate() const +{ + return mpData->force_fps; +} + +Config& Config::setForceFrameRate(qreal value) +{ + if (mpData->force_fps == value) + return *this; + mpData->force_fps = value; + Q_EMIT forceFrameRateChanged(); + Q_EMIT changed(); + return *this; +} + +QStringList Config::decoderPriorityNames() const +{ + return mpData->video_decoders; +} + +Config& Config::setDecoderPriorityNames(const QStringList &value) +{ + if (mpData->video_decoders == value) { + qDebug("decoderPriority not changed"); + return *this; + } + mpData->video_decoders = value; + Q_EMIT decoderPriorityNamesChanged(); + Q_EMIT changed(); + mpData->save(); + return *this; +} + +bool Config::zeroCopy() const +{ + return mpData->zero_copy; +} + +Config& Config::setZeroCopy(bool value) +{ + if (mpData->zero_copy == value) + return *this; + mpData->zero_copy = value; + Q_EMIT zeroCopyChanged(); + Q_EMIT changed(); + mpData->save(); + return *this; +} + +QString Config::captureDir() const +{ + return mpData->capture_dir; +} + +Config& Config::setCaptureDir(const QString& dir) +{ + if (mpData->capture_dir == dir) + return *this; + mpData->capture_dir = dir; + Q_EMIT captureDirChanged(dir); + Q_EMIT changed(); + return *this; +} + +QString Config::captureFormat() const +{ + return mpData->capture_fmt; +} + +Config& Config::setCaptureFormat(const QString& format) +{ + if (mpData->capture_fmt == format) + return *this; + mpData->capture_fmt = format; + Q_EMIT captureFormatChanged(format); + Q_EMIT changed(); + return *this; +} + +// only works for non-yuv capture +int Config::captureQuality() const +{ + return mpData->capture_quality; +} + +Config& Config::setCaptureQuality(int quality) +{ + if (mpData->capture_quality == quality) + return *this; + mpData->capture_quality = quality; + Q_EMIT captureQualityChanged(quality); + Q_EMIT changed(); + return *this; +} + +QStringList Config::subtitleEngines() const +{ + return mpData->subtitle_engines; +} + +Config& Config::setSubtitleEngines(const QStringList &value) +{ + if (mpData->subtitle_engines == value) + return *this; + mpData->subtitle_engines = value; + Q_EMIT subtitleEnginesChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::subtitleAutoLoad() const +{ + return mpData->subtitle_autoload; +} + +Config& Config::setSubtitleAutoLoad(bool value) +{ + if (mpData->subtitle_autoload == value) + return *this; + mpData->subtitle_autoload = value; + Q_EMIT subtitleAutoLoadChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::subtitleEnabled() const +{ + return mpData->subtitle_enabled; +} + +Config& Config::setSubtitleEnabled(bool value) +{ + if (mpData->subtitle_enabled == value) + return *this; + mpData->subtitle_enabled = value; + Q_EMIT subtitleEnabledChanged(); + Q_EMIT changed(); + return *this; +} + +QFont Config::subtitleFont() const +{ + return mpData->subtitle_font; +} + +Config& Config::setSubtitleFont(const QFont& value) +{ + if (mpData->subtitle_font == value) + return *this; + mpData->subtitle_font = value; + Q_EMIT subtitleFontChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::subtitleOutline() const +{ + return mpData->subtitle_outline; +} +Config& Config::setSubtitleOutline(bool value) +{ + if (mpData->subtitle_outline == value) + return *this; + mpData->subtitle_outline = value; + Q_EMIT subtitleOutlineChanged(); + Q_EMIT changed(); + return *this; +} + +QColor Config::subtitleColor() const +{ + return mpData->subtitle_color; +} + +Config& Config::setSubtitleColor(const QColor& value) +{ + if (mpData->subtitle_color == value) + return *this; + mpData->subtitle_color = value; + Q_EMIT subtitleColorChanged(); + Q_EMIT changed(); + return *this; +} + +QColor Config::subtitleOutlineColor() const +{ + return mpData->subtitle_outline_color; +} +Config& Config::setSubtitleOutlineColor(const QColor& value) +{ + if (mpData->subtitle_outline_color == value) + return *this; + mpData->subtitle_outline_color = value; + Q_EMIT subtitleOutlineColorChanged(); + Q_EMIT changed(); + return *this; +} + +int Config::subtitleBottomMargin() const +{ + return mpData->subtilte_bottom_margin; +} + +Config& Config::setSubtitleBottomMargin(int value) +{ + if (mpData->subtilte_bottom_margin == value) + return *this; + mpData->subtilte_bottom_margin = value; + Q_EMIT subtitleBottomMarginChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::subtitleDelay() const +{ + return mpData->subtitle_delay; +} + +Config& Config::setSubtitleDelay(qreal value) +{ + if (mpData->subtitle_delay == value) + return *this; + mpData->subtitle_delay = value; + Q_EMIT subtitleDelayChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::assFontFile() const +{ + return mpData->ass_font_file; +} + +Config& Config::setAssFontFile(const QString &value) +{ + if (mpData->ass_font_file == value) + return *this; + mpData->ass_font_file = value; + Q_EMIT assFontFileChanged(); + Q_EMIT changed(); + return *this; +} + + +QString Config::assFontsDir() const +{ + return mpData->ass_fonts_dir; +} + +Config& Config::setAssFontsDir(const QString &value) +{ + if (mpData->ass_fonts_dir == value) + return *this; + mpData->ass_fonts_dir = value; + Q_EMIT assFontsDirChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::isAssFontFileForced() const +{ + return mpData->ass_force_font_file; +} + +Config& Config::setAssFontFileForced(bool value) +{ + if (mpData->ass_force_font_file == value) + return *this; + mpData->ass_force_font_file = value; + Q_EMIT assFontFileForcedChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::previewEnabled() const +{ + return mpData->preview_enabled; +} + +Config& Config::setPreviewEnabled(bool value) +{ + if (mpData->preview_enabled == value) + return *this; + mpData->preview_enabled = value; + Q_EMIT previewEnabledChanged(); + Q_EMIT changed(); + return *this; +} + +int Config::previewWidth() const +{ + return mpData->preview_w; +} + +Config& Config::setPreviewWidth(int value) +{ + if (mpData->preview_w == value) + return *this; + mpData->preview_w = value; + Q_EMIT previewWidthChanged(); + Q_EMIT changed(); + return *this; +} + +int Config::previewHeight() const +{ + return mpData->preview_h; +} + +Config& Config::setPreviewHeight(int value) +{ + if (mpData->preview_h == value) + return *this; + mpData->preview_h = value; + Q_EMIT previewHeightChanged(); + Q_EMIT changed(); + return *this; +} +QVariantHash Config::avformatOptions() const +{ + QVariantHash vh; + if (!mpData->avformat_extra.isEmpty()) { + QStringList s(mpData->avformat_extra.split(QString::fromLatin1(" "))); + for (int i = 0; i < s.size(); ++i) { + int eq = s[i].indexOf(QLatin1String("=")); + if (eq < 0) { + continue; + } else { + vh[s[i].mid(0, eq)] = s[i].mid(eq+1); + } + } + } + if (mpData->probe_size > 0) { + vh[QString::fromLatin1("probesize")] = mpData->probe_size; + } + if (mpData->analyze_duration) { + vh[QString::fromLatin1("analyzeduration")] = mpData->analyze_duration; + } + if (mpData->direct) { + vh[QString::fromLatin1("avioflags")] = QString::fromLatin1("direct"); + }; + return vh; +} + +bool Config::avformatOptionsEnabled() const +{ + return mpData->avformat_on; +} + +Config& Config::setAvformatOptionsEnabled(bool value) +{ + if (mpData->avformat_on == value) + return *this; + mpData->avformat_on = value; + Q_EMIT avformatOptionsEnabledChanged(); + Q_EMIT changed(); + return *this; +} + +unsigned int Config::probeSize() const +{ + return mpData->probe_size; +} + +Config& Config::probeSize(unsigned int ps) +{ + mpData->probe_size = ps; + return *this; +} + +int Config::analyzeDuration() const +{ + return mpData->analyze_duration; +} + +Config& Config::analyzeDuration(int ad) +{ + mpData->analyze_duration = ad; + return *this; +} + +bool Config::reduceBuffering() const +{ + return mpData->direct; +} + +Config& Config::reduceBuffering(bool y) +{ + mpData->direct = y; + return *this; +} + +QString Config::avformatExtra() const +{ + return mpData->avformat_extra; +} + +Config& Config::avformatExtra(const QString &text) +{ + mpData->avformat_extra = text; + return *this; +} + +QString Config::avfilterVideoOptions() const +{ + return mpData->avfilterVideo; +} + +Config& Config::avfilterVideoOptions(const QString& options) +{ + if (mpData->avfilterVideo == options) + return *this; + mpData->avfilterVideo = options; + Q_EMIT avfilterVideoChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::avfilterVideoEnable() const +{ + return mpData->avfilterVideo_on; +} + +Config& Config::avfilterVideoEnable(bool e) +{ + if (mpData->avfilterVideo_on == e) + return *this; + mpData->avfilterVideo_on = e; + Q_EMIT avfilterVideoChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::avfilterAudioOptions() const +{ + return mpData->avfilterAudio; +} + +Config& Config::avfilterAudioOptions(const QString& options) +{ + if (mpData->avfilterAudio == options) + return *this; + mpData->avfilterAudio = options; + Q_EMIT avfilterAudioChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::avfilterAudioEnable() const +{ + return mpData->avfilterAudio_on; +} + +Config& Config::avfilterAudioEnable(bool e) +{ + if (mpData->avfilterAudio_on == e) + return *this; + mpData->avfilterAudio_on = e; + Q_EMIT avfilterAudioChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::isEGL() const +{ + return mpData->egl; +} + +Config& Config::setEGL(bool value) +{ + if (mpData->egl == value) + return *this; + mpData->egl = value; + Q_EMIT EGLChanged(); + Q_EMIT changed(); + return *this; +} + +Config::OpenGLType Config::openGLType() const +{ + return mpData->opengl; +} + +Config& Config::setOpenGLType(OpenGLType value) +{ + if (mpData->opengl == value) + return *this; + mpData->opengl = value; + Q_EMIT openGLTypeChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::getANGLEPlatform() const +{ + return mpData->angle_dx; +} + +Config& Config::setANGLEPlatform(const QString& value) +{ + if (mpData->angle_dx == value) + return *this; + mpData->angle_dx = value; + Q_EMIT ANGLEPlatformChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::userShaderEnabled() const +{ + return mpData->user_shader; +} + +Config& Config::setUserShaderEnabled(bool value) +{ + if (mpData->user_shader == value) + return *this; + mpData->user_shader = value; + Q_EMIT userShaderEnabledChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::intermediateFBO() const +{ + return mpData->fbo; +} + +Config& Config::setIntermediateFBO(bool value) +{ + if (mpData->fbo == value) + return *this; + mpData->fbo = value; + Q_EMIT intermediateFBOChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::fragHeader() const +{ + return mpData->frag_header; +} + +Config& Config::setFragHeader(const QString &text) +{ + if (mpData->frag_header == text) + return *this; + mpData->frag_header = text; + Q_EMIT fragHeaderChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::fragSample() const +{ + return mpData->frag_sample; +} + +Config& Config::setFragSample(const QString &text) +{ + if (mpData->frag_sample == text) + return *this; + mpData->frag_sample = text; + Q_EMIT fragSampleChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::fragPostProcess() const +{ + return mpData->frag_pp; +} + +Config& Config::setFragPostProcess(const QString &text) +{ + if (mpData->frag_pp == text) + return *this; + mpData->frag_pp = text; + Q_EMIT fragPostProcessChanged(); + Q_EMIT changed(); + return *this; +} + +int Config::bufferValue() const +{ + return mpData->buffer_value; +} + +Config& Config::setBufferValue(int value) +{ + if (mpData->buffer_value == value) + return *this; + mpData->buffer_value = value; + Q_EMIT bufferValueChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::timeout() const +{ + return mpData->timeout; +} + +Config& Config::setTimeout(qreal value) +{ + if (mpData->timeout == value) + return *this; + mpData->timeout = value; + Q_EMIT timeoutChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::logLevel() const +{ + return mpData->log; +} + +Config& Config::setLogLevel(const QString& value) +{ + if (mpData->log == value.toLower()) + return *this; + mpData->log = value.toLower(); + Q_EMIT logLevelChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::language() const +{ + return mpData->lang; +} + +Config& Config::setLanguage(const QString& value) +{ + if (mpData->lang == value) + return *this; + mpData->lang = value; + Q_EMIT languageChanged(); + Q_EMIT changed(); + return *this; +} + +QVariantList Config::history() const +{ + return mpData->history; +} + +void Config::addHistory(const QVariantMap &value) +{ + mpData->history.prepend(value); + Q_EMIT historyChanged(); + QSqlDatabase db = QSqlDatabase::database(); + QSqlQuery query(db); + if (!query.prepare(QString::fromUtf8("INSERT INTO history (url, start, duration) " + "VALUES (:url, :start, :duration)"))) { + qWarning("error prepare sql query"); + } + query.bindValue(QString::fromUtf8(":url"), value.value("url").toString()); + query.bindValue(QString::fromUtf8(":start"), value.value("start").toLongLong()); + query.bindValue(QString::fromUtf8(":duration"), value.value("duration").toLongLong()); + if (!query.exec()) + qWarning("failed to add history: %d", db.isOpen()); +} + +void Config::removeHistory(const QString &url) +{ + QVariantList::Iterator it = mpData->history.begin(); + bool change = false; + while (it != mpData->history.end()) { + if (it->toMap().value("url") != url) { + ++it; + continue; + } + it = mpData->history.erase(it); + change = true; + } + if (!change) + return; + Q_EMIT historyChanged(); + QSqlDatabase db = QSqlDatabase::database(); + QSqlQuery query(db); + query.prepare(QString::fromUtf8("DELETE FROM history WHERE url = :url")); + query.bindValue(QString::fromUtf8(":url"), url); + if (!query.exec()) + qWarning("failed to remove history"); +} + +void Config::clearHistory() +{ + if (mpData->history.isEmpty()) + return; + mpData->history.clear(); + Q_EMIT historyChanged(); + QSqlDatabase db = QSqlDatabase::database(); + QSqlQuery query(db); + query.prepare(QString::fromUtf8("DELETE FROM history")); + // 'TRUNCATE table history' is faster + if (!query.exec()) + qWarning("failed to clear history"); +} + +bool Config::abortOnTimeout() const +{ + return mpData->abort_timeout; +} + +Config& Config::setAbortOnTimeout(bool value) +{ + if (mpData->abort_timeout == value) + return *this; + mpData->abort_timeout = value; + Q_EMIT abortOnTimeoutChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::icnPrefix() const +{ + return mpData->icn_prefix; +} + +Config& Config::setIcnPrefix(const QString &text) +{ + if (mpData->icn_prefix == text) + return *this; + mpData->icn_prefix = text; + Q_EMIT icnPrefixChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::icnSuffix() const +{ + return mpData->icn_suffix; +} + +Config& Config::setIcnSuffix(const QString &text) +{ + if (mpData->icn_suffix == text) + return *this; + mpData->icn_suffix = text; + Q_EMIT icnSuffixChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::httpPrefix() const +{ + return mpData->http_prefix; +} + +Config& Config::setHttpPrefix(const QString &text) +{ + if (mpData->http_prefix == text) + return *this; + mpData->http_prefix = text; + Q_EMIT httpPrefixChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::httpSuffix() const +{ + return mpData->http_suffix; +} + +Config& Config::setHttpSuffix(const QString &text) +{ + if (mpData->http_suffix == text) + return *this; + mpData->http_suffix = text; + Q_EMIT httpSuffixChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::segmentBufferSize() const +{ + return mpData->segment_buffer_size; +} + +Config& Config::setSegmentBufferSize(qreal value) +{ + if (mpData->segment_buffer_size == value) + return *this; + mpData->segment_buffer_size = value; + Q_EMIT segmentBufferSizeChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::lastPlayed() const +{ + return mpData->last_played; +} + +Config& Config::setLastPlayed(const QString &text) +{ + if (mpData->last_played == text) + return *this; + mpData->last_played = text; + Q_EMIT lastPlayedChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::adaptationLogic() const +{ + return mpData->adaptation_logic; +} + +Config& Config::setAdaptationLogic(const QString &text) +{ + if (mpData->adaptation_logic == text) + return *this; + mpData->adaptation_logic = text; + Q_EMIT adaptationLogicChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::rateAlpha() const +{ + return mpData->rate_alpha; +} + +Config& Config::setRateAlpha(qreal value) +{ + if (mpData->rate_alpha == value) + return *this; + mpData->rate_alpha = value; + Q_EMIT rateAlphaChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::bufferReservoirThreshold() const +{ + return mpData->buffer_reservoir_threshold; +} + +bool Config::icn() const +{ + return mpData->icn; +} + +Config& Config::setIcn(bool value) +{ + if (mpData->icn == value) + return *this; + mpData->icn = value; + Q_EMIT icnChanged(); + Q_EMIT changed(); + return *this; +} + +Config& Config::setBufferReservoirThreshold(qreal value) +{ + if (mpData->buffer_reservoir_threshold == value) + return *this; + mpData->buffer_reservoir_threshold = value; + Q_EMIT bufferReservoirThresholdChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::bufferMaxThreshold() const +{ + return mpData->buffer_max_threshold; +} + +Config& Config::setBufferMaxThreshold(qreal value) +{ + if (mpData->buffer_max_threshold == value) + return *this; + mpData->buffer_max_threshold = value; + Q_EMIT bufferMaxThresholdChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::adaptechFirstThreshold() const +{ + return mpData->adaptech_first_threshold; +} + +Config& Config::setAdaptechFirstThreshold(qreal value) +{ + if (mpData->adaptech_first_threshold == value) + return *this; + mpData->adaptech_first_threshold = value; + Q_EMIT adaptechFirstThresholdChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::adaptechSecondThreshold() const +{ + return mpData->adaptech_second_threshold; +} + +Config& Config::setAdaptechSecondThreshold(qreal value) +{ + if (mpData->adaptech_second_threshold == value) + return *this; + mpData->adaptech_second_threshold = value; + Q_EMIT adaptechSecondThresholdChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::adaptechSwitchUpMargin() const +{ + return mpData->adaptech_switch_up_margin; +} + +Config& Config::setAdaptechSwitchUpMargin(qreal value) +{ + if (mpData->adaptech_switch_up_margin == value) + return *this; + mpData->adaptech_switch_up_margin = value; + Q_EMIT adaptechSwitchUpMarginChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::adaptechSlackParameter() const +{ + return mpData->adaptech_slack_parameter; +} + +Config& Config::setAdaptechSlackParameter(qreal value) +{ + if (mpData->adaptech_slack_parameter == value) + return *this; + mpData->adaptech_slack_parameter = value; + Q_EMIT adaptechSlackParameterChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::adaptechAlpha() const +{ + return mpData->adaptech_alpha; +} + +Config& Config::setAdaptechAlpha(qreal value) +{ + if (mpData->adaptech_alpha == value) + return *this; + mpData->adaptech_alpha = value; + Q_EMIT adaptechAlphaChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::bufferThreeThresholdFirst() const +{ + return mpData->buffer_3Threshold_first; +} + +Config& Config::setBufferThreeThresholdFirst(qreal value) +{ + if (mpData->buffer_3Threshold_first == value) + return *this; + mpData->buffer_3Threshold_first = value; + Q_EMIT bufferThreeThresholdFirstChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::bufferThreeThresholdSecond() const +{ + return mpData->buffer_3Threshold_second; +} + +Config& Config::setBufferThreeThresholdSecond(qreal value) +{ + if (mpData->buffer_3Threshold_second == value) + return *this; + mpData->buffer_3Threshold_second = value; + Q_EMIT bufferThreeThresholdSecondChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::bufferThreeThresholdThird() const +{ + return mpData->buffer_3Threshold_third; +} + +Config& Config::setBufferThreeThresholdThird(qreal value) +{ + if (mpData->buffer_3Threshold_third == value) + return *this; + mpData->buffer_3Threshold_third = value; + Q_EMIT bufferThreeThresholdThirdChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::pandaParamAlpha() const +{ + return mpData->panda_param_alpha; +} + +Config& Config::setPandaParamAlpha(qreal value) +{ + if (mpData->panda_param_alpha == value) + return *this; + mpData->panda_param_alpha = value; + Q_EMIT pandaParamAlphaChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::pandaParamBeta() const +{ + return mpData->panda_param_beta; +} + +Config& Config::setPandaParamBeta(qreal value) +{ + if (mpData->panda_param_beta == value) + return *this; + mpData->panda_param_beta = value; + Q_EMIT pandaParamBetaChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::pandaParamBMin() const +{ + return mpData->panda_param_Bmin; +} + +Config& Config::setPandaParamBMin(qreal value) +{ + if (mpData->panda_param_Bmin == value) + return *this; + mpData->panda_param_Bmin = value; + Q_EMIT pandaParamBMinChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::pandaParamK() const +{ + return mpData->panda_param_K; +} + +Config& Config::setPandaParamK(qreal value) +{ + if (mpData->panda_param_K == value) + return *this; + mpData->panda_param_K = value; + Q_EMIT pandaParamKChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::pandaParamW() const +{ + return mpData->panda_param_W; +} + +Config& Config::setPandaParamW(qreal value) +{ + if (mpData->panda_param_W == value) + return *this; + mpData->panda_param_W = value; + Q_EMIT pandaParamWChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::pandaParamEpsilon() const +{ + return mpData->panda_param_epsilon; +} + +Config& Config::setPandaParamEpsilon(qreal value) +{ + if (mpData->panda_param_epsilon == value) + return *this; + mpData->panda_param_epsilon = value; + Q_EMIT pandaParamEpsilon(); + Q_EMIT changed(); + return *this; +} + +qreal Config::bolaBufferTarget() const +{ + return mpData->bola_buffer_target; +} + +Config& Config::setBolaBufferTarget(qreal value) +{ + if (mpData->bola_buffer_target == value) + return *this; + mpData->bola_buffer_target = value; + Q_EMIT bolaBufferTargetChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::bolaAlpha() const +{ + return mpData->bola_alpha; +} + +Config& Config::setBolaAlpha(qreal value) +{ + if (mpData->bola_alpha == value) + return *this; + mpData->bola_alpha = value; + Q_EMIT bolaAlphaChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::repeat() const +{ + return mpData->repeat; +} + +Config& Config::setRepeat(bool value) +{ + if (mpData->repeat == value) + return *this; + mpData->repeat = value; + Q_EMIT repeatChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::graph() const +{ + return mpData->graph; +} + +Config& Config::setGraph(bool value) +{ + if (mpData->graph == value) + return *this; + mpData->graph = value; + Q_EMIT graphChanged(); + Q_EMIT changed(); + return *this; +} + +bool Config::fullScreen() const +{ + return mpData->full_screen; +} + +Config& Config::setFullScreen(bool value) +{ + if (mpData->full_screen == value) + return *this; + mpData->full_screen = value; + Q_EMIT fullScreenChanged(); + Q_EMIT changed(); + return *this; +} + +QString Config::lastFile() const +{ + return mpData->last_file; +} + +Config& Config::setLastFile(const QString &value) +{ + if (mpData->last_file == value) + return *this; + mpData->last_file = value; + Q_EMIT lastFileChanged(); + Q_EMIT changed(); + return *this; +} + +void Config::save() +{ + mpData->save(); +} + +QString Config::getConfigPath() { + return appDataDir(); +} + +bool Config::autotune() const +{ + return mpData->autotune; +} + +Config& Config::setAutotune(bool value) +{ + if (mpData->autotune == value) + return *this; + mpData->autotune = value; + Q_EMIT autotuneChanged(); + Q_EMIT changed(); + return *this; +} + +int Config::lifetime() const +{ + return mpData->lifetime; +} + +Config& Config::setLifetime(int value) +{ + if (mpData->lifetime == value) + return *this; + mpData->lifetime = value; + Q_EMIT lifetimeChanged(); + Q_EMIT changed(); + return *this; +} + +int Config::retransmissions() const +{ + return mpData->retransmissions; +} + +Config& Config::setRetransmissions(int value) +{ + if (mpData->retransmissions == value) + return *this; + mpData->retransmissions = value; + Q_EMIT retransmissionsChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::alpha() const +{ + return mpData->alpha; +} + +Config& Config::setAlpha(qreal value) +{ + if (mpData->alpha == value) + return *this; + mpData->alpha = value; + Q_EMIT alphaChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::beta() const +{ + return mpData->beta; +} + +Config& Config::setBeta(qreal value) +{ + if (mpData->beta == value) + return *this; + mpData->beta = value; + Q_EMIT betaChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::drop() const +{ + return mpData->drop; +} + +Config& Config::setDrop(qreal value) +{ + if (mpData->drop == value) + return *this; + mpData->drop = value; + Q_EMIT dropChanged(); + Q_EMIT changed(); + return *this; +} + + +qreal Config::betaWifi() const +{ + return mpData->beta_wifi; +} + +Config& Config::setBetaWifi(qreal value) +{ + if (mpData->beta_wifi == value) + return *this; + mpData->beta_wifi = value; + Q_EMIT betaWifiChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::dropWifi() const +{ + return mpData->drop_wifi; +} + +Config& Config::setDropWifi(qreal value) +{ + if (mpData->drop_wifi == value) + return *this; + mpData->drop_wifi = value; + Q_EMIT dropWifiChanged(); + Q_EMIT changed(); + return *this; +} + +int Config::delayWifi() const +{ + return mpData->delay_wifi; +} + +Config& Config::setDelayWifi(int value) +{ + if (mpData->delay_wifi == value) + return *this; + mpData->delay_wifi = value; + Q_EMIT delayWifiChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::betaLte() const +{ + return mpData->beta_lte; +} + +Config& Config::setBetaLte(qreal value) +{ + if (mpData->beta_lte == value) + return *this; + mpData->beta_lte = value; + Q_EMIT betaLteChanged(); + Q_EMIT changed(); + return *this; +} + +qreal Config::dropLte() const +{ + return mpData->drop_lte; +} + +Config& Config::setDropLte(qreal value) +{ + if (mpData->drop_lte == value) + return *this; + mpData->drop_lte = value; + Q_EMIT dropLteChanged(); + Q_EMIT changed(); + return *this; +} + +int Config::delayLte() const +{ + return mpData->delay_lte; +} + +Config& Config::setDelayLte(int value) +{ + if (mpData->delay_lte == value) + return *this; + mpData->delay_lte = value; + Q_EMIT delayLteChanged(); + Q_EMIT changed(); + return *this; +} + +int Config::batchingParameter() const +{ + return mpData->batching_parameter; +} + +Config& Config::setBatchingParameter(int value) +{ + if (mpData->batching_parameter == value) + return *this; + mpData->batching_parameter = value; + Q_EMIT batchingParameterChanged(); + Q_EMIT changed(); + return *this; +} + +int Config::rateEstimator() const +{ + return mpData->rate_estimator; +} + +Config& Config::setRateEstimator(int value) +{ + if (mpData->rate_estimator == value) + return *this; + mpData->rate_estimator = value; + Q_EMIT rateEstimatorChanged(); + Q_EMIT changed(); + return *this; +} diff --git a/Common/Config.h b/Common/Config.h new file mode 100644 index 00000000..018b757f --- /dev/null +++ b/Common/Config.h @@ -0,0 +1,463 @@ +/****************************************************************************** + QtAV Player Demo: this file is part of QtAV examples + Copyright (C) 2012-2016 Wang Bin +* This file is part of QtAV (from 2014) + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#ifndef PLAYER_CONFIG_H +#define PLAYER_CONFIG_H + +#include "CommonExport.h" +#include +#include +#include +#include +#include + + +class COMMON_EXPORT Config : public QObject +{ + Q_OBJECT + Q_PROPERTY(QVariantList history READ history NOTIFY historyChanged) + // last file opened by file dialog + Q_PROPERTY(QString lastFile READ lastFile WRITE setLastFile NOTIFY lastFileChanged) + Q_PROPERTY(qreal forceFrameRate READ forceFrameRate WRITE setForceFrameRate NOTIFY forceFrameRateChanged) + Q_PROPERTY(QStringList decoderPriorityNames READ decoderPriorityNames WRITE setDecoderPriorityNames NOTIFY decoderPriorityNamesChanged) + Q_PROPERTY(bool zeroCopy READ zeroCopy WRITE setZeroCopy NOTIFY zeroCopyChanged) + Q_PROPERTY(QString captureDir READ captureDir WRITE setCaptureDir NOTIFY captureDirChanged) + Q_PROPERTY(QString captureFormat READ captureFormat WRITE setCaptureFormat NOTIFY captureFormatChanged) + Q_PROPERTY(int captureQuality READ captureQuality WRITE setCaptureQuality NOTIFY captureQualityChanged) + Q_PROPERTY(QStringList subtitleEngines READ subtitleEngines WRITE setSubtitleEngines NOTIFY subtitleEnginesChanged) + Q_PROPERTY(bool subtitleAutoLoad READ subtitleAutoLoad WRITE setSubtitleAutoLoad NOTIFY subtitleAutoLoadChanged) + Q_PROPERTY(bool subtitleEnabled READ subtitleEnabled WRITE setSubtitleEnabled NOTIFY subtitleEnabledChanged) + Q_PROPERTY(QFont subtitleFont READ subtitleFont WRITE setSubtitleFont NOTIFY subtitleFontChanged) + Q_PROPERTY(QColor subtitleColor READ subtitleColor WRITE setSubtitleColor NOTIFY subtitleColorChanged) + Q_PROPERTY(QColor subtitleOutlineColor READ subtitleOutlineColor WRITE setSubtitleOutlineColor NOTIFY subtitleOutlineColorChanged) + Q_PROPERTY(bool subtitleOutline READ subtitleOutline WRITE setSubtitleOutline NOTIFY subtitleOutlineChanged) + Q_PROPERTY(int subtitleBottomMargin READ subtitleBottomMargin WRITE setSubtitleBottomMargin NOTIFY subtitleBottomMarginChanged) + Q_PROPERTY(qreal subtitleDelay READ subtitleDelay WRITE setSubtitleDelay NOTIFY subtitleDelayChanged) + // font properties for libass engine + Q_PROPERTY(QString assFontFile READ assFontFile WRITE setAssFontFile NOTIFY assFontFileChanged) + Q_PROPERTY(QString assFontsDir READ assFontsDir WRITE setAssFontsDir NOTIFY assFontsDirChanged) + Q_PROPERTY(bool assFontFileForced READ isAssFontFileForced WRITE setAssFontFileForced NOTIFY assFontFileForcedChanged) + + Q_PROPERTY(bool previewEnabled READ previewEnabled WRITE setPreviewEnabled NOTIFY previewEnabledChanged) + Q_PROPERTY(int previewWidth READ previewWidth WRITE setPreviewWidth NOTIFY previewWidthChanged) + Q_PROPERTY(int previewHeight READ previewHeight WRITE setPreviewHeight NOTIFY previewHeightChanged) + Q_PROPERTY(bool EGL READ isEGL WRITE setEGL NOTIFY EGLChanged) + Q_PROPERTY(OpenGLType openGLType READ openGLType WRITE setOpenGLType NOTIFY openGLTypeChanged) + Q_PROPERTY(QString ANGLEPlatform READ getANGLEPlatform WRITE setANGLEPlatform NOTIFY ANGLEPlatformChanged) + Q_PROPERTY(bool avformatOptionsEnabled READ avformatOptionsEnabled WRITE setAvformatOptionsEnabled NOTIFY avformatOptionsEnabledChanged) + Q_PROPERTY(qreal timeout READ timeout WRITE setTimeout NOTIFY timeoutChanged) + Q_PROPERTY(int bufferValue READ bufferValue WRITE setBufferValue NOTIFY bufferValueChanged) + Q_PROPERTY(QString logLevel READ logLevel WRITE setLogLevel NOTIFY logLevelChanged) + Q_ENUMS(OpenGLType) + Q_PROPERTY(QString language READ language WRITE setLanguage NOTIFY languageChanged) + + Q_PROPERTY(bool userShaderEnabled READ userShaderEnabled WRITE setUserShaderEnabled NOTIFY userShaderEnabledChanged) + Q_PROPERTY(bool intermediateFBO READ intermediateFBO WRITE setIntermediateFBO NOTIFY intermediateFBOChanged) + Q_PROPERTY(QString fragHeader READ fragHeader WRITE setFragHeader NOTIFY fragHeaderChanged) + Q_PROPERTY(QString fragSample READ fragSample WRITE setFragSample NOTIFY fragSampleChanged) + Q_PROPERTY(QString fragPostProcess READ fragPostProcess WRITE setFragPostProcess NOTIFY fragPostProcessChanged) + Q_PROPERTY(QString icnPrefix READ icnPrefix WRITE setIcnPrefix NOTIFY icnPrefixChanged) + Q_PROPERTY(QString httpPrefix READ httpPrefix WRITE setHttpPrefix NOTIFY httpPrefixChanged) + Q_PROPERTY(QString icnSuffix READ icnSuffix WRITE setIcnSuffix NOTIFY icnSuffixChanged) + Q_PROPERTY(QString httpSuffix READ httpSuffix WRITE setHttpSuffix NOTIFY httpSuffixChanged) + + Q_PROPERTY(qreal segmentBufferSize READ segmentBufferSize WRITE setSegmentBufferSize NOTIFY segmentBufferSizeChanged) + Q_PROPERTY(QString lastPlayed READ lastPlayed WRITE setLastPlayed NOTIFY lastPlayedChanged) + Q_PROPERTY(QString adaptationLogic READ adaptationLogic WRITE setAdaptationLogic NOTIFY adaptationLogicChanged) + Q_PROPERTY(bool icn READ icn WRITE setIcn NOTIFY icnChanged) + Q_PROPERTY(qreal rateAlpha READ rateAlpha WRITE setRateAlpha NOTIFY rateAlphaChanged) + Q_PROPERTY(qreal bufferReservoirThreshold READ bufferReservoirThreshold WRITE setBufferReservoirThreshold NOTIFY bufferReservoirThresholdChanged) + Q_PROPERTY(qreal bufferMaxThreshold READ bufferMaxThreshold WRITE setBufferMaxThreshold NOTIFY bufferMaxThresholdChanged) + Q_PROPERTY(qreal adaptechFirstThreshold READ adaptechFirstThreshold WRITE setAdaptechFirstThreshold NOTIFY adaptechFirstThresholdChanged) + Q_PROPERTY(qreal adaptechSecondThreshold READ adaptechSecondThreshold WRITE setAdaptechSecondThreshold NOTIFY adaptechSecondThresholdChanged) + Q_PROPERTY(qreal adaptechSwitchUpMargin READ adaptechSwitchUpMargin WRITE setAdaptechSwitchUpMargin NOTIFY adaptechSwitchUpMarginChanged) + Q_PROPERTY(qreal adaptechSlackParameter READ adaptechSlackParameter WRITE setAdaptechSlackParameter NOTIFY adaptechSlackParameterChanged) + Q_PROPERTY(qreal adaptechAlpha READ adaptechAlpha WRITE setAdaptechAlpha NOTIFY adaptechAlphaChanged) + Q_PROPERTY(qreal bufferThreeThresholdFirst READ bufferThreeThresholdFirst WRITE setBufferThreeThresholdFirst NOTIFY bufferThreeThresholdFirstChanged) + Q_PROPERTY(qreal bufferThreeThresholdSecond READ bufferThreeThresholdSecond WRITE setBufferThreeThresholdSecond NOTIFY bufferThreeThresholdSecondChanged) + Q_PROPERTY(qreal bufferThreeThresholdThird READ bufferThreeThresholdThird WRITE setBufferThreeThresholdThird NOTIFY bufferThreeThresholdThirdChanged) + Q_PROPERTY(qreal pandaParamAlpha READ pandaParamAlpha WRITE setPandaParamAlpha NOTIFY pandaParamAlphaChanged) + Q_PROPERTY(qreal pandaParamBeta READ pandaParamBeta WRITE setPandaParamBeta NOTIFY pandaParamBetaChanged) + Q_PROPERTY(qreal pandaParamBMin READ pandaParamBMin WRITE setPandaParamBMin NOTIFY pandaParamBMinChanged) + Q_PROPERTY(qreal pandaParamK READ pandaParamK WRITE setPandaParamK NOTIFY pandaParamKChanged) + Q_PROPERTY(qreal pandaParamW READ pandaParamW WRITE setPandaParamW NOTIFY pandaParamWChanged) + Q_PROPERTY(qreal pandaParamEpsilon READ pandaParamEpsilon WRITE setPandaParamAlpha NOTIFY pandaParamEpsilonChanged) + Q_PROPERTY(qreal bolaBufferTarget READ bolaBufferTarget WRITE setBolaBufferTarget NOTIFY bolaBufferTargetChanged) + Q_PROPERTY(qreal bolaAlpha READ bolaAlpha WRITE setBolaAlpha NOTIFY bolaAlphaChanged) + Q_PROPERTY(bool repeat READ repeat WRITE setRepeat NOTIFY repeatChanged) + Q_PROPERTY(bool graph READ graph WRITE setGraph NOTIFY graphChanged) + Q_PROPERTY(bool fullScreen READ fullScreen WRITE setFullScreen NOTIFY fullScreenChanged) + Q_PROPERTY(bool autotune READ autotune WRITE setAutotune NOTIFY autotuneChanged) + Q_PROPERTY(int lifetime READ lifetime WRITE setLifetime NOTIFY lifetimeChanged) + Q_PROPERTY(int retransmissions READ retransmissions WRITE setRetransmissions NOTIFY retransmissionsChanged) + Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged) + Q_PROPERTY(qreal beta READ beta WRITE setBeta NOTIFY betaChanged) + Q_PROPERTY(qreal drop READ drop WRITE setDrop NOTIFY dropChanged) + Q_PROPERTY(qreal betaWifi READ betaWifi WRITE setBetaWifi NOTIFY betaWifiChanged) + Q_PROPERTY(qreal dropWifi READ dropWifi WRITE setDropWifi NOTIFY dropWifiChanged) + Q_PROPERTY(int delayWifi READ delayWifi WRITE setDelayWifi NOTIFY delayWifiChanged) + Q_PROPERTY(qreal betaLte READ betaLte WRITE setBetaLte NOTIFY betaLteChanged) + Q_PROPERTY(qreal dropLte READ dropLte WRITE setDropLte NOTIFY dropLteChanged) + Q_PROPERTY(int delayLte READ delayLte WRITE setDelayLte NOTIFY delayLteChanged) + Q_PROPERTY(int batchingParameter READ batchingParameter WRITE setBatchingParameter NOTIFY batchingParameterChanged) + Q_PROPERTY(int rateEstimator READ rateEstimator WRITE setRateEstimator NOTIFY rateEstimatorChanged) + +public: + enum OpenGLType { // currently only for windows + Auto, + Desktop, + OpenGLES, + Software + }; + + static Config& instance(); + static void setName(const QString& name); // config file base name + static QString getName(); + /*! + * \brief defaultConfigFile + * Config file name is $appname.ini. Must call Config::setName() first + */ + static QString defaultConfigFile(); + static QString defaultDir(); + Q_INVOKABLE bool reset(); + void reload(); + //void loadFromFile(const QString& file); + QString getConfigPath(); + QString lastFile() const; + Config& setLastFile(const QString& value); + + qreal forceFrameRate() const; + Config& setForceFrameRate(qreal value); + // in priority order. the same order as displayed in ui + QStringList decoderPriorityNames() const; + Config& setDecoderPriorityNames(const QStringList& names); + + bool zeroCopy() const; + Config& setZeroCopy(bool value); + + QString captureDir() const; + Config& setCaptureDir(const QString& dir); + + /*! + * \brief captureFormat + * can be "yuv" to capture yuv image without convertion. the suffix is the yuv format, e.g. "yuv420p", "nv12" + * or can be "jpg", "png" + * \return + */ + QString captureFormat() const; + Config& setCaptureFormat(const QString& format); + // only works for non-yuv capture. value: -1~100, -1: default + int captureQuality() const; + Config& setCaptureQuality(int quality); + + QStringList subtitleEngines() const; + Config& setSubtitleEngines(const QStringList& value); + bool subtitleAutoLoad() const; + Config& setSubtitleAutoLoad(bool value); + bool subtitleEnabled() const; + Config& setSubtitleEnabled(bool value); + + QFont subtitleFont() const; + Config& setSubtitleFont(const QFont& value); + bool subtitleOutline() const; + Config& setSubtitleOutline(bool value); + QColor subtitleColor() const; + Config& setSubtitleColor(const QColor& value); + QColor subtitleOutlineColor() const; + Config& setSubtitleOutlineColor(const QColor& value); + int subtitleBottomMargin() const; + Config& setSubtitleBottomMargin(int value); + + qreal subtitleDelay() const; + Config& setSubtitleDelay(qreal value); + + QString assFontFile() const; + Config& setAssFontFile(const QString& value); + QString assFontsDir() const; + Config& setAssFontsDir(const QString& value); + bool isAssFontFileForced() const; + Config& setAssFontFileForced(bool value); + + bool previewEnabled() const; + Config& setPreviewEnabled(bool value); + int previewWidth() const; + Config& setPreviewWidth(int value); + int previewHeight() const; + Config& setPreviewHeight(int value); + + QVariantHash avformatOptions() const; + bool avformatOptionsEnabled() const; + Config& setAvformatOptionsEnabled(bool value); + int analyzeDuration() const; + Config& analyzeDuration(int ad); + unsigned int probeSize() const; + Config& probeSize(unsigned int ps); + bool reduceBuffering() const; + Config& reduceBuffering(bool y); + QString avformatExtra() const; + Config& avformatExtra(const QString& text); + + QString avfilterVideoOptions() const; + Config& avfilterVideoOptions(const QString& options); + bool avfilterVideoEnable() const; + Config& avfilterVideoEnable(bool e); + + QString avfilterAudioOptions() const; + Config& avfilterAudioOptions(const QString& options); + bool avfilterAudioEnable() const; + Config& avfilterAudioEnable(bool e); + + // currently only for xcb + bool isEGL() const; + Config& setEGL(bool value); + // can be "Desktop", "OpenGLES", "Software" + OpenGLType openGLType() const; + Config& setOpenGLType(OpenGLType value); + + QString getANGLEPlatform() const; + Config& setANGLEPlatform(const QString &value); + + // ms >0. default 30000ms + qreal timeout() const; + Config& setTimeout(qreal value); + + bool abortOnTimeout() const; + Config& setAbortOnTimeout(bool value); + + // <0: auto + int bufferValue() const; + Config& setBufferValue(int value); + + // can be: "", "off", "debug", "warning", "critical", "fatal", "all" + QString logLevel() const; + Config& setLogLevel(const QString& value); + + QString language() const; + Config& setLanguage(const QString& value); + + Q_INVOKABLE QVariant operator ()(const QString& key) const; + Q_INVOKABLE Config& operator ()(const QString& key, const QVariant& value); + + /// history will not be clear in reset() + QVariantList history() const; + // {url: urlString, start: ms, duration: ms} + Q_INVOKABLE void addHistory(const QVariantMap& value); + Q_INVOKABLE void removeHistory(const QString& url); + Q_INVOKABLE void clearHistory(); + + Config& setUserShaderEnabled(bool value); + bool userShaderEnabled() const; + Config& setIntermediateFBO(bool value); + bool intermediateFBO() const; + Config& setFragHeader(const QString& text); + QString fragHeader() const; + Config& setFragSample(const QString& text); + QString fragSample() const; + Config& setFragPostProcess(const QString& text); + QString fragPostProcess() const; + Config& setIcnPrefix(const QString &value); + QString icnPrefix() const; + Config& setIcnSuffix(const QString &value); + QString icnSuffix() const; + Config& setHttpPrefix(const QString &value); + QString httpPrefix() const; + Config& setHttpSuffix(const QString &value); + QString httpSuffix() const; + Config& setSegmentBufferSize(qreal value); + qreal segmentBufferSize() const; + Config& setLastPlayed(const QString &value); + QString lastPlayed() const; + Config& setAdaptationLogic(const QString &value); + QString adaptationLogic() const; + Config& setIcn(bool value); + bool icn() const; + Config& setRateAlpha(qreal value); + qreal rateAlpha() const; + Config& setBufferReservoirThreshold(qreal value); + qreal bufferReservoirThreshold() const; + Config& setBufferMaxThreshold(qreal value); + qreal bufferMaxThreshold() const; + Config& setAdaptechFirstThreshold(qreal value); + qreal adaptechFirstThreshold() const; + Config& setAdaptechSecondThreshold(qreal value); + qreal adaptechSecondThreshold() const; + Config& setAdaptechSwitchUpMargin(qreal value); + qreal adaptechSwitchUpMargin() const; + Config& setAdaptechSlackParameter(qreal value); + qreal adaptechSlackParameter() const; + Config& setAdaptechAlpha(qreal value); + qreal adaptechAlpha() const; + Config& setBufferThreeThresholdFirst(qreal value); + qreal bufferThreeThresholdFirst() const; + Config& setBufferThreeThresholdSecond(qreal value); + qreal bufferThreeThresholdSecond() const; + Config& setBufferThreeThresholdThird(qreal value); + qreal bufferThreeThresholdThird() const; + Config& setPandaParamAlpha(qreal value); + qreal pandaParamAlpha() const; + Config& setPandaParamBeta(qreal value); + qreal pandaParamBeta() const; + Config& setPandaParamBMin(qreal value); + qreal pandaParamBMin() const; + Config& setPandaParamK(qreal value); + qreal pandaParamK() const; + Config& setPandaParamW(qreal value); + qreal pandaParamW() const; + Config& setPandaParamEpsilon(qreal value); + qreal pandaParamEpsilon() const; + Config& setBolaBufferTarget(qreal value); + qreal bolaBufferTarget() const; + Config& setBolaAlpha(qreal value); + qreal bolaAlpha() const; + Config& setRepeat(bool value); + bool repeat() const; + Config& setGraph(bool value); + bool graph() const; + Config& setFullScreen(bool value); + bool fullScreen() const; + Config& setAutotune(bool value); + bool autotune() const; + Config& setLifetime(int value); + int lifetime() const; + Config& setRetransmissions(int value); + int retransmissions() const; + Config& setAlpha(qreal value); + qreal alpha() const; + Config& setBeta(qreal value); + qreal beta() const; + Config& setDrop(qreal value); + qreal drop() const; + Config& setBetaWifi(qreal value); + qreal betaWifi() const; + Config& setDropWifi(qreal value); + qreal dropWifi() const; + Config& setDelayWifi(int value); + int delayWifi() const; + Config& setBetaLte(qreal value); + qreal betaLte() const; + Config& setDropLte(qreal value); + qreal dropLte() const; + Config& setDelayLte(int value); + int delayLte() const; + Config& setBatchingParameter(int value); + int batchingParameter() const; + Config& setRateEstimator(int value); + int rateEstimator() const; + + +public: + Q_SIGNAL void changed(); + Q_SIGNAL void userShaderEnabledChanged(); + Q_SIGNAL void intermediateFBOChanged(); + Q_SIGNAL void fragHeaderChanged(); + Q_SIGNAL void fragSampleChanged(); + Q_SIGNAL void fragPostProcessChanged(); + + Q_SIGNAL void lastFileChanged(); + //keyword 'signals' maybe protected. we need call the signals in other classes. Q_SIGNAL is empty + Q_SIGNAL void forceFrameRateChanged(); + Q_SIGNAL void decodingThreadsChanged(int n); + Q_SIGNAL void decoderPriorityNamesChanged(); + Q_SIGNAL void registeredDecodersChanged(const QVector& r); + Q_SIGNAL void zeroCopyChanged(); + Q_SIGNAL void captureDirChanged(const QString& dir); + Q_SIGNAL void captureFormatChanged(const QString& fmt); + Q_SIGNAL void captureQualityChanged(int quality); + Q_SIGNAL void avfilterVideoChanged(); + Q_SIGNAL void avfilterAudioChanged(); + Q_SIGNAL void subtitleEnabledChanged(); + Q_SIGNAL void subtitleAutoLoadChanged(); + Q_SIGNAL void subtitleEnginesChanged(); + Q_SIGNAL void subtitleFontChanged(); + Q_SIGNAL void subtitleColorChanged(); + Q_SIGNAL void subtitleOutlineChanged(); + Q_SIGNAL void subtitleOutlineColorChanged(); + Q_SIGNAL void subtitleBottomMarginChanged(); + Q_SIGNAL void subtitleDelayChanged(); + Q_SIGNAL void assFontFileChanged(); + Q_SIGNAL void assFontsDirChanged(); + Q_SIGNAL void assFontFileForcedChanged(); + Q_SIGNAL void previewEnabledChanged(); + Q_SIGNAL void previewWidthChanged(); + Q_SIGNAL void previewHeightChanged(); + Q_SIGNAL void EGLChanged(); + Q_SIGNAL void openGLTypeChanged(); + Q_SIGNAL void ANGLEPlatformChanged(); + Q_SIGNAL void avformatOptionsEnabledChanged(); + Q_SIGNAL void bufferValueChanged(); + Q_SIGNAL void timeoutChanged(); + Q_SIGNAL void abortOnTimeoutChanged(); + Q_SIGNAL void logLevelChanged(); + Q_SIGNAL void languageChanged(); + Q_SIGNAL void historyChanged(); + Q_SIGNAL void icnSuffixChanged(); + Q_SIGNAL void httpSuffixChanged(); + Q_SIGNAL void icnPrefixChanged(); + Q_SIGNAL void httpPrefixChanged(); + Q_SIGNAL void segmentBufferSizeChanged(); + Q_SIGNAL void lastPlayedChanged(); + Q_SIGNAL void adaptationLogicChanged(); + Q_SIGNAL void icnChanged(); + Q_SIGNAL void rateAlphaChanged(); + Q_SIGNAL void bufferReservoirThresholdChanged(); + Q_SIGNAL void bufferMaxThresholdChanged(); + Q_SIGNAL void adaptechFirstThresholdChanged(); + Q_SIGNAL void adaptechSecondThresholdChanged(); + Q_SIGNAL void adaptechSwitchUpMarginChanged(); + Q_SIGNAL void adaptechSlackParameterChanged(); + Q_SIGNAL void adaptechAlphaChanged(); + Q_SIGNAL void bufferThreeThresholdFirstChanged(); + Q_SIGNAL void bufferThreeThresholdSecondChanged(); + Q_SIGNAL void bufferThreeThresholdThirdChanged(); + Q_SIGNAL void pandaParamAlphaChanged(); + Q_SIGNAL void pandaParamBetaChanged(); + Q_SIGNAL void pandaParamBMinChanged(); + Q_SIGNAL void pandaParamKChanged(); + Q_SIGNAL void pandaParamWChanged(); + Q_SIGNAL void pandaParamEpsilonChanged(); + Q_SIGNAL void bolaAlphaChanged(); + Q_SIGNAL void bolaBufferTargetChanged(); + Q_SIGNAL void repeatChanged(); + Q_SIGNAL void graphChanged(); + Q_SIGNAL void fullScreenChanged(); + Q_SIGNAL void autotuneChanged(); + Q_SIGNAL void lifetimeChanged(); + Q_SIGNAL void retransmissionsChanged(); + Q_SIGNAL void alphaChanged(); + Q_SIGNAL void betaChanged(); + Q_SIGNAL void dropChanged(); + Q_SIGNAL void betaWifiChanged(); + Q_SIGNAL void dropWifiChanged(); + Q_SIGNAL void delayWifiChanged(); + Q_SIGNAL void betaLteChanged(); + Q_SIGNAL void dropLteChanged(); + Q_SIGNAL void delayLteChanged(); + Q_SIGNAL void batchingParameterChanged(); + Q_SIGNAL void rateEstimatorChanged(); + +protected: + explicit Config(QObject *parent = 0); + ~Config(); + +public Q_SLOTS: + void save(); + +private: + class Data; + Data *mpData; +}; + +#endif // PLAYER_CONFIG_H diff --git a/Common/Info.plist b/Common/Info.plist new file mode 100644 index 00000000..8e33963e --- /dev/null +++ b/Common/Info.plist @@ -0,0 +1,225 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + AAC + AC3 + AIFF + M4A + MKA + MP3 + OGG + PCM + VAW + WAV + WAW + WMA + aac + ac3 + aiff + m4a + mka + mp3 + ogg + pcm + vaw + wav + waw + wma + + CFBundleTypeIconFile + document.icns + CFBundleTypeName + Audio file + CFBundleTypeRole + Viewer + LSTypeIsPackage + + NSPersistentStoreTypeKey + XML + + + CFBundleTypeExtensions + + * + * + 3GP + 3IV + 3gp + 3iv + ASF + AVI + CPK + DAT + DIVX + DV + FLAC + FLI + FLV + H264 + I263 + M2TS + M4V + MKV + MOV + MP2 + MP4 + MPEG + MPG + MPG2 + MPG4 + NSV + NUT + NUV + OGG + OGM + QT + RM + RMVB + VCD + VFW + VOB + WEBM + WMV + asf + avi + cpk + dat + divx + dv + flac + fli + flv + h264 + i263 + m2ts + m4v + mkv + mov + mp2 + mp4 + mpeg + mpg + mpg2 + mpg4 + mts + nsv + nut + nuv + ogg + ogm + qt + rm + rmvb + vcd + vfw + vob + webm + wmv + f4v + ts + + CFBundleTypeIconFile + document.icns + CFBundleTypeName + Movie file + CFBundleTypeRole + Viewer + LSTypeIsPackage + + NSPersistentStoreTypeKey + XML + + + CFBundleTypeExtensions + + AQT + ASS + JSS + RT + SMI + SRT + SSA + SUB + TXT + UTF + aqt + ass + jss + rt + smi + srt + ssa + sub + txt + utf + + CFBundleTypeIconFile + document.icns + CFBundleTypeName + Subtitles file + CFBundleTypeRole + Viewer + LSTypeIsPackage + + NSPersistentStoreTypeKey + XML + + + CFBundleExecutable + @EXECUTABLE@ + CFBundleIconFile + Cisco.icns + CFBundleIdentifier + com.cisco.@EXECUTABLE@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + QtPlayer @EXECUTABLE@ + CFBundleDisplayName + QtPlayer @EXECUTABLE@ + LSMinimumSystemVersionByArchitecture + + x86_64 + 10.6.0 + + CFBundlePackageType + APPL + CFBundleShortVersionString + @SHORT_VERSION@ + CFBundleVersion + @FULL_VERSION@ + NSHighResolutionCapable + + UIFileSharingEnabled + + CFBundleURLTypes + + + CFBundleTypeRole + Viewer + CFBundleURLName + Streaming Protocol + CFBundleURLSchemes + + mms + mmst + http + httpproxy + rtp + rtsp + ftp + udp + smb + + + + + diff --git a/Common/QOptions.cpp b/Common/QOptions.cpp new file mode 100644 index 00000000..82bf2184 --- /dev/null +++ b/Common/QOptions.cpp @@ -0,0 +1,375 @@ +/****************************************************************************** + QOptions: make command line options easy. https://github.com/wang-bin/qoptions + Copyright (C) 2011-2015 Wang Bin + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +******************************************************************************/ + +#include "QOptions.h" +#include +#include + +QOption::QOption() +{} + +QOption::QOption(const char *name, const QVariant &defaultValue, Type type, const QString &description) +:mType(type),mDescription(description),mDefaultValue(defaultValue) +{ + setName(QLatin1String(name)); +} + +QOption::QOption(const char *name, Type type, const QString &description) +:mType(type),mDescription(description),mDefaultValue(QVariant()) +{ + //if (mType==QOption::NoToken) + // mValue = false; + setName(QLatin1String(name)); +} +/* +QOption::QOption(const char *name, const QVariant& value, Type type, const QString &description) +:mType(type),mDescription(description),mDefaultValue(*value),mValue(value) +{qDebug("%s %s %d", __FILE__, __FUNCTION__, __LINE__); + setName(name); +} +*/ + +QString QOption::shortName() const +{ + return mShortName; +} + +QString QOption::longName() const +{ + return mLongName; +} + +QString QOption::formatName() const +{ + if (mLongName.isEmpty()) + return QLatin1String("-") + mShortName; + if (mShortName.isEmpty()) + return QLatin1String("--") + mLongName; + return QString::fromLatin1("-%1 [--%2]").arg(mShortName).arg(mLongName); +} + +QString QOption::description() const +{ + return mDescription; +} + +QVariant QOption::value() const +{ + if (mValue.isValid()) + return mValue; + return mDefaultValue; +} + +void QOption::setValue(const QVariant &value) +{ + mValue = value; +} + +bool QOption::isSet() const +{ + return mValue.isValid(); +} + +bool QOption::isValid() const +{ + return !shortName().isEmpty() || !longName().isEmpty(); +} + +void QOption::setType(QOption::Type type) +{ + mType = type; + if (mType==NoToken) + mValue = false; +} + +QOption::Type QOption::type() const +{ + return mType; +} + +QString QOption::help() const +{ + QString message = formatName(); + if (type()==QOption::SingleToken) + message.append(QLatin1String(" value")); + else if (type()==QOption::MultiToken) + message.append(QLatin1String(" value1 ... valueN")); + + message = QString::fromLatin1("%1").arg(message, -33); + message.append(mDescription); + if (mDefaultValue.isValid() && !mDefaultValue.toString().isEmpty()) + message.append(QLatin1String(" (default: ") + mDefaultValue.toString() + QLatin1String(")")); + return message; +} + +bool QOption::operator <(const QOption& o) const +{ + return mType < o.type() || mShortName < o.shortName() || mLongName < o.longName() || mDescription < o.description(); +} + +static QString get_short(const QString& name) +{ + if (name.startsWith(QLatin1String("--"))) + return QString(); + if (name.startsWith(QLatin1String("-"))) + return name.mid(1); + return name; +} + +static QString get_long(const QString& name) +{ + if (name.startsWith(QLatin1String("--"))) + return name.mid(2); + if (name.startsWith(QLatin1String("-"))) + return QString(); + return name; +} + +void QOption::setName(const QString &name) +{ + int comma = name.indexOf(QLatin1Char(',')); + if (comma>0) { + QString name1 = name.left(comma); + QString name2 = name.mid(comma+1); + if (name1.startsWith(QLatin1String("--"))) { + mLongName = name1.mid(2); + mShortName = get_short(name2); + } else if (name1.startsWith(QLatin1String("-"))) { + mShortName = name1.mid(1); + mLongName = get_long(name2); + } else { + if (name2.startsWith(QLatin1String("--"))) { + mLongName = name2.mid(2); + mShortName = name1; + } else if (name2.startsWith(QLatin1String("-"))) { + mShortName = name2.mid(1); + mLongName = name1; + } else { + mShortName = name2; + mLongName = name1; + } + } + } else { + if (name.startsWith(QLatin1String("--"))) + mLongName = name.mid(2); + else if (name.startsWith(QLatin1String("-"))) + mShortName = name.mid(1); + else + mShortName = name; + } +} + + + +QOptions::QOptions() +{ +} + +QOptions::~QOptions() +{ + mOptionGroupMap.clear(); + mOptions.clear(); +} + +bool QOptions::parse(int argc, const char *const*argv) +{ + if (mOptionGroupMap.isEmpty()) + return false; + + if (argc==1) + return true; + + bool result = true; + QStringList args; + for (int i=1;i::Iterator it_list; + mOptions = mOptionGroupMap.keys(); + + while (it != args.end()) { + if (it->startsWith(QLatin1String("--"))) { + int e = it->indexOf(QLatin1Char('=')); + for (it_list = mOptions.begin(); it_list != mOptions.end(); ++it_list) { + if (it_list->longName() == it->mid(2,e-2)) { + if (it_list->type()==QOption::NoToken) { + it_list->setValue(true); + //qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString())); + it = args.erase(it); + break; + } + if (e>0) { // + it_list->setValue(it->mid(e+1)); + //qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString())); + } else { + it = args.erase(it); + if (it == args.end()) + break; + it_list->setValue(*it); + //qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString())); + } + it = args.erase(it); + break; + } + } + if (it_list == mOptions.end()) { + qWarning() << "unknown option: " << *it; + result = false; + ++it; + } + //handle unknown option + } else if (it->startsWith(QLatin1Char('-'))) { + for (it_list = mOptions.begin(); it_list != mOptions.end(); ++it_list) { + QString sname = it_list->shortName(); + int sname_len = sname.length(); //usally is 1 + //TODO: startsWith(-height,-h) Not endsWith, -oabco + if (it->midRef(1).compare(sname) == 0) { + if (it_list->type() == QOption::NoToken) { + it_list->setValue(true); + it = args.erase(it); + break; + } + if (it->length() == sname_len+1) {//-o abco + it = args.erase(it); + if (it == args.end()) + break; + it_list->setValue(*it); + //qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString())); + } else { + it_list->setValue(it->mid(sname_len+1)); + //qDebug("%d %s", __LINE__, qPrintable(it_list->value().toString())); + } + it = args.erase(it); + break; + } + } + if (it_list==mOptions.end()) { + qWarning() << "unknown option: " << *it; + result = false; + ++it; + } + //handle unknown option + } else { + qWarning() << "unknown option: " << *it; + ++it; + } + } + if (!result) { + print(); + } + return result; +} + +QOptions& QOptions::add(const QString &group_description) +{ + mCurrentDescription = group_description; + return *this; +} + +QOptions& QOptions::addDescription(const QString &description) +{ + mDescription = description; + return *this; +} + +QOptions& QOptions::operator ()(const char* name, const QString& description) +{ + QOption op(name, QOption::NoToken, description); + mOptions.append(op); + mOptionGroupMap.insert(op, mCurrentDescription); + return *this; +} + +QOptions& QOptions::operator ()(const char* name, QOption::Type type, const QString& description) +{ + QOption op(name, type, description); + mOptions.append(op); + mOptionGroupMap.insert(op, mCurrentDescription); + return *this; +} + +QOptions& QOptions::operator ()(const char* name, const QVariant& defaultValue, const QString& description) +{ + QOption op(name, defaultValue, QOption::SingleToken, description); + mOptions.append(op); + mOptionGroupMap.insert(op, mCurrentDescription); + return *this; +} + +QOptions& QOptions::operator ()(const char* name, const QVariant& defaultValue, QOption::Type type, const QString& description) +{ + QOption op(name, defaultValue, type, description); + mOptions.append(op); + mOptionGroupMap.insert(op, mCurrentDescription); + return *this; +} +/* + +QOptions& QOptions::operator ()(const char* name, const QVariant& value, QOption::Type type, const QString& description) +{ + QOption op(name, value, type, description); + mOptions.append(op); + mOptionGroupMap.insert(op, mCurrentDescription); + return *this; +} +*/ + +QOption QOptions::option(const QString &name) const +{ + if (mOptions.isEmpty()) + return QOption(); + QList::ConstIterator it_list; + for (it_list=mOptions.constBegin(); it_list!=mOptions.constEnd(); ++it_list) { + if (it_list->shortName()==name || it_list->longName()==name) { + return *it_list; + } + } + return QOption(); +} + +QVariant QOptions::value(const QString& name) const +{ + return option(name).value(); +} + +QVariant QOptions::operator [](const QString& name) const +{ + return value(name); +} + +QString QOptions::help() const +{ + QString message = mDescription; + QStringList groups = mOptionGroupMap.values(); + groups.removeDuplicates(); + QList::ConstIterator it; + + QList::ConstIterator it_op; + for (it=groups.constBegin(); it!=groups.constEnd(); ++it) { + message.append(QLatin1String("\n")).append(*it); + QList options = mOptionGroupMap.keys(*it); + for (it_op=options.constBegin();it_op!=options.constEnd();++it_op) + message.append(QLatin1String("\n ")).append(it_op->help()); + } + return message; +} + +void QOptions::print() const +{ + qDebug("%s", help().toUtf8().constData()); +} diff --git a/Common/QOptions.h b/Common/QOptions.h new file mode 100644 index 00000000..7e02867e --- /dev/null +++ b/Common/QOptions.h @@ -0,0 +1,113 @@ +/****************************************************************************** + QOptions: make command line options easy. https://github.com/wang-bin/qoptions + Copyright (C) 2011-2015 Wang Bin + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +******************************************************************************/ + +#ifndef QOPTIONS_H +#define QOPTIONS_H + +#include +#include +#include + +#if defined(BUILD_QOPT_LIB) +# define QOPT_EXPORT Q_DECL_EXPORT +#elif defined(BUILD_QOPT_IMPORT) +# define QOPT_EXPORT Q_DECL_IMPORT //only for vc? +#else +# define QOPT_EXPORT +#endif + + +class QOPT_EXPORT QOption { +public: + // TODO: MultiToken -name value1 -name value2 ... + enum Type { + NoToken, SingleToken, MultiToken + }; + QOption(); + explicit QOption(const char* name, const QVariant& defaultValue, Type type, const QString& description); + explicit QOption(const char* name, Type type, const QString& description); + //explicit QOption(const char* name, const QVariant& value, Type type, const QString& description); + + QString shortName() const; + QString longName() const; + QString formatName() const; + QString description() const; + QVariant value() const; + void setValue(const QVariant& value); + bool isSet() const; + bool isValid() const; + + void setType(QOption::Type type); + QOption::Type type() const; + + QString help() const; + void print() const; + bool operator <(const QOption& o) const; +private: + /*! + * \brief setName + * short/long name format: + * "--long", "-short", "short" + * "long,short", "--long,short", "--long,-short", "long,-short" + * "short,--long", "-short,long", "-short,--long" + * \param name + */ + void setName(const QString& name); + + QOption::Type mType; + QString mShortName, mLongName, mDescription; + QVariant mDefaultValue; + QVariant mValue; +}; + + +class QOPT_EXPORT QOptions { +public: + //e.g. application information, copyright etc. + QOptions(); + //QOptions(const QOptions& o); + ~QOptions(); + //QOptions& operator=(const QOptions& o); + + /*! + * \brief parse + * \param argc + * \param argv + * \return false if invalid option found + */ + bool parse(int argc, const char*const* argv); + QOptions& add(const QString& group_description); + QOptions& addDescription(const QString& description); + + QOptions& operator ()(const char* name, const QString& description = QString()); + QOptions& operator ()(const char* name, QOption::Type type, const QString& description = QString()); + QOptions& operator ()(const char* name, const QVariant& defaultValue, const QString& description); + QOptions& operator ()(const char* name, const QVariant& defaultValue, QOption::Type type, const QString& description = QString()); + //QOptions& operator ()(const char* name, QVariant* value, QOption::Type type, const QString& description = QString()); + + QOption option(const QString& name) const; + QVariant value(const QString& name) const; + QVariant operator [](const QString& name) const; + + QString help() const; + void print() const; +private: + QString mDescription, mCurrentDescription; + QList mOptions; + QMap mOptionGroupMap; +}; + +#endif // QOPTIONS_H diff --git a/Common/QtQuick2ApplicationViewer.cpp b/Common/QtQuick2ApplicationViewer.cpp new file mode 100755 index 00000000..f2efdf3d --- /dev/null +++ b/Common/QtQuick2ApplicationViewer.cpp @@ -0,0 +1,104 @@ +/* + This file was generated by the Qt Quick 2 Application wizard of Qt Creator. + QtQuick2ApplicationViewer is a convenience class containing mobile device specific + code such as screen orientation handling. Also QML paths and debugging are + handled here. + It is recommended not to modify this file, since newer versions of Qt Creator + may offer an updated version of it. +*/ + +#include "QtQuick2ApplicationViewer.h" + +#include +#include +#include + +class QtQuick2ApplicationViewerPrivate +{ + QString mainQmlFile; + friend class QtQuick2ApplicationViewer; + static QString adjustPath(const QString &path); +}; + +QString QtQuick2ApplicationViewerPrivate::adjustPath(const QString &path) +{ + if (path.startsWith(QLatin1String("qrc:"))) + return path; +#if defined(Q_OS_IOS) + if (!QDir::isAbsolutePath(path)) + return QString::fromLatin1("%1/%2") + .arg(QCoreApplication::applicationDirPath(), path); +#elif defined(Q_OS_MAC) + if (!QDir::isAbsolutePath(path)) + return QString::fromLatin1("%1/../Resources/%2") + .arg(QCoreApplication::applicationDirPath(), path); +#elif defined(Q_OS_BLACKBERRY) + if (!QDir::isAbsolutePath(path)) + return QString::fromLatin1("app/native/%1").arg(path); +#elif !defined(Q_OS_ANDROID) + QString pathInInstallDir = + QString::fromLatin1("%1/../%2").arg(QCoreApplication::applicationDirPath(), path); + if (QFileInfo(pathInInstallDir).exists()) + return pathInInstallDir; + pathInInstallDir = + QString::fromLatin1("%1/%2").arg(QCoreApplication::applicationDirPath(), path); + if (QFileInfo(pathInInstallDir).exists()) + return pathInInstallDir; +#elif defined(Q_OS_ANDROID_NO_SDK) + return QLatin1String("/data/user/qt/") + path; +#endif + return path; +} + +QtQuick2ApplicationViewer::QtQuick2ApplicationViewer(QWindow *parent) + : QQuickView(parent) + , d(new QtQuick2ApplicationViewerPrivate()) +{ + connect(engine(), SIGNAL(quit()), SLOT(close())); + setResizeMode(QQuickView::SizeRootObjectToView); +} + +QtQuick2ApplicationViewer::~QtQuick2ApplicationViewer() +{ + delete d; +} + +void QtQuick2ApplicationViewer::setMainQmlFile(const QString &file) +{ + if (file.startsWith(QLatin1String("qrc:"))) { + d->mainQmlFile = file; + setSource(QUrl(d->mainQmlFile)); + return; + } + d->mainQmlFile = QtQuick2ApplicationViewerPrivate::adjustPath(file); +#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK) + QUrl qmlUrl(QUrl(QLatin1String("assets:/")+d->mainQmlFile)); +#else + QUrl qmlUrl(QUrl::fromLocalFile(d->mainQmlFile)); +#endif + if (d->mainQmlFile.startsWith(QLatin1String("qrc:/"))) { + qmlUrl = QUrl(d->mainQmlFile); + } + setSource(qmlUrl); +} + +void QtQuick2ApplicationViewer::addImportPath(const QString &path) +{ + engine()->addImportPath(QtQuick2ApplicationViewerPrivate::adjustPath(path)); +} + +void QtQuick2ApplicationViewer::showExpanded() +{ + if (QApplication::platformName() == QLatin1String("qnx") || + QApplication::platformName() == QLatin1String("eglfs")) { + showFullScreen(); + } else { + show(); + } + return; +#if defined(Q_OS_QNX) //|| defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_MAEMO) + showFullScreen(); +#else + show(); +#endif +} diff --git a/Common/QtQuick2ApplicationViewer.h b/Common/QtQuick2ApplicationViewer.h new file mode 100755 index 00000000..6eee3c89 --- /dev/null +++ b/Common/QtQuick2ApplicationViewer.h @@ -0,0 +1,32 @@ +/* + This file was generated by the Qt Quick 2 Application wizard of Qt Creator. + QtQuick2ApplicationViewer is a convenience class containing mobile device specific + code such as screen orientation handling. Also QML paths and debugging are + handled here. + It is recommended not to modify this file, since newer versions of Qt Creator + may offer an updated version of it. +*/ + +#ifndef QTQUICK2APPLICATIONVIEWER_H +#define QTQUICK2APPLICATIONVIEWER_H + +#include + +class QtQuick2ApplicationViewer : public QQuickView +{ + Q_OBJECT + +public: + explicit QtQuick2ApplicationViewer(QWindow *parent = 0); + virtual ~QtQuick2ApplicationViewer(); + + void setMainQmlFile(const QString &file); + void addImportPath(const QString &path); + + void showExpanded(); + +private: + class QtQuick2ApplicationViewerPrivate *d; +}; + +#endif // QTQUICK2APPLICATIONVIEWER_H diff --git a/Common/Viper.icns b/Common/Viper.icns new file mode 100644 index 00000000..eccc7bdb Binary files /dev/null and b/Common/Viper.icns differ diff --git a/Common/ViperBuffer.cpp b/Common/ViperBuffer.cpp new file mode 100644 index 00000000..27ec8b5e --- /dev/null +++ b/Common/ViperBuffer.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ViperBuffer.h" +ViperBuffer::ViperBuffer(QObject* parent) : + QIODevice(parent) +{ + readMax = 32768; + readBuffer = (uint8_t*)malloc(sizeof(uint8_t)*readMax); + pthread_mutex_init(&(this->monitorMutex), NULL); + qByteArrayVector.reserve(2); + qByteArrayVector.push_back(new QByteArray()); + qByteArrayVector.push_back(new QByteArray()); + indexReadBuffer = 0; + indexWriteBuffer = 0; +} + +ViperBuffer::~ViperBuffer() +{ + pthread_mutex_destroy(&(this->monitorMutex)); + free(readBuffer); +} + +bool ViperBuffer::isSequential() const +{ + return true; +} + +bool ViperBuffer::open(OpenMode mode) +{ + setOpenMode(mode); + return true; +} + +void ViperBuffer::close() +{ + qByteArrayVector.clear(); + setOpenMode(NotOpen); +} + +void ViperBuffer::clear() +{ + qByteArrayVector.at(0)->clear(); + qByteArrayVector.at(1)->clear(); + indexReadBuffer = 0; + indexWriteBuffer = 0; +} + +qint64 ViperBuffer::readData(char* data, qint64 maxSize) +{ + pthread_mutex_lock(&(this->monitorMutex)); + if ((maxSize = qMin(maxSize, qint64(qByteArrayVector.at(indexReadBuffer)->size()))) <= 0) + { + pthread_mutex_unlock(&(this->monitorMutex)); + return qint64(0); + } + memcpy(data, qByteArrayVector.at(indexReadBuffer)->constData(), maxSize); + qByteArrayVector.at(indexReadBuffer)->remove(0,maxSize); + pthread_mutex_unlock(&(this->monitorMutex)); + return maxSize; + +} + +qint64 ViperBuffer::writeData(libdash::framework::input::MediaObject* media) +{ + pthread_mutex_lock(&(this->monitorMutex)); + int ret = 0; + int total = 0; + ret = media->ReadInitSegment(readBuffer,readMax); + total += ret; + this->writeData((const char *)readBuffer, ret); + + ret = media->Read(readBuffer,readMax); + while(ret) + { + total += ret; + this->writeData((const char *)readBuffer, ret); + ret = media->Read(readBuffer,readMax); + } + pthread_mutex_unlock(&(this->monitorMutex)); + return total; +} + +qint64 ViperBuffer::writeData(const char* data, qint64 maxSize) +{ + qByteArrayVector.at(indexWriteBuffer)->append(data, maxSize); + return maxSize; +} + + +void ViperBuffer::writeToNextBuffer() +{ + + pthread_mutex_lock(&(this->monitorMutex)); + indexWriteBuffer = (indexWriteBuffer + 1 ) % 2; + pthread_mutex_unlock(&(this->monitorMutex)); + +} + +void ViperBuffer::readFromNextBuffer() +{ + pthread_mutex_lock(&(this->monitorMutex)); + indexReadBuffer = (indexReadBuffer + 1 ) % 2; + pthread_mutex_unlock(&(this->monitorMutex)); + +} + diff --git a/Common/ViperBuffer.h b/Common/ViperBuffer.h new file mode 100644 index 00000000..37060178 --- /dev/null +++ b/Common/ViperBuffer.h @@ -0,0 +1,51 @@ +#ifndef VIPERBUFFER_H +#define VIPERBUFFER_H + +#include +#include +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "../Input/MediaObject.h" + +class ViperBuffer : public QIODevice +{ + Q_OBJECT +public: + ViperBuffer(QObject* parent = 0); + ~ViperBuffer(); + bool open(OpenMode mode); + void close(); + bool isSequential() const; + qint64 readData(char* data, qint64 maxSize); + qint64 writeData(libdash::framework::input::MediaObject* segment); + QByteArray* buffer(); + void clear(); + void writeToNextBuffer(); + void readFromNextBuffer(); + +private: + std::vector qByteArrayVector; + unsigned int indexReadBuffer; + unsigned int indexWriteBuffer; + pthread_mutex_t monitorMutex; + int readMax; + uint8_t* readBuffer; + qint64 writeData(const char* data, qint64 maxSize); + Q_DISABLE_COPY(ViperBuffer) +}; +#endif // VIPERBUFFER_H diff --git a/Input/DASHManager.cpp b/Input/DASHManager.cpp new file mode 100644 index 00000000..a86263dc --- /dev/null +++ b/Input/DASHManager.cpp @@ -0,0 +1,161 @@ +/* + * DASHManager.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "DASHManager.h" + +using namespace libdash::framework::input; +using namespace libdash::framework::buffer; + +using namespace dash; +using namespace dash::network; +using namespace dash::mpd; + +DASHManager::DASHManager(viper::managers::StreamType type, uint32_t maxCapacity, IDASHManagerObserver* stream, IMPD* mpd, bool icnEnabled, double icnAlpha, bool nodecoding, float beta, float drop) : + readSegmentCount (0), + receiver (NULL), + multimediaStream (stream), + isRunning (false), + icn (icnEnabled), + icnAlpha (icnAlpha), + noDecoding (nodecoding), + beta (beta), + drop (drop) +{ + + this->buffer = new Buffer(maxCapacity,libdash::framework::buffer::VIDEO); + this->buffer->attachObserver(this); + + this->receiver = new DASHReceiver(mpd, this, this->buffer, maxCapacity, this->isICN(), this->icnAlpha, this->beta, this->drop); +} +DASHManager::~DASHManager() +{ + this->stop(); + delete this->receiver; + delete this->buffer; + + this->receiver = NULL; + this->buffer = NULL; +} + +bool DASHManager::isICN() +{ + return this->icn; +} + +void DASHManager::shouldAbort() +{ + Debug("DASH MANAGER: ABORT REQUEST\n"); + this->receiver->ShouldAbort(); +} + +bool DASHManager::start() +{ + this->receiver->SetAdaptationLogic(this->adaptationLogic); + if (!this->receiver->Start()) + return false; + + this->isRunning = true; + return true; +} + +void DASHManager::stop() +{ + if (!this->isRunning) + return; + + this->isRunning = false; + + this->receiver->Stop(); + this->buffer->clear(); +} + +uint32_t DASHManager::getPosition() +{ + return this->receiver->GetPosition(); +} + +void DASHManager::setLooping(bool looping) +{ + this->receiver->SetLooping(looping); +} + +void DASHManager::setPosition(uint32_t segmentNumber) +{ + this->receiver->SetPosition(segmentNumber); +} + +void DASHManager::setPositionInMsec(uint32_t milliSecs) +{ + this->receiver->SetPositionInMsecs(milliSecs); +} + +void DASHManager::setAdaptationLogic(libdash::framework::adaptation::IAdaptationLogic *_adaptationLogic) +{ + this->adaptationLogic = _adaptationLogic; +} + +void DASHManager::clear() +{ + this->buffer->clear(); +} + +void DASHManager::setRepresentation(IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + this->receiver->SetRepresentation(period, adaptationSet, representation); +} + +void DASHManager::enqueueRepresentation(IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + this->receiver->SetRepresentation(period, adaptationSet, representation); +} + +void DASHManager::onSegmentDownloaded() +{ + this->readSegmentCount++; +} + +void DASHManager::notifyStatistics(int segNum, uint32_t bitrate, int fps, uint32_t quality) +{ + this->multimediaStream->notifyStatistics(segNum, bitrate, fps, quality); +} + +void DASHManager::notifyQualityDownloading(uint32_t quality) +{ + this->multimediaStream->notifyQualityDownloading(quality); +} + +int DASHManager::getBufferLevel() +{ + int res = this->multimediaStream->getBufferLevel(); + return this->multimediaStream->getBufferLevel(); +} + +bool DASHManager::canPush() +{ + this->multimediaStream->canPush(); +} + +MediaObject* DASHManager::getSegment() +{ + return this->buffer->getFront(); +} + +void DASHManager::setTargetDownloadingTime(double target) +{ + this->receiver->SetTargetDownloadingTime(target); +} + +void DASHManager::onBufferStateChanged(BufferType type, uint32_t fillstateInPercent, int maxC) +{ + this->multimediaStream->onSegmentBufferStateChanged(fillstateInPercent, maxC); + if(this->adaptationLogic->isBufferBased()) + this->receiver->OnSegmentBufferStateChanged(fillstateInPercent, maxC); +} diff --git a/Input/DASHManager.h b/Input/DASHManager.h new file mode 100644 index 00000000..2b723426 --- /dev/null +++ b/Input/DASHManager.h @@ -0,0 +1,80 @@ +/* + * DASHManager.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_INPUT_DASHMANAGER_H_ +#define LIBDASH_FRAMEWORK_INPUT_DASHMANAGER_H_ + +#include "DASHReceiver.h" +#include "IDASHReceiverObserver.h" +#include "libdash.h" +#include "IMPD.h" +#include +#include "IDASHManagerObserver.h" +//#include "../Buffer/Segment.h" +#include "../Managers/IStreamObserver.h" +#include "../Buffer/IBufferObserver.h" + + +namespace libdash +{ +namespace framework +{ +namespace input +{ + +class DASHManager : public IDASHReceiverObserver, public IBufferObserver +{ +public: + DASHManager (viper::managers::StreamType type, uint32_t maxCapacity, IDASHManagerObserver *multimediaStream, dash::mpd::IMPD *mpd, bool icnEnabled, double icnAlpha, bool nodecoding, float beta, float drop); + virtual ~DASHManager (); + + bool start(); + void stop(); + uint32_t getPosition(); + void setPosition(uint32_t segmentNumber); // to implement + void setLooping(bool looping); + void setPositionInMsec(uint32_t millisec); + void clear(); + void setRepresentation(dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation); + void enqueueRepresentation(dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation); + + void onSegmentDownloaded(); + void notifyStatistics(int, uint32_t, int, uint32_t); + void notifyQualityDownloading (uint32_t); + bool canPush(); + int getBufferLevel(); + void setAdaptationLogic(libdash::framework::adaptation::IAdaptationLogic *_adaptationLogic); + bool isICN(); + void shouldAbort(); + + void setTargetDownloadingTime(double); + MediaObject* getSegment(); + void onBufferStateChanged(BufferType type, uint32_t fillstateInPercent, int maxC); + +private: + float beta; + float drop; + buffer::Buffer *buffer; + DASHReceiver *receiver; + uint32_t readSegmentCount; + IDASHManagerObserver *multimediaStream; + bool isRunning; + bool icn; + double icnAlpha; + bool noDecoding; + + libdash::framework::adaptation::IAdaptationLogic *adaptationLogic; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_INPUT_DASHMANAGER_H_ */ diff --git a/Input/DASHReceiver.cpp b/Input/DASHReceiver.cpp new file mode 100644 index 00000000..df9d019b --- /dev/null +++ b/Input/DASHReceiver.cpp @@ -0,0 +1,453 @@ +/* + * DASHReceiver.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "DASHReceiver.h" +#include + +using namespace libdash::framework::input; +using namespace libdash::framework::buffer; +using namespace libdash::framework::mpd; +using namespace dash::mpd; + +using duration_in_seconds = std::chrono::duration >; + +DASHReceiver::DASHReceiver (IMPD *mpd, IDASHReceiverObserver *obs, Buffer *buffer, uint32_t bufferSize, bool icnEnabled, double icnAlpha, float beta, float drop) : + mpd (mpd), + period (NULL), + adaptationSet (NULL), + representation (NULL), + adaptationSetStream (NULL), + representationStream (NULL), + segmentNumber (0), + observer (obs), + buffer (buffer), + bufferSize (bufferSize), + isBuffering (false), + withFeedBack (false), + icn (icnEnabled), + icnAlpha (icnAlpha), + previousQuality (0), + isPaused (false), + threadComplete (false), + isScheduledPaced (false), + targetDownload (0.0), + downloadingTime (0.0), + bufferLevelAtUpdate (0), + isBufferBased (false), + isLooping (false), + beta (beta), + drop (drop) +{ + readMax = 32768; + readBuffer = (uint8_t*)malloc(sizeof(uint8_t)*readMax); + this->period = this->mpd->GetPeriods().at(0); + this->adaptationSet = this->period->GetAdaptationSets().at(0); + this->representation = this->adaptationSet->GetRepresentation().at(0); + + this->adaptationSetStream = new AdaptationSetStream(mpd, period, adaptationSet); + this->representationStream = adaptationSetStream->getRepresentationStream(this->representation); + this->segmentOffset = CalculateSegmentOffset(); + + this->conn = NULL; + this->initConn = NULL; + readMax = 32768; + readBuffer = (uint8_t *)malloc(sizeof(uint8_t) * readMax); + + if(icn) + { + this->conn = new ICNConnectionConsumerApi(this->icnAlpha, this->beta, this->drop); + this->initConn = new ICNConnectionConsumerApi(this->icnAlpha, this->beta, this->drop); + } + InitializeCriticalSection(&this->monitorMutex); + InitializeCriticalSection(&this->monitorPausedMutex); + InitializeConditionVariable(&this->paused); +} +DASHReceiver::~DASHReceiver () +{ + free(readBuffer); + if(this->initConn) + delete(this->initConn); + if(this->conn) + delete(this->conn); + delete(this->adaptationSetStream); + DeleteCriticalSection(&this->monitorMutex); + DeleteCriticalSection(&this->monitorPausedMutex); + DeleteConditionVariable(&this->paused); +} + +void DASHReceiver::SetAdaptationLogic(adaptation::IAdaptationLogic *_adaptationLogic) +{ + this->adaptationLogic = _adaptationLogic; + this->isBufferBased = this->adaptationLogic->isBufferBased(); + this->withFeedBack = this->adaptationLogic->isRateBased(); +} +bool DASHReceiver::Start () +{ + if(this->isBuffering) + return false; + + this->isBuffering = true; + this->bufferingThread = createThreadPortable(DoBuffering, this); + + if(this->bufferingThread == NULL) + { + this->isBuffering = false; + return false; + } + + return true; +} +void DASHReceiver::Stop() +{ + if(!this->isBuffering) + return; + + this->isBuffering = false; + this->buffer->setEOS(true); + + if(this->bufferingThread != NULL) + { + JoinThread(this->bufferingThread); + destroyThreadPortable(this->bufferingThread); + } + this->period = this->mpd->GetPeriods().at(0); + this->adaptationSet = this->period->GetAdaptationSets().at(0); + this->representation = this->adaptationSet->GetRepresentation().at(0); +} + +MediaObject* DASHReceiver::GetNextSegment () +{ + ISegment *seg = NULL; + + EnterCriticalSection(&this->monitorPausedMutex); + while(this->isPaused) + SleepConditionVariableCS(&this->paused, &this->monitorPausedMutex, INFINITE); + + if(this->segmentNumber >= this->representationStream->getSize()) + { + qDebug("looping? : %s\n", this->isLooping ? "YES" : "NO"); + if(this->isLooping) + { + this->segmentNumber = 0; + } + else + { + LeaveCriticalSection(&this->monitorPausedMutex); + return NULL; + } + } + seg = this->representationStream->getMediaSegment(this->segmentNumber + this->segmentOffset); + + if (seg != NULL) + { + std::vector rep = this->adaptationSet->GetRepresentation(); + + this->NotifyQualityDownloading(this->representation->GetBandwidth()); + + MediaObject *media = new MediaObject(seg, this->representation,this->withFeedBack); + this->segmentNumber++; + LeaveCriticalSection(&this->monitorPausedMutex); + return media; + } + LeaveCriticalSection(&this->monitorPausedMutex); + return NULL; +} +MediaObject* DASHReceiver::GetSegment (uint32_t segNum) +{ + ISegment *seg = NULL; + + if(segNum >= this->representationStream->getSize()) + return NULL; + + seg = this->representationStream->getMediaSegment(segNum + segmentOffset); + + if (seg != NULL) + { + MediaObject *media = new MediaObject(seg, this->representation); + return media; + } + + return NULL; +} +MediaObject* DASHReceiver::GetInitSegment () +{ + ISegment *seg = NULL; + + seg = this->representationStream->getInitializationSegment(); + + if (seg != NULL) + { + MediaObject *media = new MediaObject(seg, this->representation); + return media; + } + + return NULL; +} +MediaObject* DASHReceiver::FindInitSegment (dash::mpd::IRepresentation *representation) +{ + if (!this->InitSegmentExists(representation)) + return NULL; + + return this->initSegments[representation]; +} +uint32_t DASHReceiver::GetPosition () +{ + return this->segmentNumber; +} +void DASHReceiver::SetLooping (bool looping) +{ + this->isLooping = looping; +} +void DASHReceiver::SetPosition (uint32_t segmentNumber) +{ + this->segmentNumber = segmentNumber; +} +void DASHReceiver::SetPositionInMsecs (uint32_t milliSecs) +{ + this->positionInMsecs = milliSecs; +} + +void DASHReceiver::NotifyQualityDownloading (uint32_t quality) +{ + this->observer->notifyQualityDownloading(quality); +} + +void DASHReceiver::SetRepresentation (IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + EnterCriticalSection(&this->monitorMutex); + + bool periodChanged = false; + + if (this->representation == representation) + { + LeaveCriticalSection(&this->monitorMutex); + return; + } + + this->representation = representation; + + if (this->adaptationSet != adaptationSet) + { + this->adaptationSet = adaptationSet; + + if (this->period != period) + { + this->period = period; + periodChanged = true; + } + + delete this->adaptationSetStream; + this->adaptationSetStream = NULL; + + this->adaptationSetStream = new AdaptationSetStream(this->mpd, this->period, this->adaptationSet); + } + + this->representationStream = this->adaptationSetStream->getRepresentationStream(this->representation); + this->DownloadInitSegment(this->representation); + + if (periodChanged) + { + this->segmentNumber = 0; + this->CalculateSegmentOffset(); + } + LeaveCriticalSection(&this->monitorMutex); +} + +libdash::framework::adaptation::IAdaptationLogic* DASHReceiver::GetAdaptationLogic () +{ + return this->adaptationLogic; +} +dash::mpd::IRepresentation* DASHReceiver::GetRepresentation () +{ + return this->representation; +} +uint32_t DASHReceiver::CalculateSegmentOffset () +{ + if (mpd->GetType() == "static") + return 0; + + uint32_t firstSegNum = this->representationStream->getFirstSegmentNumber(); + uint32_t currSegNum = this->representationStream->getCurrentSegmentNumber(); + uint32_t startSegNum = currSegNum - 2*bufferSize; + + return (startSegNum > firstSegNum) ? startSegNum : firstSegNum; +} +void DASHReceiver::NotifySegmentDownloaded () +{ + this->observer->onSegmentDownloaded(); +} + +void DASHReceiver::NotifyBitrateChange(dash::mpd::IRepresentation *representation) +{ + if(this->representation != representation) + { + this->representation = representation; + this->SetRepresentation(this->period,this->adaptationSet,this->representation); + } +} +void DASHReceiver::DownloadInitSegment (IRepresentation* rep) +{ + if (this->InitSegmentExists(rep)) + return; + + MediaObject *initSeg = NULL; + initSeg = this->GetInitSegment(); + + if (initSeg) + { + initSeg->StartDownload(this->initConn); + this->initSegments[rep] = initSeg; + initSeg->WaitFinished(); + } +} +bool DASHReceiver::InitSegmentExists (IRepresentation* rep) +{ + if (this->initSegments.find(rep) != this->initSegments.end()) + return true; + + return false; +} + +void DASHReceiver::Notifybps (uint64_t bps) +{ + if(this->adaptationLogic) + { + if(this->withFeedBack) + { + this->adaptationLogic->bitrateUpdate(bps, this->segmentNumber); + } + } +} +void DASHReceiver::NotifyDLTime (double time) +{ + if(this->adaptationLogic) + { + if(this->withFeedBack) + { + this->adaptationLogic->dLTimeUpdate(time); + } + } +} + +void DASHReceiver::NotifyCheckedAdaptationLogic() +{ + this->adaptationLogic->checkedByDASHReceiver(); +} +//Is only called when this->adaptationLogic->IsBufferBased +void DASHReceiver::OnSegmentBufferStateChanged(uint32_t fillstateInPercent, int maxC) +{ + this->adaptationLogic->bufferUpdate(this->observer->getBufferLevel(), maxC); + this->bufferLevelAtUpdate = this->observer->getBufferLevel(); +} +void DASHReceiver::OnEOS(bool value) +{ + this->adaptationLogic->onEOS(value); +} + +bool DASHReceiver::PushBack(MediaObject *mediaObject) +{ + MediaObject *init = this->FindInitSegment(mediaObject->GetRepresentation()); + mediaObject->AddInitSegment(init); + //TODO the read should be in a function + + //Grab the infos for the analytics: bitrate, fps + dash::mpd::IRepresentation* datRep = mediaObject->GetRepresentation(); + uint32_t bitrate = 0; + int fps = 0; + uint32_t quality = 0; + bitrate = datRep->GetBandwidth(); + quality = datRep->GetHeight(); + fps = this->bufferLevelAtUpdate; + this->observer->notifyStatistics((int)this->segmentNumber - 1, bitrate, fps, quality); + + return(this->buffer->pushBack(mediaObject)); +} + +/* Thread that does the buffering of segments */ +void* DASHReceiver::DoBuffering (void *receiver) +{ + DASHReceiver *dashReceiver = (DASHReceiver *) receiver; + + dashReceiver->DownloadInitSegment(dashReceiver->GetRepresentation()); + + MediaObject *media = dashReceiver->GetNextSegment(); + dashReceiver->NotifyCheckedAdaptationLogic(); + media->SetDASHReceiver(dashReceiver); + std::chrono::time_point m_start_time = std::chrono::system_clock::now(); + while(media != NULL && dashReceiver->isBuffering) + { + //this is the case in PANDA + if(dashReceiver->isScheduledPaced) + { + double delay = std::chrono::duration_cast(std::chrono::system_clock::now() - m_start_time).count(); + Debug("delay: %f, target: %f\n", delay, dashReceiver->targetDownload); + if(delay < dashReceiver->targetDownload) + { + sleep(dashReceiver->targetDownload - delay); + } + } + m_start_time = std::chrono::system_clock::now(); + media->StartDownload(dashReceiver->conn); + + media->WaitFinished(); + bool canPush = dashReceiver->CanPush(); + if (canPush && !dashReceiver->PushBack(media)) + { + if(media) + { + delete(media); + } + media = NULL; + dashReceiver->threadComplete = true; + return NULL; + } + + dashReceiver->NotifySegmentDownloaded(); + media = dashReceiver->GetNextSegment(); + dashReceiver->NotifyCheckedAdaptationLogic(); + if(media) + media->SetDASHReceiver(dashReceiver); + } + + dashReceiver->buffer->setEOS(true); + dashReceiver->threadComplete = true; + return NULL; +} + +//can Push video to buffer in the renderer +bool DASHReceiver::CanPush () +{ + return this->observer->canPush(); +} +void DASHReceiver::ShouldAbort () +{ + Debug("DASH RECEIVER SEGMENT --\n"); + this->segmentNumber--; + Debug("DASH RECEIVER ABORT REQUEST\n"); +} + +void DASHReceiver::SetTargetDownloadingTime (double target) +{ + this->isScheduledPaced = true; + this->targetDownload = target; +} + +void DASHReceiver::SetBeta (float beta) +{ + this->beta = beta; +} + +void DASHReceiver::SetDrop (float drop) +{ + this->drop = drop; +} + + diff --git a/Input/DASHReceiver.h b/Input/DASHReceiver.h new file mode 100644 index 00000000..a2893b4d --- /dev/null +++ b/Input/DASHReceiver.h @@ -0,0 +1,129 @@ +/* + * DASHReceiver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_INPUT_DASHRECEIVER_H_ +#define LIBDASH_FRAMEWORK_INPUT_DASHRECEIVER_H_ + +#include "libdash.h" +#include "IMPD.h" + +#include "../Input/MediaObject.h" +#include "IDASHReceiverObserver.h" +#include "../MPD/AdaptationSetStream.h" +#include "../MPD/IRepresentationStream.h" +#include "../Portable/MultiThreading.h" +#include "../Buffer/Buffer.h" +#include +#include "ICNConnectionConsumerApi.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class IAdaptationLogic; +} +namespace buffer +{ +class MediaObjectBuffer; +template +class Buffer; +} +namespace input +{ +class MediaObject; +class DASHReceiver +{ +public: + DASHReceiver(dash::mpd::IMPD *mpd, IDASHReceiverObserver *obs, buffer::Buffer *buffer, uint32_t bufferSize, bool icnEnabled, double icnAlpha, float beta, float drop); + virtual ~DASHReceiver(); + + bool Start(); + void Stop(); + input::MediaObject* GetNextSegment(); + input::MediaObject* GetSegment(uint32_t segmentNumber); + input::MediaObject* GetInitSegment(); + input::MediaObject* FindInitSegment(dash::mpd::IRepresentation *representation); + uint32_t GetPosition(); + void SetPosition(uint32_t segmentNumber); + void SetLooping(bool isLoopinp); + void SetPositionInMsecs(uint32_t milliSecs); + dash::mpd::IRepresentation* GetRepresentation(); + void SetRepresentation(dash::mpd::IPeriod *period, + dash::mpd::IAdaptationSet *adaptationSet, + dash::mpd::IRepresentation *representation); + void SetAdaptationLogic(adaptation::IAdaptationLogic *_adaptationLogic); + libdash::framework::adaptation::IAdaptationLogic* GetAdaptationLogic(); + void NotifyQualityDownloading(uint32_t quality); + void Notifybps(uint64_t bps); + void NotifyDLTime(double time); + void NotifyBitrateChange(dash::mpd::IRepresentation *representation); + void OnSegmentBufferStateChanged(uint32_t fillstateInPercent, int maxC); + bool IsICN(); + void ShouldAbort(); + void OnEOS(bool value); + void SetTargetDownloadingTime(double); + void NotifyCheckedAdaptationLogic(); + bool threadComplete; + bool PushBack(MediaObject* media); + bool CanPush(); + void SetBeta(float beta); + void SetDrop(float drop); + +private: + float beta; + float drop; + bool withFeedBack; + bool isBufferBased; + std::map initSegments; + libdash::framework::buffer::Buffer *buffer; + IDASHReceiverObserver *observer; + dash::mpd::IMPD *mpd; + dash::mpd::IPeriod *period; + dash::mpd::IAdaptationSet *adaptationSet; + dash::mpd::IRepresentation *representation; + mpd::AdaptationSetStream *adaptationSetStream; + mpd::IRepresentationStream *representationStream; + uint32_t segmentNumber; + uint32_t positionInMsecs; + uint32_t segmentOffset; + uint32_t bufferSize; + mutable CRITICAL_SECTION monitorMutex; + mutable CRITICAL_SECTION monitorPausedMutex; + mutable CONDITION_VARIABLE paused; + bool isPaused; + bool isScheduledPaced; + bool isLooping; + double targetDownload; + double downloadingTime; + adaptation::IAdaptationLogic *adaptationLogic; + IICNConnection *conn; + IICNConnection *initConn; + THREAD_HANDLE bufferingThread; + bool isBuffering; + bool icn; + double icnAlpha; + int previousQuality; + int bufferLevelAtUpdate; + int readMax; + uint8_t *readBuffer; + uint32_t CalculateSegmentOffset(); + void NotifySegmentDownloaded(); + void DownloadInitSegment(dash::mpd::IRepresentation* rep); + bool InitSegmentExists(dash::mpd::IRepresentation* rep); + static void* DoBuffering(void *receiver); +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_INPUT_DASHRECEIVER_H_ */ diff --git a/Input/ICNConnectionConsumerApi.cpp b/Input/ICNConnectionConsumerApi.cpp new file mode 100644 index 00000000..9de82908 --- /dev/null +++ b/Input/ICNConnectionConsumerApi.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ICNICPDOWNLOAD + +#include "ICNConnectionConsumerApi.h" + +#define DEFAULT_LIFETIME 250 +#define RETRY_TIMEOUTS 5 + +using namespace dash; +using namespace dash::network; +using namespace dash::metrics; +using namespace icnet; + +using std::bind; +using std::placeholders::_1; +using std::placeholders::_2; +using std::placeholders::_3; + +using duration_in_seconds = std::chrono::duration >; + + +namespace libdash { +namespace framework { +namespace input { +ICNConnectionConsumerApi::ICNConnectionConsumerApi(double alpha, float beta, float drop) : + m_recv_name(ccnx::Name()), + m_first(1), + m_isFinished(false), + sizeDownloaded (0), + cumulativeBytesReceived(0), + icnAlpha(alpha), + beta(beta), + drop(drop) +{ + gamma = 1; + this->speed = 0.0; + this->dnltime = 0.0; + this->deezData = NULL; + this->deezDataSize = 0; + this->datSize = 0; + this->dataPos = 0; + InitializeConditionVariable (&this->contentRetrieved); + InitializeCriticalSection (&this->monitorMutex); + + this->myConsumer = new ConsumerSocket(ccnx::Name(), TransportProtocolAlgorithms::RAAQM); + this->myConsumer->setSocketOption(RaaqmTransportOptions::GAMMA_VALUE, (int)gamma); + + bool configFile = false; + //CHECK if we are not going to override the configuration file. (if !autotune) + if(FILE *fp = fopen("/usr/etc/consumer.conf", "r")) + { + fclose(fp); + configFile = true; + } + if(!configFile) + { + qDebug("beta %f, drop %f", this->beta, this->drop); + this->myConsumer->setSocketOption(RaaqmTransportOptions::BETA_VALUE, this->beta); + this->myConsumer->setSocketOption(RaaqmTransportOptions::DROP_FACTOR, this->drop); + } + this->myConsumer->setSocketOption(RateEstimationOptions::RATE_ESTIMATION_OBSERVER, this); + this->myConsumer->setSocketOption(ConsumerCallbacksOptions::CONTENT_RETRIEVED, (ConsumerContentCallback) bind(&ICNConnectionConsumerApi::processPayload, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + this->myConsumer->setSocketOption(ConsumerCallbacksOptions::CONTENT_OBJECT_TO_VERIFY, (ConsumerContentObjectVerificationCallback)bind(&ICNConnectionConsumerApi::onPacket, this, std::placeholders::_1, std::placeholders::_2)); +#ifdef NO_GUI + if(this->icnAlpha != 20) + this->icnRateBased = true; +#else + this->icnRateBased = true; +#endif + Debug("ICN class created\n"); + +} + +ICNConnectionConsumerApi::~ICNConnectionConsumerApi() { + delete this->myConsumer; + if(this->deezData) + { + free(this->deezData); + this->deezData = NULL; + } + DeleteConditionVariable (&this->contentRetrieved); + DeleteCriticalSection (&this->monitorMutex); +} + +void ICNConnectionConsumerApi::Init(IChunk *chunk) { + Debug("ICN Connection: STARTING\n"); + m_first = 1; + sizeDownloaded = 0; + m_name = "ccnx:/" + chunk->Host() + chunk->Path(); + m_isFinished = false; + + res = false; + dataPos = 0; + datSize = 0; + if(this->deezData) + { + memset(this->deezData, 0, this->deezDataSize); + } + + qDebug("ICN_Connection:\tINTIATED_to_name %s\n", m_name.c_str()); + qDebug("ICN_Connection:\tSTARTING DOWNLOAD %s\n", m_name.c_str()); +} + +void ICNConnectionConsumerApi::InitForMPD(const std::string& url) +{ + m_first = 1; + sizeDownloaded = 0; + + if(url.find("//") != std::string::npos) + { + int pos = url.find("//"); + char* myName = (char*)malloc(strlen(url.c_str()) - 1); + strncpy(myName, url.c_str(), pos + 1); + strncpy(myName + pos + 1, url.c_str() + pos + 2, strlen(url.c_str()) - pos - 2); + m_name = std::string(myName); + free(myName); + } + else + { + m_name = url; + } + m_isFinished = false; + + res = false; + dataPos = 0; + datSize = 0; + Debug("ICN_Connection:\tINTIATED_for_mpd %s\n", m_name.c_str()); +} + +int ICNConnectionConsumerApi::Read(uint8_t* data, size_t len, IChunk *chunk) +{ + return this->Read(data, len); +} + +int ICNConnectionConsumerApi::Read(uint8_t *data, size_t len) +{ + if(!res) + m_start_time = std::chrono::system_clock::now(); + + if(res) + { + if(this->dataPos == this->datSize) + { + this->dnltime = std::chrono::duration_cast(std::chrono::system_clock::now() - m_start_time).count(); + if(speed == 0 || !this->icnRateBased) + speed = (double) (sizeDownloaded * 8 / this->dnltime); + cumulativeBytesReceived += sizeDownloaded; + Debug("ICN_Connection:\tFINISHED DOWNLOADING %s Average_DL: %f size: %lu cumulative: %lu Throughput: %f\n", m_name.c_str(), speed, sizeDownloaded, cumulativeBytesReceived, (double) (sizeDownloaded * 8 / this->dnltime)); + return 0; + } + if((this->datSize - this->dataPos) > (int)len) + { + memcpy(data, this->deezData + this->dataPos, len); + this->dataPos += len; + sizeDownloaded += len; + return len; + } + else + { + assert(this->datSize - this->dataPos > 0); + memcpy(data, this->deezData + this->dataPos, this->datSize - this->dataPos); + int temp = this->datSize - this->dataPos; + this->dataPos += this->datSize - this->dataPos; + sizeDownloaded += temp; + return temp; + } + } + + Debug("will consume: %s\n", m_name.c_str()); + this->myConsumer->consume(m_name); + EnterCriticalSection(&this->monitorMutex); + + while(this->m_isFinished == false) + SleepConditionVariableCS(&this->contentRetrieved, &this->monitorMutex, INFINITE); + + assert(this->datSize != 0); + this->res = true; + LeaveCriticalSection(&this->monitorMutex); + if(this->datSize > (int)len) + { + memcpy(data, this->deezData, len); + this->dataPos += len; + sizeDownloaded += len; + return len; + } + else + { + memcpy(data, this->deezData, this->datSize); + this->dataPos += this->datSize; + sizeDownloaded += this->datSize; + return this->datSize; + } +} + +int ICNConnectionConsumerApi::Peek(uint8_t *data, size_t len, IChunk *chunk) { + return -1; +} + +bool ICNConnectionConsumerApi::onPacket(ConsumerSocket& c, const ContentObject& data) +{ + return true; +} + +void ICNConnectionConsumerApi::processPayload(ConsumerSocket& c, const uint8_t* buffer, size_t bufferSize) +{ + EnterCriticalSection(&this->monitorMutex); + if(this->deezData == NULL) + { + this->deezData = (char *)malloc(bufferSize*sizeof(uint8_t)); + this->deezDataSize = bufferSize; + } + else + { + if(bufferSize > this->deezDataSize) + { + this->deezData = (char *)realloc(this->deezData, bufferSize * (sizeof(uint8_t))); + this->deezDataSize = bufferSize; + } + } + memcpy(this->deezData, buffer, bufferSize*sizeof(uint8_t)); + this->m_isFinished = true; + this->datSize = (int) bufferSize; + WakeAllConditionVariable(&this->contentRetrieved); + LeaveCriticalSection(&this->monitorMutex); +} + +double ICNConnectionConsumerApi::GetAverageDownloadingSpeed() +{ + Debug("ICNConnection: DL speed is %f\n", this->speed); + return this->speed; +} + +double ICNConnectionConsumerApi::GetDownloadingTime() +{ + Debug("ICNConnection: DL time is %f\n", this->dnltime); + return this->dnltime; +} + +const std::vector &ICNConnectionConsumerApi::GetTCPConnectionList() const { + return tcpConnections; +} + +const std::vector &ICNConnectionConsumerApi::GetHTTPTransactionList() const { + return httpTransactions; +} + +void ICNConnectionConsumerApi::notifyStats(double winSize) +{ + this->speed = (winSize); // * 1000000 * 1400 * 8; + Debug("ICNConnection:\tNotificationICPDL\t%f\t%f\n", winSize, speed); +} + +void ICNConnectionConsumerApi::SetBeta(float beta) +{ + this->beta = beta; +} + + +void ICNConnectionConsumerApi::SetDrop(float drop) +{ + this->drop = drop; +} + +} /* namespace input */ +} /* namespace framework */ +} /* namespace libdash */ + + +#endif //NDEF ICNICPDOWNLOAD diff --git a/Input/ICNConnectionConsumerApi.h b/Input/ICNConnectionConsumerApi.h new file mode 100644 index 00000000..a0343443 --- /dev/null +++ b/Input/ICNConnectionConsumerApi.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef QTPLAYER_INPUT_ICNCONNECTIONCONSUMERAPI_H_ +#define QTPLAYER_INPUT_ICNCONNECTIONCONSUMERAPI_H_ + +#include +#include "../Portable/Networking.h" +#include "IICNConnection.h" +#include "../debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../Portable/MultiThreading.h" +#include + +//logging purpose +#include +#include + + +namespace libdash { +namespace framework { +namespace input { + +class ICNConnectionConsumerApi : public IICNConnection, public icnet::IcnObserver { +public: + ICNConnectionConsumerApi(double alpha, float beta, float drop); + virtual ~ICNConnectionConsumerApi(); + + virtual void Init(dash::network::IChunk *chunk); + + void InitForMPD(const std::string& url); + + virtual int Read(uint8_t *data, size_t len, dash::network::IChunk *chunk); + + int Read(uint8_t *data, size_t len); + + virtual int Peek(uint8_t *data, size_t len, dash::network::IChunk *chunk); + + virtual double GetAverageDownloadingSpeed(); + + virtual double GetDownloadingTime(); + + void processPayload(icnet::ConsumerSocket& , const uint8_t*, size_t); + + bool onPacket(icnet::ConsumerSocket& , const icnet::ContentObject&); + + const std::vector &GetTCPConnectionList() const; + + const std::vector &GetHTTPTransactionList() const; + virtual void SetBeta(float beta); + virtual void SetDrop(float drop); + virtual void notifyStats(double throughput); + +private: + float beta; + float drop; + uint64_t i_chunksize; + int i_lifetime; + int i_missed_co; + /**< number of content objects we missed in ICNBlock */ + + std::string m_name; + icnet::ccnx::Name m_recv_name; + icnet::ccnx::Portal m_portal; + int m_first; + bool m_isFinished; + uint64_t m_nextSeg; + + double icnAlpha; + bool icnRateBased; + + bool allow_stale; + int sysTimeout; + unsigned InitialMaxwindow; + unsigned int timer; + double drop_factor; + double p_min; + unsigned int gamma; + unsigned int samples; + unsigned int nchunks; // XXX chunks=-1 means: download the whole file! + bool output; + bool report_path; + icnet::ConsumerSocket* myConsumer; + bool res; + std::vector mdata; + char* deezData; + int deezDataSize; + int datSize; + int dataPos; + int firstChunk; + double speed; // in bps + double dnltime; //in seconds + uint64_t sizeDownloaded; + std::chrono::time_point m_start_time; + + std::vector tcpConnections; + std::vector httpTransactions; + uint64_t cumulativeBytesReceived; + + mutable CRITICAL_SECTION monitorMutex; + mutable CONDITION_VARIABLE contentRetrieved; +}; + +} /* namespace input */ +} /* namespace framework */ +} /* namespace libdash */ + +#endif /* QTPLAYER_INPUT_ICNCONNECTIONCONSUMERAPI_H_ */ diff --git a/Input/IDASHManagerObserver.h b/Input/IDASHManagerObserver.h new file mode 100644 index 00000000..c5df8fe9 --- /dev/null +++ b/Input/IDASHManagerObserver.h @@ -0,0 +1,41 @@ +/* + * IDASHManagerObserver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_INPUT_IDASHMANAGEROBSERVER_H_ +#define LIBDASH_FRAMEWORK_INPUT_IDASHMANAGEROBSERVER_H_ + +#include +#include "../Buffer/Buffer.h" + +namespace libdash +{ +namespace framework +{ +namespace input +{ +class IDASHManagerObserver +{ +public: + virtual ~IDASHManagerObserver() {} + + virtual void addFrame(QImage *frame) = 0; + virtual void setEOS(bool value)= 0; + + virtual void onSegmentBufferStateChanged(uint32_t fillstateInPercent, int maxC) = 0; + virtual void notifyStatistics(int segNum, uint32_t bitrate, int fps, uint32_t quality) = 0; + virtual void notifyQualityDownloading (uint32_t quality) = 0; + virtual bool canPush() = 0; + virtual int getBufferLevel() = 0; +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_INPUT_IDASHMANAGEROBSERVER_H_ */ diff --git a/Input/IDASHReceiverObserver.h b/Input/IDASHReceiverObserver.h new file mode 100644 index 00000000..0a334aa2 --- /dev/null +++ b/Input/IDASHReceiverObserver.h @@ -0,0 +1,35 @@ +/* + * IDASHReceiverObserver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_INPUT_IDASHRECEIVEROBSERVER_H_ +#define LIBDASH_FRAMEWORK_INPUT_IDASHRECEIVEROBSERVER_H_ + +namespace libdash +{ +namespace framework +{ +namespace input +{ +class IDASHReceiverObserver +{ +public: + virtual ~IDASHReceiverObserver() {} + + virtual void onSegmentDownloaded() = 0; + virtual void notifyStatistics(int segNum, uint32_t bitrate, int fps, uint32_t quality) = 0; + virtual void notifyQualityDownloading(uint32_t quality) = 0; + virtual bool canPush() = 0; + virtual int getBufferLevel() = 0; +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_INPUT_IDASHRECEIVEROBSERVER_H_ */ diff --git a/Input/IDataReceiver.h b/Input/IDataReceiver.h new file mode 100644 index 00000000..15d7e0f6 --- /dev/null +++ b/Input/IDataReceiver.h @@ -0,0 +1,34 @@ +/* + * IDataReceiver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_INPUT_IDATARECEIVER_H_ +#define LIBDASH_FRAMEWORK_INPUT_IDATARECEIVER_H_ + +#include + +namespace libdash +{ +namespace framework +{ +namespace input +{ +class IDataReceiver +{ +public: + virtual ~IDataReceiver() {} + + virtual int read(uint8_t *buf, int buf_size ) = 0; + virtual bool isAudio() = 0; +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_INPUT_IDATARECEIVER_H_ */ diff --git a/Input/IICNConnection.h b/Input/IICNConnection.h new file mode 100644 index 00000000..260f0450 --- /dev/null +++ b/Input/IICNConnection.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef QTPLAYER_INPUT_IICNCONNECTION_H_ +#define QTPLAYER_INPUT_IICNCONNECTION_H_ + +#include "IConnection.h" +#include "IChunk.h" + + +namespace libdash { +namespace framework { + +namespace input { + +class IICNConnection : public dash::network::IConnection { + +public: + virtual ~IICNConnection(){}; + + virtual int Read(uint8_t* data, size_t len) = 0; + virtual void Init(dash::network::IChunk *chunk) = 0; + virtual void InitForMPD(const std::string&) = 0; + virtual void SetBeta(float beta) = 0; + virtual void SetDrop(float drop) = 0; +}; +} +} +} + + +#endif // QTPLAYER_INPUT_IICNCONNECTION_H_ diff --git a/Input/MediaObject.cpp b/Input/MediaObject.cpp new file mode 100644 index 00000000..ae6b9f6b --- /dev/null +++ b/Input/MediaObject.cpp @@ -0,0 +1,175 @@ +/* + * MediaObject.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "MediaObject.h" +#include +#include + +using namespace libdash::framework::input; +using namespace dash::mpd; +using namespace dash::network; +using namespace dash::metrics; + +MediaObject::MediaObject(ISegment *segment, IRepresentation *rep, bool withFeedBack) : + segment (segment), + rep (rep), + withFeedBack (withFeedBack) +{ + this->initSeg = NULL; + InitializeConditionVariable (&this->stateChanged); + InitializeCriticalSection (&this->stateLock); +} + +MediaObject::~MediaObject() +{ + if(this->state == IN_PROGRESS) + { + this->segment->AbortDownload(); + this->OnDownloadStateChanged(ABORTED); + } + this->segment->DetachDownloadObserver(this); + this->WaitFinished(); + + DeleteConditionVariable (&this->stateChanged); + DeleteCriticalSection (&this->stateLock); + delete this->segment; + this->segment = NULL; +} + +void MediaObject::SetFeedBack(bool flag) +{ + this->withFeedBack = flag; +} + +void MediaObject::AddInitSegment(MediaObject* initSeg) +{ + this->initSeg = initSeg; +} + +int MediaObject::ReadInitSegment(uint8_t* data, size_t len) +{ + if(this->initSeg) + return this->initSeg->Peek(data,len); + else + return 0; +} + +bool MediaObject::StartDownload() +{ + this->segment->AttachDownloadObserver(this); + return this->segment->StartDownload(); +} + +bool MediaObject::StartDownload(IICNConnection* conn) +{ + if(conn == NULL) + return this->StartDownload(); + + conn->Init(this->segment); + + this->segment->AttachDownloadObserver(this); + return this->segment->StartDownload(conn); +} + +const char* MediaObject::GetPath() +{ + return ((IChunk*)this->segment)->Path().c_str(); +} + +void MediaObject::AbortDownload() +{ + this->segment->AbortDownload(); + this->OnDownloadStateChanged(ABORTED); +} + +void MediaObject::WaitFinished() +{ + EnterCriticalSection(&this->stateLock); + + while(this->state != COMPLETED && this->state != ABORTED){ + SleepConditionVariableCS(&this->stateChanged, &this->stateLock, INFINITE); + } + + LeaveCriticalSection(&this->stateLock); + if(this->state != ABORTED) + { + if(this->withFeedBack && this->dashReceiver) + { + this->dashReceiver->Notifybps(this->bps); + this->dashReceiver->NotifyDLTime(this->dnltime); + } + } +} + +int MediaObject::Read(uint8_t *data, size_t len) +{ + return this->segment->Read(data, len); +} + +int MediaObject::Peek(uint8_t *data, size_t len) +{ + return this->segment->Peek(data, len); +} + +int MediaObject::Peek(uint8_t *data, size_t len, size_t offset) +{ + return this->segment->Peek(data, len, offset); +} + +IRepresentation* MediaObject::GetRepresentation() +{ + return this->rep; +} + +ISegment* MediaObject::GetSegment() +{ + return this->segment; +} + +void MediaObject::OnDownloadTimeChanged(double dnltime) +{ + this->dnltime = dnltime; +} + +void MediaObject::OnDownloadStateChanged(DownloadState state) +{ + EnterCriticalSection(&this->stateLock); + + this->state = state; + + WakeAllConditionVariable(&this->stateChanged); + LeaveCriticalSection(&this->stateLock); +} + +void MediaObject::OnDownloadRateChanged(uint64_t bitsPerSecond) +{ + this->bps = bitsPerSecond; +} + +void MediaObject::SetDASHReceiver(input::DASHReceiver *_dashReceiver) +{ + this->dashReceiver = _dashReceiver; +} + +void MediaObject::SetAdaptationLogic(adaptation::IAdaptationLogic *_adaptationLogic) +{ + this->adaptationLogic = _adaptationLogic; +} + +const std::vector& MediaObject::GetTCPConnectionList() const +{ + return this->segment->GetTCPConnectionList(); +} + +const std::vector& MediaObject::GetHTTPTransactionList () const +{ + return this->segment->GetHTTPTransactionList(); +} diff --git a/Input/MediaObject.h b/Input/MediaObject.h new file mode 100644 index 00000000..9d5c7826 --- /dev/null +++ b/Input/MediaObject.h @@ -0,0 +1,78 @@ +/* + * MediaObject.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_INPUT_MEDIAOBJECT_H_ +#define LIBDASH_FRAMEWORK_INPUT_MEDIAOBJECT_H_ + +#include "../Adaptation/IAdaptationLogic.h" +#include "IMPD.h" +#include "IDownloadObserver.h" +#include "IDASHMetrics.h" +#include "../Portable/MultiThreading.h" +#include "ICNConnectionConsumerApi.h" + +namespace libdash +{ +namespace framework +{ +namespace adaptation +{ +class IAdaptationLogic; +} +namespace input +{ +class DASHReceiver; +class MediaObject : public dash::network::IDownloadObserver, public dash::metrics::IDASHMetrics +{ +public: + MediaObject(dash::mpd::ISegment *segment, dash::mpd::IRepresentation *rep, bool withFeedBack = false); + virtual ~MediaObject(); + + bool StartDownload(); + bool StartDownload(IICNConnection* conn); + void AbortDownload(); + void WaitFinished(); + int Read(uint8_t *data, size_t len); + int Peek(uint8_t *data, size_t len); + int Peek(uint8_t *data, size_t len, size_t offset); + dash::mpd::IRepresentation* GetRepresentation(); + dash::mpd::ISegment* GetSegment(); + const char* GetPath(); + void SetFeedBack(bool flag); + virtual void OnDownloadStateChanged(dash::network::DownloadState state); + virtual void OnDownloadRateChanged(uint64_t bytesDownloaded); //Here the bytesDownloaded is in fact the bitrate bps + virtual void OnDownloadTimeChanged(double dnltime); + + void AddInitSegment(MediaObject* initSeg); + int ReadInitSegment(uint8_t* data, size_t len); + const std::vector& GetTCPConnectionList() const; + const std::vector& GetHTTPTransactionList() const; + void SetAdaptationLogic(framework::adaptation::IAdaptationLogic *_adaptationLogic); + void SetDASHReceiver(input::DASHReceiver *_dashReceiver); + +private: + dash::mpd::ISegment *segment; + MediaObject *initSeg; + dash::mpd::IRepresentation *rep; + dash::network::DownloadState state; + uint64_t bps; + bool withFeedBack; + double dnltime; + input::DASHReceiver *dashReceiver; + adaptation::IAdaptationLogic *adaptationLogic; + mutable CRITICAL_SECTION stateLock; + mutable CONDITION_VARIABLE stateChanged; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_INPUT_MEDIAOBJECT_H_ */ diff --git a/MPD/AbstractRepresentationStream.cpp b/MPD/AbstractRepresentationStream.cpp new file mode 100644 index 00000000..449c6aa8 --- /dev/null +++ b/MPD/AbstractRepresentationStream.cpp @@ -0,0 +1,84 @@ +/* + * AbstractRepresentationStream.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AbstractRepresentationStream.h" + +using namespace libdash::framework::mpd; +using namespace dash::mpd; + +AbstractRepresentationStream::AbstractRepresentationStream(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) : + mpd (mpd), + period (period), + adaptationSet (adaptationSet), + representation (representation) +{ +} +AbstractRepresentationStream::~AbstractRepresentationStream() +{ +} + +void AbstractRepresentationStream::setBaseUrls(const std::vector baseurls) +{ + this->baseUrls.clear(); + + for (size_t i = 0; i < baseurls.size(); i++) + this->baseUrls.push_back(baseurls.at(i)); +} + +uint32_t AbstractRepresentationStream::getSize() +{ + return UINT32_MAX - 1; +} + +uint32_t AbstractRepresentationStream::getFirstSegmentNumber() +{ + if (this->mpd->GetType() == "dynamic") + { + uint32_t currTime = TimeResolver::getCurrentTimeInSec(); + uint32_t availStT = TimeResolver::getUTCDateTimeInSec(this->mpd->GetAvailabilityStarttime()); + uint32_t duration = this->getAverageSegmentDuration(); + uint32_t timeshift = TimeResolver::getDurationInSec(this->mpd->GetTimeShiftBufferDepth()); + return (currTime - duration - availStT - timeshift ) / duration; + } + return 0; +} + +uint32_t AbstractRepresentationStream::getCurrentSegmentNumber() +{ + if (this->mpd->GetType() == "dynamic") + { + uint32_t currTime = TimeResolver::getCurrentTimeInSec(); + uint32_t duration = this->getAverageSegmentDuration(); + uint32_t availStT = TimeResolver::getUTCDateTimeInSec(this->mpd->GetAvailabilityStarttime()); + + return (currTime - duration - availStT) / duration; + } + return 0; +} + +uint32_t AbstractRepresentationStream::getLastSegmentNumber () +{ + if (this->mpd->GetType() == "dynamic") + { + uint32_t currTime = TimeResolver::getCurrentTimeInSec(); + uint32_t duration = this->getAverageSegmentDuration(); + uint32_t availStT = TimeResolver::getUTCDateTimeInSec(this->mpd->GetAvailabilityStarttime()); + uint32_t checkTime = mpd->GetFetchTime() + + TimeResolver::getDurationInSec(this->mpd->GetMinimumUpdatePeriod()); + return ( ((checkTime > currTime) ? currTime : checkTime) - duration - availStT) / duration; + } + return 0; +} + +uint32_t AbstractRepresentationStream::getAverageSegmentDuration() +{ + return 1; +} diff --git a/MPD/AbstractRepresentationStream.h b/MPD/AbstractRepresentationStream.h new file mode 100644 index 00000000..e0f354b4 --- /dev/null +++ b/MPD/AbstractRepresentationStream.h @@ -0,0 +1,61 @@ +/* + * AbstracRepresenationStream.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_MPD_ABSTRACTREPRESENTATIONSTREAM_H_ +#define LIBDASH_FRAMEWORK_MPD_ABSTRACTREPRESENTATIONSTREAM_H_ + +#include "IRepresentationStream.h" +#include "IBaseUrl.h" +#include "IRepresentation.h" +#include "IAdaptationSet.h" +#include "IMPD.h" +#include "IPeriod.h" +#include "BaseUrlResolver.h" +#include "TimeResolver.h" + +namespace libdash +{ +namespace framework +{ +namespace mpd +{ +class AbstractRepresentationStream : public IRepresentationStream +{ +public: + AbstractRepresentationStream(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, + dash::mpd::IRepresentation *representation); + virtual ~AbstractRepresentationStream(); + + virtual dash::mpd::ISegment* getInitializationSegment() = 0; + virtual dash::mpd::ISegment* getIndexSegment(size_t segmentNumber) = 0; + virtual dash::mpd::ISegment* getMediaSegment(size_t segmentNumber) = 0; + virtual dash::mpd::ISegment* getBitstreamSwitchingSegment() = 0; + virtual RepresentationStreamType getStreamType() = 0; + + virtual uint32_t getSize(); + virtual uint32_t getFirstSegmentNumber(); + virtual uint32_t getCurrentSegmentNumber(); + virtual uint32_t getLastSegmentNumber(); + virtual uint32_t getAverageSegmentDuration(); + +protected: + virtual void setBaseUrls(const std::vector baseurls); + + std::vector baseUrls; + dash::mpd::IMPD *mpd; + dash::mpd::IPeriod *period; + dash::mpd::IAdaptationSet *adaptationSet; + dash::mpd::IRepresentation *representation; +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_ADAPTATION_ABSTRACTADAPTATIONLOGIC_H_ */ diff --git a/MPD/AdaptationSetHelper.cpp b/MPD/AdaptationSetHelper.cpp new file mode 100644 index 00000000..b5deefc7 --- /dev/null +++ b/MPD/AdaptationSetHelper.cpp @@ -0,0 +1,58 @@ +/* + * AdaptationSetHelper.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AdaptationSetHelper.h" + +using namespace libdash::framework::mpd; +using namespace dash::mpd; + +std::vector AdaptationSetHelper::getAudioAdaptationSets (dash::mpd::IPeriod *period) +{ + std::vector audioAdaptationSets; + std::vector adaptationSets = period->GetAdaptationSets(); + for (size_t i = 0; i < adaptationSets.size(); i++) + if (AdaptationSetHelper::isAudioAdaptationSet(adaptationSets.at(i))) + audioAdaptationSets.push_back(adaptationSets.at(i)); + + return audioAdaptationSets; +} + +std::vector AdaptationSetHelper::getVideoAdaptationSets (dash::mpd::IPeriod *period) +{ + std::vector videoAdaptationSets; + std::vector adaptationSets = period->GetAdaptationSets(); + for (size_t i = 0; i < adaptationSets.size(); i++) { + if (AdaptationSetHelper::isVideoAdaptationSet(adaptationSets.at(i))) { + videoAdaptationSets.push_back(adaptationSets.at(i)); + } + } + return videoAdaptationSets; +} + +bool AdaptationSetHelper::isAudioAdaptationSet(dash::mpd::IAdaptationSet *adaptationSet) +{ + return isContainedInMimeType(adaptationSet, "audio"); +} + +bool AdaptationSetHelper::isVideoAdaptationSet(dash::mpd::IAdaptationSet *adaptationSet) +{ + return isContainedInMimeType(adaptationSet, "video"); +} + +bool AdaptationSetHelper::isContainedInMimeType (dash::mpd::IAdaptationSet *adaptationSet, std::string value) +{ + for (size_t i = 0; i < adaptationSet->GetRepresentation().size(); i++) + if (adaptationSet->GetRepresentation().at(i)->GetMimeType() != "") + if (adaptationSet->GetRepresentation().at(i)->GetMimeType().find(value) != std::string::npos) + return true; + + return false; +} diff --git a/MPD/AdaptationSetHelper.h b/MPD/AdaptationSetHelper.h new file mode 100644 index 00000000..683d0f0c --- /dev/null +++ b/MPD/AdaptationSetHelper.h @@ -0,0 +1,36 @@ +/* + * AdaptationSetHelper.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_MPD_ADAPTATIONSETHELPER_H_ +#define LIBDASH_FRAMEWORK_MPD_ADAPTATIONSETHELPER_H_ + +#include "IMPD.h" +#include + +namespace libdash +{ +namespace framework +{ +namespace mpd +{ +class AdaptationSetHelper +{ +public: + static std::vector getAudioAdaptationSets(dash::mpd::IPeriod *period); + static std::vector getVideoAdaptationSets(dash::mpd::IPeriod *period); + static bool isAudioAdaptationSet(dash::mpd::IAdaptationSet *adaptationSet); + static bool isVideoAdaptationSet(dash::mpd::IAdaptationSet *adaptationSet); + static bool isContainedInMimeType(dash::mpd::IAdaptationSet *adaptationSet, std::string value); +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_MPD_ADAPTATIONSETHELPER_H_ */ diff --git a/MPD/AdaptationSetStream.cpp b/MPD/AdaptationSetStream.cpp new file mode 100644 index 00000000..62bc1912 --- /dev/null +++ b/MPD/AdaptationSetStream.cpp @@ -0,0 +1,82 @@ +/* + * AdaptationSetStream.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AdaptationSetStream.h" + +using namespace libdash::framework::mpd; +using namespace dash::mpd; + +AdaptationSetStream::AdaptationSetStream(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet) : + mpd (mpd), + period (period), + adaptationSet (adaptationSet) +{ + initialize(); +} + +AdaptationSetStream::~AdaptationSetStream() +{ + std::map::iterator iter; + for (iter = representations.begin(); iter != representations.end(); ++iter) + { + delete(iter->second); + } +} + +IRepresentationStream* AdaptationSetStream::getRepresentationStream(IRepresentation *representation) +{ + return this->representations.find(representation)->second; +} + +RepresentationStreamType AdaptationSetStream::determineRepresentationStreamType (IRepresentation *representation) +{ + /* check on Representation Level */ + if (representation->GetSegmentList()) + return libdash::framework::mpd::SegmentList; + + if (representation->GetSegmentTemplate()) + return libdash::framework::mpd::SegmentTemplate; + + if (representation->GetSegmentBase() || representation->GetBaseURLs().size() > 0) + return libdash::framework::mpd::SingleMediaSegment; + + /* check on AdaptationSet Level */ + if (this->adaptationSet->GetSegmentList()) + return libdash::framework::mpd::SegmentList; + + if (this->adaptationSet->GetSegmentTemplate()) + return libdash::framework::mpd::SegmentTemplate; + + if (this->adaptationSet->GetSegmentBase()) + return libdash::framework::mpd::SingleMediaSegment; + + /* check on Period Level */ + if (this->period->GetSegmentList()) + return libdash::framework::mpd::SegmentList; + + if (this->period->GetSegmentTemplate()) + return libdash::framework::mpd::SegmentTemplate; + + if (this->period->GetSegmentBase()) + return libdash::framework::mpd::SingleMediaSegment; + + return libdash::framework::mpd::UNDEFINED; +} + +void AdaptationSetStream::initialize() +{ + for (size_t i = 0; i < adaptationSet->GetRepresentation().size(); i++) + { + IRepresentation *representation = adaptationSet->GetRepresentation().at(i); + RepresentationStreamType type = determineRepresentationStreamType(representation); + representations[representation] = RepresentationStreamFactory::create(type, mpd, period, adaptationSet, representation); + } +} diff --git a/MPD/AdaptationSetStream.h b/MPD/AdaptationSetStream.h new file mode 100644 index 00000000..3ad9788f --- /dev/null +++ b/MPD/AdaptationSetStream.h @@ -0,0 +1,50 @@ +/* + * AdaptationSetStream.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_MPD_ADAPTATIONSETSTREAM_H_ +#define LIBDASH_FRAMEWORK_MPD_ADAPTATIONSETSTREAM_H_ + +#include "IRepresentationStream.h" +#include "IRepresentation.h" +#include "IMPD.h" +#include "IPeriod.h" +#include "IAdaptationSet.h" +#include "RepresentationStreamFactory.h" +#include "BaseUrlResolver.h" + +namespace libdash +{ +namespace framework +{ +namespace mpd +{ +class AdaptationSetStream +{ +public: + AdaptationSetStream(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet); + virtual ~AdaptationSetStream(); + + IRepresentationStream* getRepresentationStream(dash::mpd::IRepresentation *representation); + +private: + RepresentationStreamType determineRepresentationStreamType(dash::mpd::IRepresentation *representation); + void initialize(); + + std::map representations; + dash::mpd::IMPD *mpd; + dash::mpd::IPeriod *period; + dash::mpd::IAdaptationSet *adaptationSet; + +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_MPD_ADAPTATIONSETSTREAM_H_ */ diff --git a/MPD/BaseUrlResolver.cpp b/MPD/BaseUrlResolver.cpp new file mode 100644 index 00000000..54b586cd --- /dev/null +++ b/MPD/BaseUrlResolver.cpp @@ -0,0 +1,64 @@ +/* + * BaseUrlResolver.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "BaseUrlResolver.h" + +using namespace dash::mpd; +using namespace libdash::framework::mpd; + +std::vector BaseUrlResolver::resolveBaseUrl(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, + size_t mpdBaseUrl, size_t periodBaseUrl, size_t adaptationSetBaseUrl) +{ + std::vector urls; + + if (mpd->GetBaseUrls().size() > 0) + { + if (mpd->GetBaseUrls().size() > mpdBaseUrl) + urls.push_back(mpd->GetBaseUrls().at(mpdBaseUrl)); + else + urls.push_back(mpd->GetBaseUrls().at(0)); + } + if (period->GetBaseURLs().size() > 0) + { + if (period->GetBaseURLs().size() > periodBaseUrl) + urls.push_back(period->GetBaseURLs().at(periodBaseUrl)); + else + urls.push_back(period->GetBaseURLs().at(0)); + } + if (adaptationSet->GetBaseURLs().size() > 0) + { + if (adaptationSet->GetBaseURLs().size() > adaptationSetBaseUrl) + urls.push_back(adaptationSet->GetBaseURLs().at(adaptationSetBaseUrl)); + else + urls.push_back(adaptationSet->GetBaseURLs().at(0)); + } + + if (urls.size() > 0) + { + if (urls.at(0)->GetUrl().substr(0,7) != "http://" && urls.at(0)->GetUrl().substr(0,8) != "https://") + { + urls.push_back(mpd->GetMPDPathBaseUrl()); + size_t lastPos = urls.size() - 1; + IBaseUrl *absoluteUrl = urls.at(lastPos); + for (size_t i = lastPos; i > 0; i--) + { + urls[i] = urls[i-1]; + } + urls[0] = absoluteUrl; + } + } + else + { + urls.push_back(mpd->GetMPDPathBaseUrl()); + } + + return urls; +} diff --git a/MPD/BaseUrlResolver.h b/MPD/BaseUrlResolver.h new file mode 100644 index 00000000..6956a00e --- /dev/null +++ b/MPD/BaseUrlResolver.h @@ -0,0 +1,33 @@ +/* + * BaseUrlResolver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_MPD_BASEURLRESOLVER_H_ +#define LIBDASH_FRAMEWORK_MPD_BASEURLRESOLVER_H_ + +#include "IMPD.h" + +namespace libdash +{ +namespace framework +{ +namespace mpd +{ +class BaseUrlResolver +{ +public: + static std::vector resolveBaseUrl(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, + size_t mpdBaseUrl, size_t periodBaseUrl, size_t adaptationSetBaseUrl); +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_MPD_BASEURLRESOLVER_H_ */ diff --git a/MPD/IRepresentationStream.h b/MPD/IRepresentationStream.h new file mode 100644 index 00000000..370c84ed --- /dev/null +++ b/MPD/IRepresentationStream.h @@ -0,0 +1,50 @@ +/* + * IRepresentationStream.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_MPD_IREPRESENTATIONSTREAM_H_ +#define LIBDASH_FRAMEWORK_MPD_IREPRESENTATIONSTREAM_H_ + +#include "ISegment.h" + +namespace libdash +{ +namespace framework +{ +namespace mpd +{ +enum RepresentationStreamType +{ + SingleMediaSegment, + SegmentList, + SegmentTemplate, + UNDEFINED +}; + +class IRepresentationStream +{ +public: + virtual ~IRepresentationStream() {} + + virtual dash::mpd::ISegment* getInitializationSegment() = 0; + virtual dash::mpd::ISegment* getIndexSegment(size_t segmentNumber) = 0; + virtual dash::mpd::ISegment* getMediaSegment(size_t segmentNumber) = 0; + virtual dash::mpd::ISegment* getBitstreamSwitchingSegment() = 0; + virtual RepresentationStreamType getStreamType() = 0; + virtual uint32_t getSize() = 0; + virtual uint32_t getFirstSegmentNumber() = 0; + virtual uint32_t getCurrentSegmentNumber() = 0; + virtual uint32_t getLastSegmentNumber() = 0; + virtual uint32_t getAverageSegmentDuration() = 0; +}; +} +} +} +#endif /* LIBDASH_FRAMEWORK_MPD_IREPRESENTATIONSTREAM_H_ */ diff --git a/MPD/RepresentationStreamFactory.cpp b/MPD/RepresentationStreamFactory.cpp new file mode 100644 index 00000000..b84bf558 --- /dev/null +++ b/MPD/RepresentationStreamFactory.cpp @@ -0,0 +1,27 @@ +/* + * RepresentationStreamFactory.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "RepresentationStreamFactory.h" + +using namespace libdash::framework::mpd; +using namespace dash::mpd; + +IRepresentationStream* RepresentationStreamFactory::create(RepresentationStreamType type, IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + switch(type) + { + case SingleMediaSegment: return new SingleMediaSegmentStream(mpd, period, adaptationSet, representation); + case SegmentList: return new SegmentListStream (mpd, period, adaptationSet, representation); + case SegmentTemplate: return new SegmentTemplateStream (mpd, period, adaptationSet, representation); + + default: return NULL; + } +} diff --git a/MPD/RepresentationStreamFactory.h b/MPD/RepresentationStreamFactory.h new file mode 100644 index 00000000..22c70b96 --- /dev/null +++ b/MPD/RepresentationStreamFactory.h @@ -0,0 +1,38 @@ +/* + * RepresentationStreamFactory.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_MPD_RERPRESENTATIONSTREAMFACTORY_H_ +#define LIBDASH_FRAMEWORK_MPD_RERPRESENTATIONSTREAMFACTORY_H_ + +#include "IRepresentationStream.h" +#include "SingleMediaSegmentStream.h" +#include "SegmentListStream.h" +#include "SegmentTemplateStream.h" +#include "IRepresentation.h" + +namespace libdash +{ +namespace framework +{ +namespace mpd +{ +class RepresentationStreamFactory +{ +public: + static IRepresentationStream* create(libdash::framework::mpd::RepresentationStreamType type, dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, + dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation); + +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_MPD_RERPRESENTATIONSTREAMFACTORY_H_ */ diff --git a/MPD/SegmentListStream.cpp b/MPD/SegmentListStream.cpp new file mode 100644 index 00000000..3a475e71 --- /dev/null +++ b/MPD/SegmentListStream.cpp @@ -0,0 +1,88 @@ +/* + * SegmentListStream.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "SegmentListStream.h" + +using namespace dash::mpd; +using namespace libdash::framework::mpd; + +SegmentListStream::SegmentListStream(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) : + AbstractRepresentationStream (mpd, period, adaptationSet, representation) +{ + this->baseUrls = BaseUrlResolver::resolveBaseUrl(mpd, period, adaptationSet, 0, 0, 0); + this->segmentList = findSegmentList(); +} + +SegmentListStream::~SegmentListStream() +{ +} + +ISegment* SegmentListStream::getInitializationSegment() +{ + if (this->segmentList->GetInitialization()) + return this->segmentList->GetInitialization()->ToSegment(this->baseUrls); + + return NULL; +} + +ISegment* SegmentListStream::getIndexSegment(size_t segmentNumber) +{ + if (this->segmentList->GetSegmentURLs().size() > segmentNumber) + return this->segmentList->GetSegmentURLs().at(segmentNumber)->ToIndexSegment(this->baseUrls); + + return NULL; +} + +ISegment* SegmentListStream::getMediaSegment(size_t segmentNumber) +{ + if (this->segmentList->GetSegmentURLs().size() > segmentNumber) + return this->segmentList->GetSegmentURLs().at(segmentNumber)->ToMediaSegment(this->baseUrls); + + return NULL; +} + +ISegment* SegmentListStream::getBitstreamSwitchingSegment () +{ + if (this->segmentList->GetBitstreamSwitching()) + return this->segmentList->GetBitstreamSwitching()->ToSegment(baseUrls); + + return NULL; +} + +RepresentationStreamType SegmentListStream::getStreamType() +{ + return SegmentList; +} + +uint32_t SegmentListStream::getSize() +{ + return this->segmentList->GetSegmentURLs().size(); +} + +ISegmentList* SegmentListStream::findSegmentList() +{ + if (this->representation->GetSegmentList()) + return this->representation->GetSegmentList(); + + if (this->adaptationSet->GetSegmentList()) + return this->adaptationSet->GetSegmentList(); + + if (this->period->GetSegmentList()) + return this->period->GetSegmentList(); + + return NULL; +} + +uint32_t SegmentListStream::getAverageSegmentDuration() +{ + /* TODO calculate average segment durations for SegmentTimeline */ + return this->segmentList->GetDuration(); +} diff --git a/MPD/SegmentListStream.h b/MPD/SegmentListStream.h new file mode 100644 index 00000000..323852f0 --- /dev/null +++ b/MPD/SegmentListStream.h @@ -0,0 +1,49 @@ +/* + * SegmentListStream.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_MPD_SEGMENTLISTSTREAM_H_ +#define LIBDASH_FRAMEWORK_MPD_SEGMENTLISTSTREAM_H_ + +#include "IMPD.h" +#include "AbstractRepresentationStream.h" +#include "ISegment.h" +#include "ISegmentList.h" + +namespace libdash +{ +namespace framework +{ +namespace mpd +{ +class SegmentListStream: public AbstractRepresentationStream +{ +public: + SegmentListStream(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation); + virtual ~SegmentListStream(); + + virtual dash::mpd::ISegment* getInitializationSegment(); + virtual dash::mpd::ISegment* getIndexSegment(size_t segmentNumber); + virtual dash::mpd::ISegment* getMediaSegment(size_t segmentNumber); + virtual dash::mpd::ISegment* getBitstreamSwitchingSegment(); + virtual RepresentationStreamType getStreamType(); + virtual uint32_t getSize(); + virtual uint32_t getAverageSegmentDuration(); + +private: + dash::mpd::ISegmentList* findSegmentList(); + dash::mpd::ISegmentList *segmentList; + +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_MPD_SEGMENTLISTSTREAM_H_ */ diff --git a/MPD/SegmentTemplateStream.cpp b/MPD/SegmentTemplateStream.cpp new file mode 100644 index 00000000..f0f475e6 --- /dev/null +++ b/MPD/SegmentTemplateStream.cpp @@ -0,0 +1,158 @@ +/* + * SegmentTemplateStream.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "SegmentTemplateStream.h" + +using namespace dash::mpd; +using namespace libdash::framework::mpd; + +SegmentTemplateStream::SegmentTemplateStream(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) : + AbstractRepresentationStream (mpd, period, adaptationSet, representation) +{ + this->baseUrls = BaseUrlResolver::resolveBaseUrl(mpd, period, adaptationSet, 0, 0, 0); + this->segmentTemplate = findSegmentTemplate(); + calculateSegmentStartTimes(); +} +SegmentTemplateStream::~SegmentTemplateStream() +{ +} + +ISegment* SegmentTemplateStream::getInitializationSegment() +{ + if (this->segmentTemplate->GetInitialization()) + return this->segmentTemplate->GetInitialization()->ToSegment(baseUrls); + + return this->segmentTemplate->ToInitializationSegment(baseUrls, representation->GetId(), representation->GetBandwidth()); +} + +ISegment* SegmentTemplateStream::getIndexSegment(size_t segmentNumber) +{ + /* time-based template */ + if (this->segmentTemplate->GetSegmentTimeline()) + { + if (this->segmentStartTimes.size() > segmentNumber) + return this->segmentTemplate->GetIndexSegmentFromTime(baseUrls, representation->GetId(), representation->GetBandwidth(), this->segmentStartTimes.at(segmentNumber)); + + return NULL; + } + + /* number-based template */ + return this->segmentTemplate->GetIndexSegmentFromNumber(baseUrls, representation->GetId(), representation->GetBandwidth(), + this->segmentTemplate->GetStartNumber() + segmentNumber); +} + +ISegment* SegmentTemplateStream::getMediaSegment(size_t segmentNumber) +{ + /* time-based template */ + if (this->segmentTemplate->GetSegmentTimeline()) + { + if (this->segmentStartTimes.size() > segmentNumber) + return this->segmentTemplate->GetMediaSegmentFromTime(baseUrls, representation->GetId(), representation->GetBandwidth(), this->segmentStartTimes.at(segmentNumber)); + + return NULL; + } + + /* number-based template */ + return this->segmentTemplate->GetMediaSegmentFromNumber(baseUrls, representation->GetId(), representation->GetBandwidth(), + this->segmentTemplate->GetStartNumber() + segmentNumber); + +} + +ISegment* SegmentTemplateStream::getBitstreamSwitchingSegment () +{ + if (this->segmentTemplate->GetBitstreamSwitching()) + return this->segmentTemplate->GetBitstreamSwitching()->ToSegment(baseUrls); + return this->segmentTemplate->ToBitstreamSwitchingSegment(baseUrls, representation->GetId(), representation->GetBandwidth()); +} + +RepresentationStreamType SegmentTemplateStream::getStreamType() +{ + return SegmentList; +} + +uint32_t SegmentTemplateStream::getSize() +{ + if (!this->segmentStartTimes.empty()) + return this->segmentStartTimes.size(); + + uint32_t numberOfSegments = 0; + double mediaPresentationDuration = 0; + + if (this->mpd->GetType() == "static") + { + mediaPresentationDuration = TimeResolver::getDurationInSec(this->mpd->GetMediaPresentationDuration()); + numberOfSegments = (uint32_t) ceil(mediaPresentationDuration / (this->segmentTemplate->GetDuration() / this->segmentTemplate->GetTimescale())); + } + else + { + numberOfSegments = UINT32_MAX - 1; + } + return numberOfSegments; +} + +uint32_t SegmentTemplateStream::getAverageSegmentDuration() +{ + /* TODO calculate average segment durations for SegmentTimeline */ + return this->segmentTemplate->GetDuration(); +} + +ISegmentTemplate* SegmentTemplateStream::findSegmentTemplate() +{ + if (this->representation->GetSegmentTemplate()) + return this->representation->GetSegmentTemplate(); + + if (this->adaptationSet->GetSegmentTemplate()) + return this->adaptationSet->GetSegmentTemplate(); + + if (this->period->GetSegmentTemplate()) + return this->period->GetSegmentTemplate(); + + return NULL; +} + +void SegmentTemplateStream::calculateSegmentStartTimes() +{ + if (!this->segmentTemplate->GetSegmentTimeline()) + return; + + size_t numOfTimelines = 0; + uint32_t segStartTime = 0; + uint32_t segDuration = 0; + size_t repeatCount = 0; + + numOfTimelines = this->segmentTemplate->GetSegmentTimeline()->GetTimelines().size(); + + for (size_t i = 0; i < numOfTimelines; i++) + { + repeatCount = this->segmentTemplate->GetSegmentTimeline()->GetTimelines().at(i)->GetRepeatCount(); + segStartTime = this->segmentTemplate->GetSegmentTimeline()->GetTimelines().at(i)->GetStartTime(); + segDuration = this->segmentTemplate->GetSegmentTimeline()->GetTimelines().at(i)->GetDuration(); + + if (repeatCount > 0) + { + for (size_t j = 0; j <= repeatCount; j++) + { + if (segStartTime > 0) + { + this->segmentStartTimes.push_back(segStartTime + segDuration * j); + } + else + { + /* TODO: implement if SegmentTemplate.SegmentTimeline.S@t is not specified */ + } + } + } + else + { + this->segmentStartTimes.push_back(segStartTime); + } + } +} diff --git a/MPD/SegmentTemplateStream.h b/MPD/SegmentTemplateStream.h new file mode 100644 index 00000000..ea85ab1a --- /dev/null +++ b/MPD/SegmentTemplateStream.h @@ -0,0 +1,53 @@ +/* + * SegmentTemplateStream.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_MPD_SEGMENTTEMPLATESTREAM_H_ +#define LIBDASH_FRAMEWORK_MPD_SEGMENTTEMPLATESTREAM_H_ + +#include + +#include "IMPD.h" +#include "AbstractRepresentationStream.h" +#include "ISegment.h" +#include "ISegmentTemplate.h" + +namespace libdash +{ +namespace framework +{ +namespace mpd +{ +class SegmentTemplateStream: public AbstractRepresentationStream +{ +public: + SegmentTemplateStream(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation); + virtual ~SegmentTemplateStream(); + + virtual dash::mpd::ISegment* getInitializationSegment(); + virtual dash::mpd::ISegment* getIndexSegment(size_t segmentNumber); + virtual dash::mpd::ISegment* getMediaSegment(size_t segmentNumber); + virtual dash::mpd::ISegment* getBitstreamSwitchingSegment(); + virtual RepresentationStreamType getStreamType(); + virtual uint32_t getSize(); + virtual uint32_t getAverageSegmentDuration(); + +private: + dash::mpd::ISegmentTemplate* findSegmentTemplate(); + void calculateSegmentStartTimes(); + + dash::mpd::ISegmentTemplate *segmentTemplate; + std::vector segmentStartTimes; +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_MPD_SEGMENTTEMPLATESTREAM_H_ */ diff --git a/MPD/SingleMediaSegmentStream.cpp b/MPD/SingleMediaSegmentStream.cpp new file mode 100644 index 00000000..ff4540e2 --- /dev/null +++ b/MPD/SingleMediaSegmentStream.cpp @@ -0,0 +1,76 @@ +/* + * SingleMediaSegmentStream.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "SingleMediaSegmentStream.h" + +using namespace dash::mpd; +using namespace libdash::framework::mpd; + +SingleMediaSegmentStream::SingleMediaSegmentStream(IMPD *mpd, IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) : + AbstractRepresentationStream (mpd, period, adaptationSet, representation) +{ + this->baseUrls = BaseUrlResolver::resolveBaseUrl(mpd, period, adaptationSet, 0, 0, 0); +} +SingleMediaSegmentStream::~SingleMediaSegmentStream() +{ +} +ISegment* SingleMediaSegmentStream::getInitializationSegment() +{ + if (this->representation->GetSegmentBase()) + { + /* TODO: check whether @sourceURL and/or @range is available in SegmentBase.Initialization, see Table 13 */ + + if (this->representation->GetSegmentBase()->GetInitialization()) + return this->representation->GetSegmentBase()->GetInitialization()->ToSegment(baseUrls); + } + + return NULL; +} +ISegment* SingleMediaSegmentStream::getIndexSegment(size_t segmentNumber) +{ + /* segmentNumber is not used in this case */ + if (this->representation->GetSegmentBase()) + { + if (this->representation->GetSegmentBase()->GetRepresentationIndex()) + return this->representation->GetSegmentBase()->GetRepresentationIndex()->ToSegment(baseUrls); + } + + return NULL; +} +ISegment* SingleMediaSegmentStream::getMediaSegment(size_t segmentNumber) +{ + /* segmentNumber equals the desired BaseUrl */ + if (this->representation->GetBaseURLs().size() > segmentNumber) + return this->representation->GetBaseURLs().at(segmentNumber)->ToMediaSegment(baseUrls); + + return this->representation->GetBaseURLs().at(0)->ToMediaSegment(baseUrls); +} +ISegment* SingleMediaSegmentStream::getBitstreamSwitchingSegment() +{ + /* not possible */ + return NULL; +} +RepresentationStreamType SingleMediaSegmentStream::getStreamType() +{ + return SingleMediaSegment; +} +uint32_t SingleMediaSegmentStream::getFirstSegmentNumber() +{ + return 0; +} +uint32_t SingleMediaSegmentStream::getCurrentSegmentNumber() +{ + return 0; +} +uint32_t SingleMediaSegmentStream::getLastSegmentNumber() +{ + return 0; +} diff --git a/MPD/SingleMediaSegmentStream.h b/MPD/SingleMediaSegmentStream.h new file mode 100644 index 00000000..afc919ee --- /dev/null +++ b/MPD/SingleMediaSegmentStream.h @@ -0,0 +1,46 @@ +/* + * SingleMediaSegmentStream.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_MPD_SINGLEMEDIASEGMENTSTREAM_H_ +#define LIBDASH_FRAMEWORK_MPD_SINGLEMEDIASEGMENTSTREAM_H_ + +#include "IMPD.h" +#include "AbstractRepresentationStream.h" +#include "ISegment.h" + +namespace libdash +{ +namespace framework +{ +namespace mpd +{ +class SingleMediaSegmentStream: public AbstractRepresentationStream +{ +public: + SingleMediaSegmentStream(dash::mpd::IMPD *mpd, dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation); + virtual ~SingleMediaSegmentStream(); + + virtual dash::mpd::ISegment* getInitializationSegment(); + virtual dash::mpd::ISegment* getIndexSegment(size_t segmentNumber); + virtual dash::mpd::ISegment* getMediaSegment(size_t segmentNumber); + virtual dash::mpd::ISegment* getBitstreamSwitchingSegment(); + virtual RepresentationStreamType getStreamType(); + + virtual uint32_t getFirstSegmentNumber(); + virtual uint32_t getCurrentSegmentNumber(); + virtual uint32_t getLastSegmentNumber(); + +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_MPD_SINGLEMEDIASEGMENTSTREAM_H_ */ diff --git a/MPD/TimeResolver.cpp b/MPD/TimeResolver.cpp new file mode 100644 index 00000000..8ce044ae --- /dev/null +++ b/MPD/TimeResolver.cpp @@ -0,0 +1,148 @@ +/* + * TimeResolver.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "TimeResolver.h" +#include "sstream" + +using namespace libdash::framework::mpd; + +bool TimeResolver::checkTimeInterval(std::string availabilityStartTime, std::string availabilityEndTime) +{ + struct tm* startTime = TimeResolver::resolveUTCDateTime(availabilityStartTime); + struct tm* currentTime = TimeResolver::getCurrentUTCTime(); + struct tm* endTime = TimeResolver::resolveUTCDateTime(availabilityEndTime); + + if (!startTime) + { + if (!endTime) + return true; + + if (difftime(mktime(endTime),mktime(currentTime)) > 0) + return true; + } + else + { + if (difftime(mktime(currentTime),mktime(startTime)) > 0) + { + if (!endTime) + return true; + + if (difftime(mktime(endTime),mktime(currentTime)) > 0) + return true; + } + } + + return false; +} + +uint32_t TimeResolver::getCurrentTimeInSec() +{ + return mktime(TimeResolver::getCurrentUTCTime()); +} +uint32_t TimeResolver::getUTCDateTimeInSec(const std::string& datetime) +{ + return mktime(TimeResolver::resolveUTCDateTime(datetime)); +} +double TimeResolver::getDurationInSec(const std::string& duration) +{ + /* no check for duration with yyyy,dd,mm */ + if (duration == "" || duration.substr(0, 2) != "PT") + return 0; + + size_t startPos = 2; + size_t endPos = std::string::npos; + uint32_t hours = 0; + uint32_t mins = 0; + double secs = 0; + + char designators[] = { 'H', 'M', 'S' }; + + endPos = duration.find(designators[0], startPos); + if (endPos != std::string::npos) + { + hours = strtol(duration.substr(startPos, endPos - startPos).c_str(), NULL, 10); + startPos = endPos + 1; + } + + endPos = duration.find(designators[1], startPos); + if (endPos != std::string::npos) + { + mins = strtol(duration.substr(startPos, endPos - startPos).c_str(), NULL, 10); + startPos = endPos + 1; + } + + endPos = duration.find(designators[2], startPos); + if (endPos != std::string::npos) + secs = strtod(duration.substr(startPos, endPos - startPos).c_str(), NULL); + + return hours*3600 + mins*60 + secs; +} + +struct tm* TimeResolver::resolveUTCDateTime(const std::string& dateTimeString) +{ + if (dateTimeString == "") + return NULL; + + time_t rawtime; + struct tm* timeInfo; + time ( &rawtime ); + timeInfo = gmtime ( &rawtime ); + + std::string timeString = dateTimeString.substr(); + + timeString = timeString.substr(0, timeString.size()-1); + + std::vector dateTime = splitToStr(timeString, 'T'); + std::vector dateChunks = splitToI(dateTime.at(0), '-'); + std::vector timeChunks = splitToI(dateTime.at(1), ':'); + + timeInfo->tm_year = dateChunks.at(0) - 1900; + timeInfo->tm_mon = dateChunks.at(1) - 1; + timeInfo->tm_mday = dateChunks.at(2); + + timeInfo->tm_hour = timeChunks.at(0); + timeInfo->tm_min = timeChunks.at(1); + timeInfo->tm_sec = timeChunks.at(2); + + return timeInfo; +} + +struct tm* TimeResolver::getCurrentUTCTime() +{ + time_t rawTime; + + time(&rawTime); + return gmtime(&rawTime); +} + +std::vector TimeResolver::splitToI(const std::string &s, char delim) +{ + std::stringstream ss(s); + std::string item; + std::vector integers; + + while(std::getline(ss, item, delim)) + integers.push_back((int)strtol(item.c_str(), NULL, 10)); + + return integers; +} + +std::vector TimeResolver::splitToStr(const std::string &s, char delim) +{ + std::stringstream ss(s); + std::string item; + std::vector strings; + + while(std::getline(ss, item, delim)) + strings.push_back(item); + + return strings; +} diff --git a/MPD/TimeResolver.h b/MPD/TimeResolver.h new file mode 100644 index 00000000..fe320e8d --- /dev/null +++ b/MPD/TimeResolver.h @@ -0,0 +1,43 @@ +/* + * TimeResolver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_MPD_TIMERESOLVER_H_ +#define LIBDASH_FRAMEWORK_MPD_TIMERESOLVER_H_ + +#include +#include "config.h" + +namespace libdash +{ +namespace framework +{ +namespace mpd +{ +class TimeResolver +{ +public: + static bool checkTimeInterval(std::string availabilityStartTime, std::string availabilityEndTime); + static uint32_t getCurrentTimeInSec(); + static uint32_t getUTCDateTimeInSec(const std::string& datetime); + static double getDurationInSec(const std::string& duration); + +private: + static struct tm* resolveUTCDateTime(const std::string& timeString); + static struct tm* getCurrentUTCTime(); + static std::vector splitToI(const std::string &s, char delim); + static std::vector splitToStr(const std::string &s, char delim); + +}; +} +} +} + +#endif /* LIBDASH_FRAMEWORK_MPD_TIMERESOLVER_H_ */ diff --git a/Managers/IMultimediaManagerBase.h b/Managers/IMultimediaManagerBase.h new file mode 100644 index 00000000..f08803ba --- /dev/null +++ b/Managers/IMultimediaManagerBase.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIPER_MANAGERS_IMULTIMEDIAMANAGERBASE_H_ +#define VIPER_MANAGERS_IMULTIMEDIAMANAGERBASE_H_ + +#include "IMPD.h" + +namespace viper +{ +namespace managers +{ +class IMultimediaManagerBase +{ +public: + virtual bool setVideoQuality(dash::mpd::IPeriod* period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation) = 0; + virtual bool setAudioQuality(dash::mpd::IPeriod* period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation) = 0; + virtual bool isStarted() = 0; + virtual bool isStopping() = 0; + virtual void shouldAbort(bool isVideo) = 0; + virtual void setTargetDownloadingTime(bool,double) = 0; + +}; +} +} + +#endif //VIPER_MANAGERS_IMULTIMEDIAMANAGERBASE_H_ diff --git a/Managers/IMultimediaManagerObserver.h b/Managers/IMultimediaManagerObserver.h new file mode 100644 index 00000000..eea0bfb1 --- /dev/null +++ b/Managers/IMultimediaManagerObserver.h @@ -0,0 +1,35 @@ +/* + * IMultimediaManagerObserver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef VIPER_MANAGERS_IMULTIMEDIAMANAGEROBSERVER_H_ +#define VIPER_MANAGERS_IMULTIMEDIAMANAGEROBSERVER_H_ + +#include + +namespace viper +{ +namespace managers +{ +class IMultimediaManagerObserver +{ +public: + virtual ~IMultimediaManagerObserver () {} + virtual void onVideoBufferStateChanged(uint32_t fillstateInPercent) = 0; + virtual void onVideoSegmentBufferStateChanged(uint32_t fillstateInPercent) = 0; + virtual void onAudioBufferStateChanged(uint32_t fillstateInPercent) = 0; + virtual void onAudioSegmentBufferStateChanged(uint32_t fillstateInPercent) = 0; + virtual void onEOS() = 0; + virtual void notifyStatistics(int, uint32_t, int, uint32_t) = 0; + virtual void notifyQualityDownloading(uint32_t) = 0; +}; +} +} +#endif /* VIPER_MANAGERS_IMULTIMEDIAMANAGEROBSERVER_H_ */ diff --git a/Managers/IStreamObserver.h b/Managers/IStreamObserver.h new file mode 100644 index 00000000..d6e8a19c --- /dev/null +++ b/Managers/IStreamObserver.h @@ -0,0 +1,46 @@ +/* + * IStreamObserver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef VIPER_MANAGERS_STREAMOBSERVER_H_ +#define VIPER_MANAGERS_STREAMOBSERVER_H_ + +#include + +namespace viper +{ +namespace managers +{ +enum StreamType +{ + AUDIO = (1u << 0), + VIDEO = (1u << 1), + SUBTITLE = (1u << 2), +}; + +class IStreamObserver +{ +public: + virtual ~IStreamObserver () {} + virtual void onSegmentDownloaded() = 0; + virtual void onSegmentBufferStateChanged(StreamType type, uint32_t fillstateInPercent, int maxC) = 0; + virtual void onVideoBufferStateChanged(uint32_t fillstateInPercent) = 0; + virtual void onAudioBufferStateChanged(uint32_t fillstateInPercent) = 0; + virtual void setEOS(bool value) = 0; + virtual void notifyStatistics(int segNum, uint32_t bitrate, int fps, uint32_t quality) = 0; + virtual void notifyQualityDownloading(uint32_t quality) = 0; + virtual bool canPush() = 0; + virtual int getBufferLevel() = 0; + +}; +} +} + +#endif /* VIPER_MANAGERS_STREAMOBSERVER_H_ */ diff --git a/Managers/MultimediaManager.cpp b/Managers/MultimediaManager.cpp new file mode 100644 index 00000000..faab66cf --- /dev/null +++ b/Managers/MultimediaManager.cpp @@ -0,0 +1,629 @@ +/* + * MultimediaManager.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "MultimediaManager.h" + +#include + +using namespace libdash::framework::adaptation; +using namespace libdash::framework::buffer; +using namespace viper::managers; +using namespace dash::mpd; + +#include + +MultimediaManager::MultimediaManager(ViperGui *viperGui, int segBufSize, std::string downloadPath, bool nodecoding) : + viperGui (viperGui), + segmentBufferSize (segBufSize), + downloadPath (downloadPath), + offset (offset), + mpd (NULL), + period (NULL), + videoAdaptationSet (NULL), + videoRepresentation (NULL), + videoLogic (NULL), + videoStream (NULL), + audioAdaptationSet (NULL), + audioRepresentation (NULL), + audioLogic (NULL), + videoRendererHandle (NULL), + audioRendererHandle (NULL), + audioStream (NULL), + started (false), + stopping (false), + framesDisplayed (0), + segmentsDownloaded (0), + isVideoRendering (false), + isAudioRendering (false), + eos (false), + playing (false), + noDecoding (nodecoding) +{ + InitializeCriticalSection (&this->monitorMutex); + InitializeCriticalSection (&this->monitorBufferMutex); + InitializeCriticalSection (&this->monitor_playing_video_mutex); + InitializeConditionVariable (&this->playingVideoStatusChanged); + InitializeCriticalSection (&this->monitor_playing_audio_mutex); + InitializeConditionVariable (&this->playingAudioStatusChanged); + + this->manager = CreateDashManager(); +} + +MultimediaManager::~MultimediaManager () +{ + this->stop(); + this->manager->Delete(); + DeleteCriticalSection (&this->monitorMutex); + DeleteCriticalSection (&this->monitorBufferMutex); + DeleteCriticalSection (&this->monitor_playing_video_mutex); + DeleteConditionVariable (&this->playingVideoStatusChanged); + DeleteCriticalSection (&this->monitor_playing_audio_mutex); + DeleteConditionVariable (&this->playingAudioStatusChanged); +} + +IMPD* MultimediaManager::getMPD() +{ + return this->mpd; +} + +bool MultimediaManager::init(const std::string& url) +{ + EnterCriticalSection(&this->monitorMutex); + this->mpd = this->manager->Open((char *)url.c_str()); + Debug("url : %s\n", url.c_str()); + if(this->mpd == NULL) + { + LeaveCriticalSection(&this->monitorMutex); + return false; + } + Debug("Done DL the mpd\n"); + LeaveCriticalSection(&this->monitorMutex); + return true; +} + +bool MultimediaManager::initICN(const std::string& url) +{ + EnterCriticalSection(&this->monitorMutex); + libdash::framework::input::IICNConnection* icnConn = new libdash::framework::input::ICNConnectionConsumerApi(20.0, this->beta, this->drop); + icnConn->InitForMPD(url); + int ret = 0; + char * data = (char *)malloc(4096); + int pos = url.find_last_of("/"); + if(pos == std::string::npos) + { + pos = strlen(url.c_str()); + } + else + { + pos = pos + 1; + } + + std::string downloadFile(this->downloadPath + url.substr(pos).c_str()); + FILE *fp; + fp = fopen(downloadFile.c_str(), "w"); + if(fp == NULL) + { + free(data); + delete icnConn; + LeaveCriticalSection(&this->monitorMutex); + return false; + } + ret = icnConn->Read((uint8_t*)data, 4096); + while(ret) + { + fwrite(data, sizeof(char), ret, fp); + ret = icnConn->Read((uint8_t*)data,4096); + } + fclose(fp); + this->mpd = this->manager->Open(const_cast(downloadFile.c_str()), url); + if(this->mpd == NULL) + { + remove(downloadFile.c_str()); + free(data); + delete icnConn; + LeaveCriticalSection(&this->monitorMutex); + return false; + } + remove(downloadFile.c_str()); + free(data); + delete icnConn; + LeaveCriticalSection(&this->monitorMutex); + return true; +} + +bool MultimediaManager::isStarted() +{ + return this->started; +} + +bool MultimediaManager::isStopping() +{ + return this->stopping; +} + +bool MultimediaManager::isICN() +{ + return this->icn; +} + +void MultimediaManager::start(bool icnEnabled, double icnAlpha, uint32_t nextOffset) +{ + this->icn = icnEnabled; + this->icnAlpha = icnAlpha; + if (this->started) + this->stop(); + if(icnAlpha <= 1 && icnAlpha >=0) + { + qDebug("ICN-enhanced rate estimation: alpha = %f\n",icnAlpha); + + } else { + qDebug("normal rate estimation\n"); + } + EnterCriticalSection(&this->monitorMutex); + if (this->videoAdaptationSet && this->videoRepresentation) + { + this->initVideoRendering(nextOffset); + this->videoStream->setAdaptationLogic(this->videoLogic); + this->videoLogic->setMultimediaManager(this); + this->videoStream->start(); + this->startVideoRenderingThread(); + } + this->started = true; + this->playing = true; + LeaveCriticalSection(&this->monitorMutex); +} + +void MultimediaManager::stop() +{ + if (!this->started) + return; + this->stopping = true; + EnterCriticalSection(&this->monitorMutex); + this->stopVideo(); + this->stopping = false; + this->started = false; + LeaveCriticalSection(&this->monitorMutex); + Debug("VIDEO STOPPED\n"); + this->period = this->mpd->GetPeriods().at(0); + this->videoAdaptationSet = this->period->GetAdaptationSets().at(0); + this->videoRepresentation = this->videoAdaptationSet->GetRepresentation().at(0); + +} + +void MultimediaManager::stopVideo() +{ + if(this->started && this->videoStream) + { + this->videoStream->stop(); + this->stopVideoRenderingThread(); + delete this->videoStream; + delete this->videoLogic; + this->videoStream = NULL; + this->videoLogic = NULL; + } +} + +void MultimediaManager::stopAudio() +{ + if (this->started && this->audioStream) + { + //TODO add audio support + } +} + +bool MultimediaManager::setVideoQuality(IPeriod* period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + EnterCriticalSection(&this->monitorMutex); + + this->period = period; + this->videoAdaptationSet = adaptationSet; + this->videoRepresentation = representation; + if (this->videoStream) + this->videoStream->setRepresentation(this->period, this->videoAdaptationSet, this->videoRepresentation); + + LeaveCriticalSection(&this->monitorMutex); + return true; +} + +bool MultimediaManager::setAudioQuality(IPeriod* period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + EnterCriticalSection(&this->monitorMutex); + + this->period = period; + this->audioAdaptationSet = adaptationSet; + this->audioRepresentation = representation; + if (this->audioStream) + this->audioStream->setRepresentation(this->period, this->audioAdaptationSet, this->audioRepresentation); + LeaveCriticalSection(&this->monitorMutex); + return true; +} + +bool MultimediaManager::isUserDependent() +{ + if(this->videoLogic) + return this->videoLogic->isUserDependent(); + else + return true; +} + +bool MultimediaManager::setVideoAdaptationLogic(libdash::framework::adaptation::LogicType type, struct libdash::framework::adaptation::AdaptationParameters *params) +{ + if(this->videoAdaptationSet) + { + if(this->videoLogic) + delete(this->videoLogic); + this->videoLogic = AdaptationLogicFactory::create(type, this->mpd, this->period, this->videoAdaptationSet, 1, params); + this->logicName = LogicType_string[type]; + } + else + { + this->videoLogic = NULL; + return true; + } + if(this->videoLogic) + return true; + else + return false; +} + +void MultimediaManager::shouldAbort(bool isVideo) +{ + if(isVideo) + { + this->videoStream->shouldAbort(); + return; + } + else + { + this->audioStream->shouldAbort(); + } +} + +void MultimediaManager::setTargetDownloadingTime(bool isVideo, double target) +{ + if(isVideo) + this->videoStream->setTargetDownloadingTime(target); + else + this->audioStream->setTargetDownloadingTime(target); +} + +bool MultimediaManager::setAudioAdaptationLogic(libdash::framework::adaptation::LogicType type, struct libdash::framework::adaptation::AdaptationParameters *params) +{ + if(this->audioAdaptationSet) + { + this->audioLogic = AdaptationLogicFactory::create(type, this->mpd, this->period, this->audioAdaptationSet, 0, params); + this->logicName = LogicType_string[type]; + } + else + { + this->audioLogic = NULL; + return true; + } + if(this->audioLogic) + return true; + else + return false; +} + +void MultimediaManager::attachManagerObserver(IMultimediaManagerObserver *observer) +{ + this->managerObservers.push_back(observer); +} + +void MultimediaManager::notifyVideoBufferObservers(uint32_t fillstateInPercent) +{ + for (size_t i = 0; i < this->managerObservers.size(); i++) + this->managerObservers.at(i)->onVideoBufferStateChanged(fillstateInPercent); +} + +void MultimediaManager::notifyVideoSegmentBufferObservers(uint32_t fillstateInPercent) +{ + for (size_t i = 0; i < this->managerObservers.size(); i++) + this->managerObservers.at(i)->onVideoSegmentBufferStateChanged(fillstateInPercent); +} + +void MultimediaManager::notifyAudioSegmentBufferObservers(uint32_t fillstateInPercent) +{ + for (size_t i = 0; i < this->managerObservers.size(); i++) + this->managerObservers.at(i)->onAudioSegmentBufferStateChanged(fillstateInPercent); +} + +void MultimediaManager::notifyAudioBufferObservers(uint32_t fillstateInPercent) +{ + for (size_t i = 0; i < this->managerObservers.size(); i++) + this->managerObservers.at(i)->onAudioBufferStateChanged(fillstateInPercent); +} + +void MultimediaManager::initVideoRendering(uint32_t offset) +{ + this->videoStream = new MultimediaStream(viper::managers::VIDEO, this->mpd, this->segmentBufferSize, this->isICN(), this->icnAlpha, this->noDecoding, this->beta, this->drop); + this->videoStream->attachStreamObserver(this); + this->videoStream->setRepresentation(this->period, this->videoAdaptationSet, this->videoRepresentation); + this->videoStream->setPosition(offset); +} + +void MultimediaManager::initAudioPlayback(uint32_t offset) +{ + this->audioStream = new MultimediaStream(viper::managers::AUDIO, this->mpd, this->segmentBufferSize, this->isICN(), this->icnAlpha, this->noDecoding, this->beta, this->drop); + this->audioStream->attachStreamObserver(this); + this->audioStream->setRepresentation(this->period, this->audioAdaptationSet, this->audioRepresentation); + this->audioStream->setPosition(offset); +} + +void MultimediaManager::setLooping(bool looping) +{ + if(this->videoStream) + { + this->videoStream->setLooping(looping); + } + if(this->audioStream) + { + this->audioStream->setLooping(looping); + } +} + +void MultimediaManager::onSegmentDownloaded () +{ + this->segmentsDownloaded++; +} + +void MultimediaManager::onSegmentBufferStateChanged(StreamType type, uint32_t fillstateInPercent, int maxC) +{ + switch (type) + { + case AUDIO: + this->notifyAudioSegmentBufferObservers(fillstateInPercent); + break; + case VIDEO: + this->notifyVideoSegmentBufferObservers(fillstateInPercent); + break; + default: + break; + } +} + +void MultimediaManager::onVideoBufferStateChanged(uint32_t fillstateInPercent) +{ + this->notifyVideoBufferObservers(fillstateInPercent); +} + +void MultimediaManager::onAudioBufferStateChanged(uint32_t fillstateInPercent) +{ + this->notifyAudioBufferObservers(fillstateInPercent); +} + +void MultimediaManager::setFrameRate(double framerate) +{ + this->frameRate = framerate; +} + +void MultimediaManager::setEOS(bool value) +{ + this->eos = value; + if(value) //ie: End of Stream so the rendering thread(s) will finish + { + this->stopping = true; + if(this->videoRendererHandle != NULL) + { + this->stopVideoRenderingThread(); + this->videoRendererHandle = NULL; + } + if(this->audioRendererHandle != NULL) + { + this->stopAudioRenderingThread(); + this->audioRendererHandle = NULL; + } + this->stopping = false; + for(size_t i = 0; i < this->managerObservers.size(); i++) + this->managerObservers.at(i)->onEOS(); + } +} + +bool MultimediaManager::startVideoRenderingThread() +{ + this->isVideoRendering = true; + if(!noDecoding) + this->videoRendererHandle = createThreadPortable (pushVideo, this); + else + this->videoRendererHandle = createThreadPortable (pushVideoNoOut, this); + + if(this->videoRendererHandle == NULL) + return false; + + return true; +} + +void MultimediaManager::stopVideoRenderingThread() +{ + this->isVideoRendering = false; + if (this->videoRendererHandle != NULL) + { + JoinThread(this->videoRendererHandle); + destroyThreadPortable(this->videoRendererHandle); + } +} + +bool MultimediaManager::startAudioRenderingThread() +{ + this->isAudioRendering = true; + if(this->audioRendererHandle == NULL) + return false; + return true; +} + +void MultimediaManager::stopAudioRenderingThread() +{ + this->isAudioRendering = false; + if (this->audioRendererHandle != NULL) + { + JoinThread(this->audioRendererHandle); + destroyThreadPortable(this->audioRendererHandle); + } +} + +bool MultimediaManager::isPlaying() +{ + return this->playing; +} + +void MultimediaManager::onPausePressed() +{ + EnterCriticalSection(&this->monitor_playing_video_mutex); + EnterCriticalSection(&this->monitor_playing_audio_mutex); + this->playing = !this->playing; + WakeAllConditionVariable(&this->playingVideoStatusChanged); + WakeAllConditionVariable(&this->playingAudioStatusChanged); + LeaveCriticalSection(&this->monitor_playing_video_mutex); + LeaveCriticalSection(&this->monitor_playing_audio_mutex); +} + +void* MultimediaManager::pushVideoNoOut(void *data) +{ + MultimediaManager *manager = (MultimediaManager*) data; + manager->lastPointInTime = std::chrono::system_clock::now(); + manager->bufferingLimit = manager->lastPointInTime; + libdash::framework::input::MediaObject *segment = manager->videoStream->getSegment(); + using duration_in_milliSeconds = std::chrono::duration>; + std::chrono::time_point timeOfInsertion; + std::chrono::time_point startTime; + long int actualPosition; + + while(manager->isVideoRendering) + { + if (segment) + { + timeOfInsertion = std::chrono::system_clock::now(); + actualPosition = std::chrono::duration_cast(manager->bufferingLimit - timeOfInsertion).count(); + if(actualPosition < 0) + { + Debug("MANAGER:\tRebuffered %d ms\n", actualPosition *(-1)); + manager->lastPointInTime = timeOfInsertion; + //TODO Replace the 2 by a variable with segmentDuration + manager->bufferingLimit = manager->lastPointInTime + std::chrono::seconds(2); + } + else + { + //TODO Replace the 2 by a variable with segmentDuration + Debug("MANAGER: INSERT TO BUFFER old_fillness: %f, new_fillness: %f\n", (double)((double)actualPosition/1000.0) / (double) this->segmentBufferSize, (double)((double)(actualPosition + 2000)/1000.0) / (double) manager->segmentBufferSize); + manager->bufferingLimit = manager->bufferingLimit + std::chrono::seconds(2); + manager->lastPointInTime = timeOfInsertion; + } + delete segment; + } + else + { + //noDecoding here means noGUI + if(manager->noDecoding) + manager->setEOS(true); + } + segment = manager->videoStream->getSegment(); + } + return NULL; + +} + +void MultimediaManager::notifyStatistics(int segNum, uint32_t bitrate, int fps, uint32_t quality) +{ + for(size_t i = 0; i < this->managerObservers.size(); i++) + { + this->managerObservers.at(i)->notifyStatistics(segNum, bitrate, fps, quality); + } +} + +void MultimediaManager::notifyQualityDownloading(uint32_t quality) +{ + for(size_t i = 0; i < this->managerObservers.size(); i++) + { + this->managerObservers.at(i)->notifyQualityDownloading(quality); + } +} + +void MultimediaManager::notifyBufferChange() +{ + if(this->videoStream) + { + this->videoStream->notifyBufferChange(this->getUBufferLevel(), this->segmentBufferSize); + } + if(this->audioStream) + { + this->audioStream->notifyBufferChange(this->getUBufferLevel(), this->segmentBufferSize); + } +} + +int MultimediaManager::getBufferLevel() +{ + return (int)this->getUBufferLevel(); +} + +uint32_t MultimediaManager::getUBufferLevel() +{ + int mBufferLevel = 0; + int segmentDurationInMs = 2000; + + if(noDecoding) + { + std::chrono::time_point timeNow = std::chrono::system_clock::now(); + using duration_in_milliSeconds = std::chrono::duration>; + long int actualPos = std::chrono::duration_cast(this->bufferingLimit - timeNow).count(); + int res = ((double)actualPos) / (double (this->segmentBufferSize * segmentDurationInMs)) * 100; + return res >= 0 ? res > 100 ? 100 : (uint32_t) res : 0; + } + else + { + mBufferLevel = this->viperGui->getBufferDuration(); + int res = ((int)mBufferLevel)/((double) (this->segmentBufferSize * this->viperGui->getSegmentDuration())) * 100; + return res >= 0 ? res > 100 ? 100 : (uint32_t) res : 0; + + } +} + +bool MultimediaManager::canPush() +{ + int segmentDurationInMs = 2000; + while(this->getUBufferLevel() >= 100 && !this->stopping) + { + sleep(segmentDurationInMs / 1000); + } + return true; +} + +void* MultimediaManager::pushVideo(void *data) +{ + MultimediaManager *manager = (MultimediaManager*) data; + libdash::framework::input::MediaObject *segment = manager->videoStream->getSegment(); + long int segmentDurationInMs = 2000; + while(manager->isVideoRendering) + { + if (segment) + { + manager->notifyBufferChange(); + manager->viperGui->writeData(segment); + delete segment; + } + segment = manager->videoStream->getSegment(); + } + return NULL; +} + +void MultimediaManager::setOffset(int offset) +{ + this->offset = offset; +} + +void MultimediaManager::setBeta(float beta) +{ + this->beta = beta; +} + +void MultimediaManager::setDrop(float drop) +{ + this->drop = drop; +} diff --git a/Managers/MultimediaManager.h b/Managers/MultimediaManager.h new file mode 100644 index 00000000..b4187486 --- /dev/null +++ b/Managers/MultimediaManager.h @@ -0,0 +1,140 @@ +/* + * MultimediaManager.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef VIPER_MANAGERS_MULTIMEDIAMANAGER_H_ +#define VIPER_MANAGERS_MULTIMEDIAMANAGER_H_ + +#include "IMPD.h" +#include "MultimediaStream.h" +#include "IMultimediaManagerBase.h" +#include "IMultimediaManagerObserver.h" +#include "../Adaptation/IAdaptationLogic.h" +#include "../Adaptation/AdaptationLogicFactory.h" +#include "../Portable/MultiThreading.h" +#include +#include +#include +#include "Common/ViperBuffer.h" +#include "UI/ViperGui.h" + +namespace viper +{ +namespace managers +{ +class MultimediaManager : public IStreamObserver, public IMultimediaManagerBase +{ +public: + MultimediaManager(ViperGui *viperGui, int segmentBufferSize, std::string downloadPath, bool noDecoding = false); + virtual ~MultimediaManager(); + + bool init (const std::string& url); + bool initICN (const std::string& url); + void start (bool icnEnabled, double icnAlpha, uint32_t nextOffset); + void stop (); + dash::mpd::IMPD* getMPD (); + + bool setVideoQuality (dash::mpd::IPeriod* period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation); + bool setAudioQuality (dash::mpd::IPeriod* period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation); + + bool setVideoAdaptationLogic (libdash::framework::adaptation::LogicType type, struct libdash::framework::adaptation::AdaptationParameters *params); + bool setAudioAdaptationLogic (libdash::framework::adaptation::LogicType type, struct libdash::framework::adaptation::AdaptationParameters *params); + + void attachManagerObserver (IMultimediaManagerObserver *observer); + + void setFrameRate (double frameRate); + + /* IStreamObserver */ + void onSegmentDownloaded (); + void onSegmentBufferStateChanged (StreamType type, uint32_t fillstateInPercent, int maxC); + void onVideoBufferStateChanged (uint32_t fillstateInPercent); + void onAudioBufferStateChanged (uint32_t fillstateInPercent); + bool isUserDependent (); + bool isStarted (); + bool isStopping (); + bool isICN (); + void setEOS (bool value); + void shouldAbort (bool isVideo); + void setTargetDownloadingTime (bool isVid, double time); + bool isPlaying (); + void onPausePressed (); + void notifyStatistics (int segNum, uint32_t bitrate, int fps, uint32_t quality); + void notifyQualityDownloading (uint32_t quality); + uint32_t getUBufferLevel (); + int getBufferLevel (); + void setLooping (bool looping); + void setOffset(int offset); + void setBeta(float beta); + void setDrop(float drop); + bool canPush (); + CRITICAL_SECTION monitorBufferMutex; + + int offset; + std::chrono::time_point lastPointInTime; + std::chrono::time_point bufferingLimit; + +private: + float beta; + float drop; + std::string downloadPath; + int segmentBufferSize; + ViperGui *viperGui; + dash::IDASHManager *manager; + dash::mpd::IMPD *mpd; + dash::mpd::IPeriod *period; + dash::mpd::IAdaptationSet *videoAdaptationSet; + dash::mpd::IRepresentation *videoRepresentation; + libdash::framework::adaptation::IAdaptationLogic *videoLogic; + MultimediaStream *videoStream; + dash::mpd::IAdaptationSet *audioAdaptationSet; + dash::mpd::IRepresentation *audioRepresentation; + libdash::framework::adaptation::IAdaptationLogic *audioLogic; + MultimediaStream *audioStream; + std::vector managerObservers; + bool started; + bool stopping; + bool icn; + double icnAlpha; + uint64_t framesDisplayed; + uint64_t segmentsDownloaded; + CRITICAL_SECTION monitorMutex; + double frameRate; + THREAD_HANDLE videoRendererHandle; + THREAD_HANDLE audioRendererHandle; + bool isVideoRendering; + bool isAudioRendering; + bool eos; + bool playing; + mutable CRITICAL_SECTION monitor_playing_video_mutex; + mutable CONDITION_VARIABLE playingVideoStatusChanged; + mutable CRITICAL_SECTION monitor_playing_audio_mutex; + mutable CONDITION_VARIABLE playingAudioStatusChanged; + const char *logicName; + bool noDecoding; + void notifyBufferChange (); + bool startVideoRenderingThread (); + void stopVideoRenderingThread (); + static void* pushVideo (void *data); + static void* pushVideoNoOut (void *data); + bool startAudioRenderingThread (); + void stopAudioRenderingThread (); + void initVideoRendering (uint32_t offset); + void initAudioPlayback (uint32_t offset); + void stopVideo (); + void stopAudio (); + void notifyVideoBufferObservers (uint32_t fillstateInPercent); + void notifyVideoSegmentBufferObservers (uint32_t fillstateInPercent); + void notifyAudioBufferObservers (uint32_t fillstateInPercent); + void notifyAudioSegmentBufferObservers (uint32_t fillstateInPercent); +}; +} +} + +#endif /* VIPER_MANAGERS_MULTIMEDIAMANAGER_H_ */ diff --git a/Managers/MultimediaStream.cpp b/Managers/MultimediaStream.cpp new file mode 100644 index 00000000..ed0d967c --- /dev/null +++ b/Managers/MultimediaStream.cpp @@ -0,0 +1,208 @@ +/* + * MultimediaStream.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "MultimediaStream.h" + +using namespace viper::managers; +using namespace libdash::framework::adaptation; +using namespace libdash::framework::input; +using namespace libdash::framework::buffer; +using namespace dash::mpd; + +MultimediaStream::MultimediaStream(StreamType type, IMPD *mpd, uint32_t bufferSize, bool icnEnabled, double icnAlpha, bool nodecoding, float beta, float drop) : + type (type), + segmentBufferSize (bufferSize), + dashManager (NULL), + mpd (mpd), + icn (icnEnabled), + icnAlpha (icnAlpha), + noDecoding (nodecoding), + beta (beta), + drop (drop) +{ + this->init(); +} +MultimediaStream::~MultimediaStream () +{ + this->stop(); + delete this->dashManager; +} + +bool MultimediaStream::isICN() +{ + return this->icn; +} + +void MultimediaStream::shouldAbort() +{ + this->dashManager->shouldAbort(); +} + +uint32_t MultimediaStream::getPosition() +{ + return this->dashManager->getPosition(); +} + +void MultimediaStream::setLooping(bool looping) +{ + this->dashManager->setLooping(looping); +} + +void MultimediaStream::setPosition(uint32_t segmentNumber) +{ + this->dashManager->setPosition(segmentNumber); +} + +void MultimediaStream::setPositionInMsec(uint32_t milliSecs) +{ + this->dashManager->setPositionInMsec(milliSecs); +} + +void MultimediaStream::init() +{ + this->dashManager = new DASHManager(this->type, this->segmentBufferSize, this, this->mpd, this->isICN(), this->icnAlpha, this->noDecoding, this->beta, this->drop); +} + +bool MultimediaStream::start() +{ + if(!this->startDownload()) + return false; + + return true; +} + +bool MultimediaStream::startDownload() +{ + dashManager->setAdaptationLogic(this->logic); + if(!dashManager->start()) + return false; + + return true; +} + +void MultimediaStream::stop() +{ + this->stopDownload(); +} + +void MultimediaStream::stopDownload() +{ + this->dashManager->stop(); +} + +void MultimediaStream::clear() +{ + this->dashManager->clear(); +} + +void MultimediaStream::addFrame(QImage *frame) +{ +} + +QImage* MultimediaStream::getFrame() +{ + return NULL; +} + +void MultimediaStream::attachStreamObserver(IStreamObserver *observer) +{ + this->observers.push_back(observer); +} + +void MultimediaStream::setRepresentation(IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + this->dashManager->setRepresentation(period, adaptationSet, representation); +} + +void MultimediaStream::enqueueRepresentation(IPeriod *period, IAdaptationSet *adaptationSet, IRepresentation *representation) +{ + this->dashManager->enqueueRepresentation(period, adaptationSet, representation); +} + +void MultimediaStream::setAdaptationLogic(libdash::framework::adaptation::IAdaptationLogic *logic) +{ + this->logic = logic; +} + +void MultimediaStream::onSegmentBufferStateChanged(uint32_t fillstateInPercent, int maxC) +{ + for (size_t i = 0; i < observers.size(); i++) + this->observers.at(i)->onSegmentBufferStateChanged(this->type, fillstateInPercent, maxC); +} + +void MultimediaStream::onBufferStateChanged(BufferType type, uint32_t fillstateInPercent, int maxC) +{ + switch(type) + { + case libdash::framework::buffer::AUDIO: + for (size_t i = 0; i < observers.size(); i++) + this->observers.at(i)->onAudioBufferStateChanged(fillstateInPercent); + break; + case libdash::framework::buffer::VIDEO: + for (size_t i = 0; i < observers.size(); i++) + this->observers.at(i)->onVideoBufferStateChanged(fillstateInPercent); + default: + break; + } +} + +void MultimediaStream::setEOS(bool value) +{ + for(size_t i = 0; i < observers.size(); i++) + this->observers.at(i)->setEOS(value); +} + +void MultimediaStream::setTargetDownloadingTime(double target) +{ + this->dashManager->setTargetDownloadingTime(target); +} + +void MultimediaStream::notifyStatistics(int segNum, uint32_t bitrate, int fps, uint32_t quality) +{ + for(size_t i = 0; i < observers.size(); i++) + this->observers.at(i)->notifyStatistics(segNum, bitrate, fps, quality); +} + +void MultimediaStream::notifyQualityDownloading(uint32_t quality) +{ + for(size_t i = 0; i < observers.size(); i++) + this->observers.at(i)->notifyQualityDownloading(quality); +} + +int MultimediaStream::getBufferLevel() +{ + int bufferFill = 0; + for(size_t i=0; i < observers.size(); i++) + { + bufferFill = this->observers.at(i)->getBufferLevel(); + } + return bufferFill; +} +bool MultimediaStream::canPush() +{ + bool flag = false; + for(size_t i=0; i < observers.size(); i++) + { + flag = flag || this->observers.at(i)->canPush(); + } + return flag; +} + + +libdash::framework::input::MediaObject* MultimediaStream::getSegment() +{ + return this->dashManager->getSegment(); +} + +void MultimediaStream::notifyBufferChange(uint32_t bufferfill, int maxC) +{ + this->dashManager->onBufferStateChanged(libdash::framework::buffer::VIDEO, bufferfill, maxC); +} diff --git a/Managers/MultimediaStream.h b/Managers/MultimediaStream.h new file mode 100644 index 00000000..6918f639 --- /dev/null +++ b/Managers/MultimediaStream.h @@ -0,0 +1,89 @@ +/* + * MultimediaStream.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef VIPER_MANAGERS_MULTIMEDIASTREAM_H_ +#define VIPER_MANAGERS_MULTIMEDIASTREAM_H_ + +#include "IMPD.h" +#include "IStreamObserver.h" +#include "../Input/DASHManager.h" +#include "../Buffer/IBufferObserver.h" +#include "../Adaptation/IAdaptationLogic.h" +#include +#include "../Input/IDASHManagerObserver.h" +#include "../Buffer/Buffer.h" +#include + +namespace viper +{ +namespace managers +{ +class MultimediaStream : public libdash::framework::input::IDASHManagerObserver, public libdash::framework::buffer::IBufferObserver +{ +public: + MultimediaStream(StreamType type, dash::mpd::IMPD *mpd, uint32_t segmentBufferSize, bool icnEnabled, double icnAlpha, bool nodecoding, float beta, float drop); + virtual ~MultimediaStream(); + + bool start(); + void stop(); + void stopDownload(); + bool startDownload(); + void clear(); + uint32_t getPosition(); + void setPosition(uint32_t segmentNumber); + void setLooping(bool looping); + void setPositionInMsec(uint32_t milliSecs); + + void addFrame(QImage *frame); + QImage* getFrame(); + libdash::framework::input::MediaObject* getSegment(); + + void setEOS(bool value); + + void notifyBufferChange(uint32_t bufferfill, int maxC); + + void setRepresentation(dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation); + void enqueueRepresentation(dash::mpd::IPeriod *period, dash::mpd::IAdaptationSet *adaptationSet, dash::mpd::IRepresentation *representation); + void setAdaptationLogic(libdash::framework::adaptation::IAdaptationLogic *logic); + + void attachStreamObserver(IStreamObserver *observer); + + void onSegmentBufferStateChanged(uint32_t fillstateInPercent, int maxC); + void onBufferStateChanged(libdash::framework::buffer::BufferType type, uint32_t fillstateInPercent, int maxC); + + void notifyStatistics(int segNum, uint32_t bitrate, int fps, uint32_t quality); + void notifyQualityDownloading (uint32_t quality); + bool canPush(); + int getBufferLevel(); + bool isICN(); + void shouldAbort(); + + void setTargetDownloadingTime(double); + +private: + float beta; + float drop; + std::vector observers; + dash::mpd::IMPD *mpd; + libdash::framework::adaptation::IAdaptationLogic *logic; + libdash::framework::input::DASHManager *dashManager; + uint32_t segmentBufferSize; + StreamType type; + bool icn; + double icnAlpha; + + bool noDecoding; + void init (); +}; +} +} + +#endif /* VIPER_MANAGERS_MULTIMEDIASTREAM_H_ */ diff --git a/Portable/MultiThreading.cpp b/Portable/MultiThreading.cpp new file mode 100644 index 00000000..fd5dc14d --- /dev/null +++ b/Portable/MultiThreading.cpp @@ -0,0 +1,120 @@ +/* + * MultiThreading.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "MultiThreading.h" + +THREAD_HANDLE createThreadPortable(void *(*start_routine) (void *), void *arg) +{ +#if defined _WIN32 || defined _WIN64 + return CreateThread (0, 0, (LPTHREAD_START_ROUTINE)start_routine, (LPVOID)arg, 0, 0); +#else + THREAD_HANDLE th = (THREAD_HANDLE)malloc(sizeof(pthread_t)); + if (!th) + { + std::cerr << "Error allocating thread." << std::endl; + return NULL; + } + + if(int err = pthread_create(th, NULL, start_routine, arg)) + { + std::cerr << strerror(err) << std::endl; + return NULL; + } + return th; +#endif +} +void destroyThreadPortable(THREAD_HANDLE th) +{ +#if !defined _WIN32 && !defined _WIN64 + if(th) + free(th); +#else + CloseHandle(th); +#endif +} + +/**************************************************************************** +* Condition variables for Windows XP and older windows sytems +*****************************************************************************/ +#if defined WINXPOROLDER +void InitCondition (condition_variable_t *cv) +{ + InitializeCriticalSection(&cv->waitersCountLock); + + cv->waitersCount = 0; + cv->waitGenerationCount = 0; + cv->releaseCount = 0; + + cv->waitingEvent = CreateEvent (NULL, // no security + TRUE, // manual-reset + FALSE, // non-signaled initially + NULL); // unnamed +} +void WaitCondition (condition_variable_t *cv, CRITICAL_SECTION *externalMutex) +{ + EnterCriticalSection(&cv->waitersCountLock); + + cv->waitersCount++; + + int currentGenerationCount = cv->waitGenerationCount; + + LeaveCriticalSection(&cv->waitersCountLock); + LeaveCriticalSection(externalMutex); + + bool isWaitDone = false; + while(!isWaitDone) + { + WaitForSingleObject (cv->waitingEvent, INFINITE); + EnterCriticalSection (&cv->waitersCountLock); + + isWaitDone = (cv->releaseCount > 0 && cv->waitGenerationCount != currentGenerationCount); + LeaveCriticalSection (&cv->waitersCountLock); + } + + EnterCriticalSection(externalMutex); + EnterCriticalSection(&cv->waitersCountLock); + + cv->waitersCount--; + cv->releaseCount--; + bool isLastWaiter = (cv->releaseCount == 0); + + LeaveCriticalSection(&cv->waitersCountLock); + + if(isLastWaiter) + ResetEvent(cv->waitingEvent); +} +void SignalCondition (condition_variable_t *cv) +{ + EnterCriticalSection(&cv->waitersCountLock); + + if(cv->waitersCount > cv->releaseCount) + { + SetEvent(cv->waitingEvent); + cv->releaseCount++; + cv->waitGenerationCount++; + } + + LeaveCriticalSection(&cv->waitersCountLock); +} +void BroadcastCondition (condition_variable_t *cv) +{ + EnterCriticalSection(&cv->waitersCountLock); + + if(cv->waitersCount > 0) + { + SetEvent(cv->waitingEvent); + cv->releaseCount = cv->waitersCount; + cv->waitGenerationCount++; + } + + LeaveCriticalSection(&cv->waitersCountLock); +} +#endif diff --git a/Portable/MultiThreading.h b/Portable/MultiThreading.h new file mode 100644 index 00000000..b959d49e --- /dev/null +++ b/Portable/MultiThreading.h @@ -0,0 +1,86 @@ +/* + * MultiThreading.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef LIBDASH_FRAMEWORK_PORTABLE_MULTITHREADING_H_ +#define LIBDASH_FRAMEWORK_PORTABLE_MULTITHREADING_H_ + +#if defined _WIN32 || defined _WIN64 + + #define _WINSOCKAPI_ + #include + #define DeleteConditionVariable(cond_p) {} + + #define PortableSleep(seconds) Sleep(seconds * 1000) + #define JoinThread(handle) WaitForSingleObject(handle, INFINITE) + + typedef HANDLE THREAD_HANDLE; + + #if defined WINXPOROLDER + /**************************************************************************** + * Variables + *****************************************************************************/ + struct condition_variable_t + { + int waitersCount; // Count of the number of waiters. + CRITICAL_SECTION waitersCountLock; // Serialize access to . + int releaseCount; // Number of threads to release via a or a . + int waitGenerationCount; // Keeps track of the current "generation" so that we don't allow one thread to steal all the "releases" from the broadcast. + HANDLE waitingEvent; // A manual-reset event that's used to block and release waiting threads. + }; + /**************************************************************************** + * Prototypes + *****************************************************************************/ + void InitCondition (condition_variable_t *cv); + void WaitCondition (condition_variable_t *cv, CRITICAL_SECTION *externalMutex); + void SignalCondition (condition_variable_t *cv); + void BroadcastCondition (condition_variable_t *cv); + /**************************************************************************** + * Defines + *****************************************************************************/ + #define CONDITION_VARIABLE condition_variable_t + + #define InitializeConditionVariable(cond_p) InitCondition(cond_p) + #define SleepConditionVariableCS(cond_p, mutex_p, infinite) WaitCondition(cond_p, mutex_p) // INFINITE should be handled mor properly + #define WakeConditionVariable(cond_p) SignalCondition(cond_p) + #define WakeAllConditionVariable(cond_p) BroadcastCondition(cond_p) + #endif + +#else + + #include + #include + #include + #include + #include + #include + #define CRITICAL_SECTION pthread_mutex_t + #define CONDITION_VARIABLE pthread_cond_t + + #define PortableSleep(seconds) usleep(seconds * 1000000) + #define JoinThread(handle) pthread_join(*(handle), NULL) + #define InitializeCriticalSection(mutex_p) pthread_mutex_init(mutex_p, NULL) + #define DeleteCriticalSection(mutex_p) pthread_mutex_destroy(mutex_p) + #define EnterCriticalSection(mutex_p) pthread_mutex_lock(mutex_p) + #define LeaveCriticalSection(mutex_p) pthread_mutex_unlock(mutex_p) + #define InitializeConditionVariable(cond_p) pthread_cond_init(cond_p, NULL) + #define DeleteConditionVariable(cond_p) pthread_cond_destroy(cond_p) + #define SleepConditionVariableCS(cond_p, mutex_p, infinite) pthread_cond_wait(cond_p, mutex_p) // INFINITE should be handled mor properly + #define WakeConditionVariable(cond_p) pthread_cond_signal(cond_p) + #define WakeAllConditionVariable(cond_p) pthread_cond_broadcast(cond_p) + + typedef pthread_t* THREAD_HANDLE; + +#endif + +THREAD_HANDLE createThreadPortable(void *(*start_routine) (void *), void *arg); +void destroyThreadPortable(THREAD_HANDLE th); + +#endif // LIBDASH_FRAMEWORK_PORTABLE_MULTITHREADING_H_ diff --git a/Portable/Networking.h b/Portable/Networking.h new file mode 100644 index 00000000..b7c5d2d3 --- /dev/null +++ b/Portable/Networking.h @@ -0,0 +1,38 @@ +/* + * Networking.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef PORTABLE_NETWORKING_H_ +#define PORTABLE_NETWORKING_H_ + +#if defined _WIN32 || defined _WIN64 + +#include +#include + +#else + +#include +#include +#include /* superset of previous */ +#include +#include +#include +#include + +#define closesocket(socket) close(socket) +#define WSAStartup(wVersionRequested, lpWSAData) 0 +#define WSACleanup() {} + +typedef unsigned char WSADATA; + +#endif + +#endif // PORTABLE_NETWORKING_H_ diff --git a/README.md b/README.md new file mode 100644 index 00000000..4933c577 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +Viper Player for ICN +==================================================== +This application is designed to provide a tool to test the adaptation video streaming using the ICN protocol stack. + +Dependencies +------------ + +- ffmpeg +- libboost-system-dev +- libparc +- libccnx-common +- libccnx-transport-rta +- long-bow +- libxml2 +- libcurl4 +- libdash +- libicnet +- Qt5.7 +- QtAV + +Build the dependencies +----------------- + +- QtAV +For building and install the library, from the root folder of the projet: + +```bash + $ git clone https://github.com/wang-bin/QtAV + $ cd QtAV + $ mkdir build && cd build + $ qmake ../QtAV.pro + $ make + $ sh sdk_install.sh +``` + +- libdash +For building the player, from the root folder of the project: + +```bash + $ git clone -b viper/master https://gerrit.fd.io/r/cicn viper + $ cd viper/libdash + $ mkdir build && cd build + $ cmake ../ + $ make + $ make install +``` + + + +Build the player +----------------- + +For building the player, from the root folder of the project: + +```bash + $ cd viper + $ mkdir build && cd build + $ qmake ../viper.pro + $ make + $ ./viper +``` + + +Platforms +--------- + +Viper has been tested in: + + - Ubuntu 16.04 (x86_64) diff --git a/UI/DASHPlayer.cpp b/UI/DASHPlayer.cpp new file mode 100644 index 00000000..b87dde74 --- /dev/null +++ b/UI/DASHPlayer.cpp @@ -0,0 +1,923 @@ +/* + * DASHPlayer.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "DASHPlayer.h" +#include + +using namespace libdash::framework::adaptation; +using namespace libdash::framework::mpd; +using namespace libdash::framework::buffer; +using namespace viper; +using namespace viper::managers; +using namespace dash::mpd; +using namespace std; + +DASHPlayer::DASHPlayer(ViperGui &gui, Config *config) : + gui (&gui), + config (config) +{ + InitializeCriticalSection(&this->monitorMutex); + this->offset = 0; + this->url = NULL; + this->icn = false; + this->adaptLogic = LogicType::RateBased; + this->seek = false; + this->reloadParameters(); + this->setSettings(0, 0, 0, 0, 0); + this->multimediaManager = new MultimediaManager(this->gui, this->parametersAdaptation->segmentBufferSize, config->getConfigPath().toStdString() + QString::fromLatin1("/").toStdString()); + this->multimediaManager->setBeta(config->beta()); + this->multimediaManager->setDrop(config->drop()); + connect(this->gui->getVideoPlayer(), SIGNAL(positionChanged(qint64)), SLOT(updateSlider(qint64))); + connect(this->gui->getVideoPlayer(), SIGNAL(stateChanged(QtAV::AVPlayer::State)), SLOT(manageGraph(QtAV::AVPlayer::State))); + connect(this->gui->getVideoPlayer(), SIGNAL(error(QtAV::AVError)), this, SLOT(error(QtAV::AVError))); + this->multimediaManager->attachManagerObserver(this); +} + +DASHPlayer::~DASHPlayer() +{ + this->multimediaManager->stop(); + delete(this->multimediaManager); + DeleteCriticalSection(&this->monitorMutex); +} + +void DASHPlayer::onStartButtonPressed(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation, int adaptationLogic) +{ + bool setOk = false; + setOk = this->multimediaManager->setVideoAdaptationLogic((LogicType)adaptationLogic, parametersAdaptation); + + if(!setOk) + { + return; + } + Debug("DASH PLAYER: STARTING VIDEO\n"); + this->multimediaManager->start(this->icn, 20, 0); +} +void DASHPlayer::stopButtonPressed() +{ + this->multimediaManager->stop(); +} + +void DASHPlayer::onStopButtonPressed() +{ + QMetaObject::invokeMethod(this->gui->getRootObject(), "unSetBuffering"); + this->gui->setPlay(false); + this->gui->setStop(true); + this->gui->setPause(false); + this->gui->getVideoPlayer()->stop(); + this->stopButtonPressed(); + this->multimediaManager->setOffset(0); + this->gui->resetGraphValues(); +} + +void DASHPlayer::onPauseButtonPressed() +{ + this->multimediaManager->onPausePressed(); +} + +void DASHPlayer::onSettingsChanged(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation) +{ + if(this->multimediaManager->getMPD() == NULL) + return; + + if (!this->settingsChanged(period, videoAdaptationSet, videoRepresentation, audioAdaptationSet, audioRepresentation)) + return; + + IPeriod *currentPeriod = this->multimediaManager->getMPD()->GetPeriods().at(period); + std::vector videoAdaptationSets = AdaptationSetHelper::getVideoAdaptationSets(currentPeriod); + std::vector audioAdaptationSets = AdaptationSetHelper::getAudioAdaptationSets(currentPeriod); + if (videoAdaptationSet >= 0 && videoRepresentation >= 0) + { + this->multimediaManager->setVideoQuality(currentPeriod, + videoAdaptationSets.at(videoAdaptationSet), + videoAdaptationSets.at(videoAdaptationSet)->GetRepresentation().at(videoRepresentation)); + } + else + { + this->multimediaManager->setVideoQuality(currentPeriod, NULL, NULL); + } +} + +void DASHPlayer::onVideoBufferStateChanged(uint32_t fillstateInPercent) +{ + emit videoBufferFillStateChanged(fillstateInPercent); +} + +void DASHPlayer::onVideoSegmentBufferStateChanged(uint32_t fillstateInPercent) +{ + emit videoSegmentBufferFillStateChanged(fillstateInPercent); +} + +void DASHPlayer::onAudioBufferStateChanged(uint32_t fillstateInPercent) +{ + emit audioBufferFillStateChanged(fillstateInPercent); +} + +void DASHPlayer::onAudioSegmentBufferStateChanged(uint32_t fillstateInPercent) +{ + emit audioSegmentBufferFillStateChanged(fillstateInPercent); +} + +void DASHPlayer::onEOS() +{ + this->onStopButtonPressed(); +} + +bool DASHPlayer::onDownloadMPDPressed (const std::string &url) +{ + this->multimediaManager->setOffset(0); + if(this->icn) + { + if(!this->multimediaManager->initICN(url)) + { + return false; + } + } + else + { + if(!this->multimediaManager->init(url)) + { + return false; + } + } + this->setSettings(-1, -1, -1, -1, -1); + this->gui->setGuiFields(this->multimediaManager->getMPD()); + return true; +} + +bool DASHPlayer::settingsChanged (int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation) +{ + EnterCriticalSection(&this->monitorMutex); + bool settingsChanged = false; + if (this->currentSettings.videoRepresentation != videoRepresentation || + this->currentSettings.audioRepresentation != audioRepresentation || + this->currentSettings.videoAdaptationSet != videoAdaptationSet || + this->currentSettings.audioAdaptationSet != audioAdaptationSet || + this->currentSettings.period != period) + settingsChanged = true; + if (settingsChanged) + this->setSettings(period, videoAdaptationSet, videoRepresentation, audioAdaptationSet, audioRepresentation); + LeaveCriticalSection(&this->monitorMutex); + return settingsChanged; +} + +void DASHPlayer::setSettings(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation) +{ + this->currentSettings.period = period; + this->currentSettings.videoAdaptationSet = videoAdaptationSet; + this->currentSettings.videoRepresentation = videoRepresentation; + this->currentSettings.audioAdaptationSet = audioAdaptationSet; + this->currentSettings.audioRepresentation = audioRepresentation; +} + +bool DASHPlayer::downloadMPD(const QString &url, const QString &adaptationLogic, bool icn) +{ + + if (this->gui->getStop()) + { + QMetaObject::invokeMethod(this->gui->getRootObject(), "stopGraph"); + this->gui->setStop(false); + this->gui->setPause(false); + this->gui->setPlay(true); + this->offset = 0; + this->multimediaManager->setOffset(0); + this->gui->setOffset(0); + this->gui->setPlay(true); + this->gui->initVideoPlayer(); + this->icn = icn; + this->segment = 0; + std::string mUrl; + if(this->icn) + { + mUrl = this->icnPrefix + url.toStdString() + this->icnSuffix; + } + else + { + mUrl = this->httpPrefix + url.toStdString() + this->httpSuffix; + } + if (!this->onDownloadMPDPressed(mUrl)) + return false; + IPeriod *period = this->multimediaManager->getMPD()->GetPeriods().at(0); + IAdaptationSet *adaptation = period->GetAdaptationSets().at(0); + IRepresentation *representation = adaptation->GetRepresentation().at(0); + uint32_t duration = representation->GetSegmentList()->GetDuration(); + uint32_t timescale = representation->GetSegmentList()->GetTimescale(); + this->gui->setListSegmentSize(representation->GetSegmentList()->GetSegmentURLs().size()); + this->segmentDuration = 1.0*duration/(1.0*timescale) * 1000; + this->gui->setSegmentDuration(this->segmentDuration); + this->parametersAdaptation->segmentDuration = this->segmentDuration; + this->parametersAdaptation->segmentDuration = 1.0*duration/(1.0*timescale) * 1000; + this->onSettingsChanged(0,0,0,0,0); + int j =0; + std::string temp = adaptationLogic.toStdString(); + temp.erase(std::remove(temp.begin(), temp.end(), ' '), temp.end()); + int adaptationLogicID = (LogicType)1; //Always Lowest by default + for(j = 0; j < LogicType::Count; j++) + { + if(!strcmp(LogicType_string[j],temp.c_str())) + { + adaptationLogicID = (LogicType)j; + break; + } + } + if(j == LogicType::Count) + { + std::cout << "Could not find the ID of the adaptation logic asked, using " << LogicType_string[adaptationLogicID] << " instead.\n" << std::endl; + } + this->onStartButtonPressed(0,0,0,0,0, adaptationLogicID ); + this->multimediaManager->setLooping(this->repeat); + this->adaptationLogic = adaptationLogicID; + } else { + if (this->gui->getPause()) + { + this->gui->setPlay(true); + this->gui->setPause(false); + this->gui->getVideoPlayer()->play(); + this->gui->getVideoPlayer()->pause(false); + } + } + return true; +} + +void DASHPlayer::play() +{ + this->offset = 0; + this->gui->initVideoPlayer(); + this->multimediaManager->setOffset(0); + this->onSettingsChanged(0,0,0,0,0); + this->onStartButtonPressed(0,0,0,0,0, this->adaptationLogic); +} + +void DASHPlayer::repeatVideo(bool repeat) +{ + this->repeat = repeat; + this->multimediaManager->setLooping(repeat); + this->gui->setRepeat(repeat); +} + +void DASHPlayer::seekVideo(float value) { + this->multimediaManager->stop(); + this->seek = true; + this->gui->initVideoPlayer(); + this->segment = value*this->gui->getDurationMilliseconds()/this->segmentDuration; + this->offset = this->segment * this->segmentDuration; + this->gui->seekSegment(this->segment); + this->multimediaManager->setOffset(this->offset); + if (!(this->multimediaManager->setVideoAdaptationLogic((LogicType)this->adaptationLogic, this->parametersAdaptation))) + { + return; + } + this->multimediaManager->start(this->icn, 20, this->segment); + this->multimediaManager->setLooping(this->repeat); + +} + +void DASHPlayer::notifyQualityDownloading(uint32_t quality) +{ + this->qualityDownloading = quality/1000000; +} + +//WARNING FPS IS USED AS BUFFERLVL +void DASHPlayer::notifyStatistics(int segNum, uint32_t bitrate, int fps, uint32_t quality) +{ + if(quality == 240) + this->mStats[segNum] = std::make_tuple(bitrate, fps, 1); + else if(quality == 360) + this->mStats[segNum] = std::make_tuple(bitrate, fps, 3); + else if(quality == 720) + this->mStats[segNum] = std::make_tuple(bitrate, fps, 5); + else if(quality == 1080) + this->mStats[segNum] = std::make_tuple(bitrate, fps, 7); + else if(quality == 1440) + this->mStats[segNum] = std::make_tuple(bitrate, fps, 9); + else + this->mStats[segNum] = std::make_tuple(bitrate, fps, 11); +} + +void DASHPlayer::updateSlider(qint64 value) +{ + this->position = this->offset + (uint64_t)value; + if (this->position <= this->gui->getDurationMilliseconds()){ + this->segment = (this->offset + value)/this->segmentDuration; + this->gui->setAnaliticsValues(std::get<0>(this->mStats[segment])/1000000, + std::get<2>(this->mStats[segment]), + (uint32_t)this->qualityDownloading, + (double)this->multimediaManager->getBufferLevel()); + this->gui->getProgressBar()->setProperty("value", 1.0*(this->position)/(1.0*this->gui->getDurationMilliseconds())); + this->gui->getNowLabel()->setProperty("text", QVariant(msec2string(this->position).c_str())); + this->gui->pauseIfBuffering(this->offset + value); + } +} + +void DASHPlayer::initSlider() +{ + this->offset = 0; + this->gui->getProgressBar()->setProperty("value", 0.0); + this->gui->getNowLabel()->setProperty("text", QVariant("00:00:00")); + this->gui->getLifeLabel()->setProperty("text", QVariant("00:00:00")); + +} + +std::string DASHPlayer::msec2string(uint64_t milliseconds) +{ + uint64_t seconds = milliseconds/1000; + int32_t sec = seconds%60; + seconds = (seconds - sec)/60; + int32_t min = seconds%60; + int32_t hours = (seconds - min)/60; + char timeStamp[10]; + sprintf(timeStamp, "%02d:%02d:%02d", hours, min, sec); + return std::string(timeStamp); +} + +void DASHPlayer::onStopped() +{ + int posPlayer = this->position; + if (this->seek) + { + this->seek = false; + } + else + { + if(!this->gui->getStop()) + { + if (posPlayer <= 1000 || posPlayer > this->gui->getDurationMilliseconds() || (this->gui->getDurationMilliseconds()- posPlayer) <= 2000 ) + { + if (this->repeat) + { + this->gui->getStreamBuffer()->readFromNextBuffer(); + this->gui->startIfRepeat(); + this->offset = 0; + this->multimediaManager->setOffset(0); + } + else + { + this->gui->initVideoPlayer(); + this->gui->seekSegment(0); + this->gui->setStop(true); + this->gui->setPlay(false); + this->gui->setPause(false); + this->multimediaManager->stop(); + QMetaObject::invokeMethod(this->gui->getRootObject(), "pauseGraph"); + this->initSlider(); + QMetaObject::invokeMethod(this->gui->getRootObject(), "setStop"); + } + + } else { + qDebug("wrong position"); + } + } + else + { + this->gui->setStop(true); + this->gui->setPlay(false); + this->gui->setPause(false); + this->gui->initVideoPlayer(); + this->gui->seekSegment(0); + this->multimediaManager->stop(); + QMetaObject::invokeMethod(this->gui->getRootObject(), "pauseGraph"); + this->initSlider(); + } + } +} + +void DASHPlayer::pause() +{ + this->gui->setPlay(false); + this->gui->setPause(true); + this->gui->getVideoPlayer()->pause(true); +} + +void DASHPlayer::setConfig (Config *config) +{ + this->config = config; +} + +void DASHPlayer::manageGraph(QtAV::AVPlayer::State value) +{ + switch (value) + { + case QtAV::AVPlayer::State::PlayingState: + if (config->graph()) + { + QMetaObject::invokeMethod(this->gui->getRootObject(), "startGraph"); + } + break; + case QtAV::AVPlayer::State::StoppedState: + if (config->graph()) + { + QMetaObject::invokeMethod(this->gui->getRootObject(), "pauseGraph"); + } + break; + case QtAV::AVPlayer::State::PausedState: + if (config->graph()) { + QMetaObject::invokeMethod(this->gui->getRootObject(), "pauseGraph"); + } + break; + } +} + +void DASHPlayer::reloadParameters() +{ + this->beta = config->beta(); + this->drop = config->drop(); + this->icnPrefix = config->icnPrefix().toStdString(); + this->httpPrefix = config->httpPrefix().toStdString(); + this->icnSuffix = config->icnSuffix().toStdString(); + this->httpSuffix = config->httpSuffix().toStdString(); + this->alpha = config->alpha(); + this->repeat = config->repeat(); + this->parametersAdaptation = (struct AdaptationParameters *)malloc(sizeof(struct AdaptationParameters)); + this->parametersAdaptation->segmentBufferSize = config->segmentBufferSize(); + this->parametersAdaptation->segmentDuration = 2; + this->parametersAdaptation->Rate_Alpha = config->rateAlpha(); + this->parametersAdaptation->Bola_Alpha = config->bolaAlpha(); + this->parametersAdaptation->Bola_bufferTargetSeconds = config->bolaBufferTarget(); + this->parametersAdaptation->BufferBased_reservoirThreshold = config->bufferReservoirThreshold(); + this->parametersAdaptation->BufferBased_maxThreshold = config->bufferMaxThreshold(); + this->parametersAdaptation->Adaptech_Alpha = config->adaptechAlpha(); + this->parametersAdaptation->Adaptech_FirstThreshold = config->adaptechFirstThreshold(); + this->parametersAdaptation->Adaptech_SecondThreshold = config->adaptechSecondThreshold(); + this->parametersAdaptation->Adaptech_SwitchUpThreshold = config->adaptechSwitchUpMargin(); + this->parametersAdaptation->Adaptech_SlackParameter = config->adaptechSlackParameter(); + this->parametersAdaptation->BufferThreeThreshold_FirstThreshold = config->bufferThreeThresholdFirst(); + this->parametersAdaptation->BufferThreeThreshold_SecondThreshold = config->bufferThreeThresholdSecond(); + this->parametersAdaptation->BufferThreeThreshold_ThirdThreshold = config->bufferThreeThresholdThird(); + this->parametersAdaptation->Panda_Alpha = config->pandaParamAlpha(); + this->parametersAdaptation->Panda_Beta = config->pandaParamBeta(); + this->parametersAdaptation->Panda_Bmin = config->pandaParamBMin(); + this->parametersAdaptation->Panda_K = config->pandaParamK(); + this->parametersAdaptation->Panda_W = config->pandaParamW(); + this->parametersAdaptation->Panda_Epsilon = config->pandaParamEpsilon(); +} + +QString DASHPlayer::getLastPlayed() +{ + return config->lastPlayed(); +} + +void DASHPlayer::setLastPlayed(QString lastPlayed) +{ + config->setLastPlayed(lastPlayed); +} + +QString DASHPlayer::getAdaptationLogic() +{ + return config->adaptationLogic(); +} + +void DASHPlayer::setAdaptationLogic(QString adaptationLogic) +{ + config->setAdaptationLogic(adaptationLogic); +} + +bool DASHPlayer::getIcn() +{ + return config->icn(); +} + +void DASHPlayer::setIcn(bool icn) +{ + config->setIcn(icn); +} + +QString DASHPlayer::getIcnPrefix() +{ + return config->icnPrefix(); +} + +void DASHPlayer::setIcnPrefix(QString icnPrefix) +{ + config->setIcnPrefix(icnPrefix); +} + +QString DASHPlayer::getHttpPrefix() +{ + return config->httpPrefix(); +} + +void DASHPlayer::setHttpPrefix(QString httpPrefix) +{ + config->setHttpPrefix(httpPrefix); +} + +QString DASHPlayer::getIcnSuffix() +{ + return config->icnSuffix(); +} + +void DASHPlayer::setIcnSuffix(QString icnSuffix) +{ + config->setIcnSuffix(icnSuffix); +} + +QString DASHPlayer::getHttpSuffix() +{ + return config->httpSuffix(); +} + +void DASHPlayer::setHttpSuffix(QString httpSuffix) +{ + config->setHttpSuffix(httpSuffix); +} + +qreal DASHPlayer::getAlpha() +{ + return config->alpha(); +} + +void DASHPlayer::setAlpha(qreal alpha) +{ + config->setAlpha(alpha); +} + +qreal DASHPlayer::getSegmentBufferSize() +{ + return config->segmentBufferSize(); +} + +void DASHPlayer::setSegmentBufferSize(qreal segmentBufferSize) +{ + config->setSegmentBufferSize(segmentBufferSize); +} + +qreal DASHPlayer::getRateAlpha() +{ + return config->rateAlpha(); +} + +void DASHPlayer::setRateAlpha(qreal rateAlpha) +{ + config->setRateAlpha(rateAlpha); +} + +qreal DASHPlayer::getBufferReservoirThreshold() +{ + return config->bufferReservoirThreshold(); +} + +void DASHPlayer::setBufferReservoirThreshold(qreal bufferReservoirThreshold) +{ + config->setBufferReservoirThreshold(bufferReservoirThreshold); +} + +qreal DASHPlayer::getBufferMaxThreshold() +{ + return config->bufferMaxThreshold(); +} + +void DASHPlayer::setBufferMaxThreshold(qreal bufferMaxThreshold) +{ + config->setBufferMaxThreshold(bufferMaxThreshold); +} + +qreal DASHPlayer::getAdaptechFirstThreshold() +{ + return config->adaptechFirstThreshold(); +} + +void DASHPlayer::setAdaptechFirstThreshold(qreal adaptechFirstThreshold) +{ + config->setAdaptechFirstThreshold(adaptechFirstThreshold); +} + +qreal DASHPlayer::getAdaptechSecondThreshold() +{ + return config->adaptechSecondThreshold(); +} + +void DASHPlayer::setAdaptechSecondThreshold(qreal adaptechSecondThreshold) +{ + config->setAdaptechSecondThreshold(adaptechSecondThreshold); +} + +qreal DASHPlayer::getAdaptechSwitchUpMargin() +{ + return config->adaptechSwitchUpMargin(); +} + +void DASHPlayer::setAdaptechSwitchUpMargin(qreal adaptechSwitchUpMargin) +{ + config->setAdaptechSwitchUpMargin(adaptechSwitchUpMargin); +} + +qreal DASHPlayer::getAdaptechSlackParameter() +{ + return config->adaptechSlackParameter(); +} + +void DASHPlayer::setAdaptechSlackParameter(qreal adaptechSlackParameter) +{ + config->setAdaptechSlackParameter(adaptechSlackParameter); +} + +qreal DASHPlayer::getAdaptechAlpha() +{ + return config->adaptechAlpha(); +} + +void DASHPlayer::setAdaptechAlpha(qreal adaptechAlpha) +{ + config->setAdaptechAlpha(adaptechAlpha); +} + +qreal DASHPlayer::getBufferThreeThresholdFirst() +{ + return config->bufferThreeThresholdFirst(); +} + +void DASHPlayer::setBufferThreeThresholdFirst(qreal bufferThreeThresholdFirst) +{ + config->setBufferThreeThresholdFirst(bufferThreeThresholdFirst); +} + +qreal DASHPlayer::getBufferThreeThresholdSecond() +{ + return config->bufferThreeThresholdSecond(); +} + +void DASHPlayer::setBufferThreeThresholdSecond(qreal bufferThreeThresholdSecond) +{ + config->setBufferThreeThresholdSecond(bufferThreeThresholdSecond); +} + +qreal DASHPlayer::getBufferThreeThresholdThird() +{ + return config->bufferThreeThresholdThird(); +} + +void DASHPlayer::setBufferThreeThresholdThird(qreal bufferThreeThresholdThird) +{ + config->setBufferThreeThresholdThird(bufferThreeThresholdThird); +} + +qreal DASHPlayer::getPandaParamAlpha() +{ + return config->pandaParamAlpha(); +} + +void DASHPlayer::setPandaParamAlpha(qreal pandaParamAlpha) +{ + config->setPandaParamAlpha(pandaParamAlpha); +} + +qreal DASHPlayer::getPandaParamBeta() +{ + return config->pandaParamBeta(); +} + +void DASHPlayer::setPandaParamBeta(qreal pandaParamBeta) +{ + config->setPandaParamBeta(pandaParamBeta); +} + +qreal DASHPlayer::getPandaParamBMin() +{ + return config->pandaParamBMin(); +} + +void DASHPlayer::setPandaParamBMin(qreal pandaParamBMin) +{ + config->setPandaParamBMin(pandaParamBMin); +} + +qreal DASHPlayer::getPandaParamK() +{ + return config->pandaParamK(); +} + +void DASHPlayer::setPandaParamK(qreal pandaParamK) +{ + config->setPandaParamK(pandaParamK); +} + +qreal DASHPlayer::getPandaParamW() +{ + return config->pandaParamW(); +} + +void DASHPlayer::setPandaParamW(qreal pandaParamW) +{ + config->setPandaParamW(pandaParamW); +} + +qreal DASHPlayer::getPandaParamEpsilon() +{ + return config->pandaParamEpsilon(); +} + +void DASHPlayer::setPandaParamEpsilon(qreal pandaParamEpsilon) +{ + config->setPandaParamEpsilon(pandaParamEpsilon); +} + +qreal DASHPlayer::getBolaBufferTarget() +{ + return config->bolaBufferTarget(); +} + +void DASHPlayer::setBolaBufferTarget(qreal bolaBufferTarget) +{ + config->setBolaBufferTarget(bolaBufferTarget); +} + +qreal DASHPlayer::getBolaAlpha() +{ + return config->bolaAlpha(); +} + +void DASHPlayer::setBolaAlpha(qreal bolaAlpha) +{ + config->setBolaAlpha(bolaAlpha); +} + +bool DASHPlayer::getRepeat() +{ + return config->repeat(); +} + +void DASHPlayer::setRepeat(bool repeat) +{ + this->repeatVideo(repeat); + config->setRepeat(repeat); +} + +bool DASHPlayer::getGraph() +{ + return config->graph(); +} + +void DASHPlayer::setGraph (bool graph) +{ + config->setGraph(graph); + if (graph) { + if (this->gui->getPlay() && this->gui->getVideoPlayer()->isPlaying()) { + QMetaObject::invokeMethod(this->gui->getRootObject(), "startGraph"); + } + } else { + QMetaObject::invokeMethod(this->gui->getRootObject(), "stopGraph"); + } +} + +bool DASHPlayer::getFullScreen() +{ + return config->fullScreen(); +} + +void DASHPlayer::setFullScreen(bool fullScreen) +{ + config->setFullScreen(fullScreen); +} + +bool DASHPlayer::getStop() +{ + return this->gui->getStop(); +} + +bool DASHPlayer::getAutotune() +{ + return config->autotune(); +} + +void DASHPlayer::setAutotune(bool autotune) +{ + config->setAutotune(autotune); +} + +int DASHPlayer::getLifetime() +{ + return config->lifetime(); +} + +void DASHPlayer::setLifetime(int lifetime) +{ + config->setLifetime(lifetime); +} + +int DASHPlayer::getRetransmissions() +{ + return config->retransmissions(); +} + +void DASHPlayer::setRetransmissions(int retransmissions) +{ + config->setRetransmissions(retransmissions); +} + +qreal DASHPlayer::getBeta() +{ + return config->beta(); +} + +void DASHPlayer::setBeta(qreal beta) +{ + config->setBeta(beta); + this->multimediaManager->setBeta(beta); +} + +qreal DASHPlayer::getDrop() +{ + return config->drop(); +} + +void DASHPlayer::setDrop(qreal drop) +{ + config->setDrop(drop); + this->multimediaManager->setDrop(drop); +} + +qreal DASHPlayer::getBetaWifi() +{ + return config->betaWifi(); +} + +void DASHPlayer::setBetaWifi(qreal betaWifi) +{ + config->setBetaWifi(betaWifi); +} + +qreal DASHPlayer::getDropWifi() +{ + return config->dropWifi(); +} + +void DASHPlayer::setDropWifi(qreal dropWifi) +{ + config->setDropWifi(dropWifi); +} + +int DASHPlayer::getDelayWifi() +{ + return config->delayWifi(); +} + +void DASHPlayer::setDelayWifi(int delayWifi) +{ + config->setDelayWifi(delayWifi); +} + +qreal DASHPlayer::getBetaLte() +{ + return config->betaLte(); +} + +void DASHPlayer::setBetaLte(qreal betaLte) +{ + config->setBetaLte(betaLte); +} + +qreal DASHPlayer::getDropLte() +{ + return config->dropLte(); +} + +void DASHPlayer::setDropLte(qreal dropLte) +{ + config->setDropLte(dropLte); +} + +int DASHPlayer::getDelayLte() +{ + return config->delayLte(); +} + +void DASHPlayer::setDelayLte(int delayLte) +{ + config->setDelayLte(delayLte); +} + +int DASHPlayer::getBatchingParameter() +{ + return config->batchingParameter(); +} + +void DASHPlayer::setBatchingParameter(int batchingParameter) +{ + config->setBatchingParameter(batchingParameter); +} + +int DASHPlayer::getRateEstimator () +{ + return config->rateEstimator(); +} + +void DASHPlayer::setRateEstimator(int rateEstimator) +{ + config->setRateEstimator(rateEstimator); +} + +void DASHPlayer::error(const QtAV::AVError &e) +{ + qDebug("error in the player!"); + seekVideo(0); +} + + + diff --git a/UI/DASHPlayer.h b/UI/DASHPlayer.h new file mode 100644 index 00000000..8913a843 --- /dev/null +++ b/UI/DASHPlayer.h @@ -0,0 +1,207 @@ +/* + * DASHPlayer.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef DASHPLAYER_H_ +#define DASHPLAYER_H_ + +#include +#include +#include +#include "libdash.h" +#include "IDASHPlayerGuiObserver.h" +#include "../Managers/IMultimediaManagerObserver.h" +#include "../Managers/MultimediaManager.h" +#include "../Adaptation/IAdaptationLogic.h" +#include "../Buffer/IBufferObserver.h" +#include "../MPD/AdaptationSetHelper.h" +#include "../Common/Config.h" +#include +#include +#include +#include + +namespace viper +{ +struct settings_t +{ + int period; + int videoAdaptationSet; + int audioAdaptationSet; + int videoRepresentation; + int audioRepresentation; +}; + +class DASHPlayer : public IDASHPlayerGuiObserver, public managers::IMultimediaManagerObserver + +{ + Q_OBJECT + +public: + DASHPlayer(ViperGui& gui, Config *config); + virtual ~DASHPlayer(); + + virtual void onSettingsChanged(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation); + virtual void onStartButtonPressed(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation, int adaptationLogic); + virtual void stopButtonPressed(); + virtual void onPauseButtonPressed(); + virtual void onVideoBufferStateChanged(uint32_t fillstateInPercent); + virtual void onVideoSegmentBufferStateChanged(uint32_t fillstateInPercent); + virtual void onAudioBufferStateChanged(uint32_t fillstateInPercent); + virtual void onAudioSegmentBufferStateChanged(uint32_t fillstateInPercent); + virtual void onEOS(); + virtual void notifyStatistics(int, uint32_t, int, uint32_t); + virtual void notifyQualityDownloading(uint32_t); + virtual bool onDownloadMPDPressed(const std::string &url); + void setConfig(Config *config); + Q_INVOKABLE bool downloadMPD(const QString &url, const QString &adaptationLogic, bool icn); + Q_INVOKABLE void pause(); + Q_INVOKABLE void seekVideo(float value); + Q_INVOKABLE void repeatVideo(bool repeat); + Q_INVOKABLE void onStopButtonPressed(); + Q_INVOKABLE void play(); + Q_INVOKABLE void onStopped(); + Q_INVOKABLE QString getLastPlayed(); + Q_INVOKABLE void setLastPlayed(QString lastPlayed); + Q_INVOKABLE QString getAdaptationLogic(); + Q_INVOKABLE void setAdaptationLogic(QString adaptationLogic); + Q_INVOKABLE bool getIcn(); + Q_INVOKABLE void setIcn(bool icn); + Q_INVOKABLE QString getIcnPrefix(); + Q_INVOKABLE void setIcnPrefix(QString icnPrefix); + Q_INVOKABLE QString getHttpPrefix(); + Q_INVOKABLE void setHttpPrefix(QString httpPrefix); + Q_INVOKABLE QString getIcnSuffix(); + Q_INVOKABLE void setIcnSuffix(QString icnSuffix); + Q_INVOKABLE QString getHttpSuffix(); + Q_INVOKABLE void setHttpSuffix(QString httpSuffix); + Q_INVOKABLE qreal getAlpha(); + Q_INVOKABLE void setAlpha(qreal alpha); + Q_INVOKABLE qreal getSegmentBufferSize(); + Q_INVOKABLE void setSegmentBufferSize(qreal segmentBufferSize); + Q_INVOKABLE qreal getRateAlpha(); + Q_INVOKABLE void setRateAlpha(qreal rateAlpha); + Q_INVOKABLE qreal getBufferReservoirThreshold(); + Q_INVOKABLE void setBufferReservoirThreshold(qreal bufferReservoirThreshold); + Q_INVOKABLE qreal getBufferMaxThreshold(); + Q_INVOKABLE void setBufferMaxThreshold(qreal bufferMaxThreshold); + Q_INVOKABLE qreal getAdaptechFirstThreshold(); + Q_INVOKABLE void setAdaptechFirstThreshold(qreal adaptechFirstThreshold); + Q_INVOKABLE qreal getAdaptechSecondThreshold(); + Q_INVOKABLE void setAdaptechSecondThreshold(qreal adaptechSecondThreshold); + Q_INVOKABLE qreal getAdaptechSwitchUpMargin(); + Q_INVOKABLE void setAdaptechSwitchUpMargin(qreal adaptechSwitchUpMargin); + Q_INVOKABLE qreal getAdaptechSlackParameter(); + Q_INVOKABLE void setAdaptechSlackParameter(qreal adaptechSlackParameter); + Q_INVOKABLE qreal getAdaptechAlpha(); + Q_INVOKABLE void setAdaptechAlpha(qreal adaptechAlpha); + Q_INVOKABLE qreal getBufferThreeThresholdFirst(); + Q_INVOKABLE void setBufferThreeThresholdFirst(qreal bufferThreeThresholdFirst); + Q_INVOKABLE qreal getBufferThreeThresholdSecond(); + Q_INVOKABLE void setBufferThreeThresholdSecond(qreal bufferThreeThresholdSecond); + Q_INVOKABLE qreal getBufferThreeThresholdThird(); + Q_INVOKABLE void setBufferThreeThresholdThird(qreal bufferThreeThresholdThird); + Q_INVOKABLE qreal getPandaParamAlpha(); + Q_INVOKABLE void setPandaParamAlpha(qreal pandaParamAlpha); + Q_INVOKABLE qreal getPandaParamBeta(); + Q_INVOKABLE void setPandaParamBeta(qreal pandaParamBeta); + Q_INVOKABLE qreal getPandaParamBMin(); + Q_INVOKABLE void setPandaParamBMin(qreal pandaParamBMin); + Q_INVOKABLE qreal getPandaParamK(); + Q_INVOKABLE void setPandaParamK(qreal pandaParamK); + Q_INVOKABLE qreal getPandaParamW(); + Q_INVOKABLE void setPandaParamW(qreal pandaParamW); + Q_INVOKABLE qreal getPandaParamEpsilon(); + Q_INVOKABLE void setPandaParamEpsilon(qreal pandaParamEpsilon); + Q_INVOKABLE qreal getBolaBufferTarget(); + Q_INVOKABLE void setBolaBufferTarget(qreal bolaBufferTarget); + Q_INVOKABLE qreal getBolaAlpha(); + Q_INVOKABLE void setBolaAlpha(qreal bolaAlpha); + Q_INVOKABLE bool getRepeat(); + Q_INVOKABLE void setRepeat(bool repeat); + Q_INVOKABLE bool getGraph(); + Q_INVOKABLE void setGraph(bool graph); + Q_INVOKABLE bool getFullScreen(); + Q_INVOKABLE void setFullScreen(bool fullScreen); + Q_INVOKABLE void reloadParameters(); + Q_INVOKABLE bool getStop(); + Q_INVOKABLE void setAutotune(bool autoTune); + Q_INVOKABLE bool getAutotune(); + Q_INVOKABLE void setLifetime(int lifeTime); + Q_INVOKABLE int getLifetime(); + Q_INVOKABLE void setRetransmissions(int retranmsissions); + Q_INVOKABLE int getRetransmissions(); + Q_INVOKABLE void setBeta(qreal beta); + Q_INVOKABLE qreal getBeta(); + Q_INVOKABLE void setDrop(qreal drop); + Q_INVOKABLE qreal getDrop(); + Q_INVOKABLE void setBetaWifi(qreal betaWifi); + Q_INVOKABLE qreal getBetaWifi(); + Q_INVOKABLE void setDropWifi(qreal dropWifi); + Q_INVOKABLE qreal getDropWifi(); + Q_INVOKABLE void setDelayWifi(int delayWifi); + Q_INVOKABLE int getDelayWifi(); + Q_INVOKABLE void setBetaLte(qreal betaLte); + Q_INVOKABLE qreal getBetaLte(); + Q_INVOKABLE void setDropLte(qreal dropLte); + Q_INVOKABLE qreal getDropLte(); + Q_INVOKABLE void setDelayLte(int delayLte); + Q_INVOKABLE int getDelayLte(); + Q_INVOKABLE void setBatchingParameter(int batchingParameter); + Q_INVOKABLE int getBatchingParameter(); + Q_INVOKABLE void setRateEstimator(int rateEstimator); + Q_INVOKABLE int getRateEstimator(); + +private: + float gamma; + float beta; + float drop; + bool seek; + Config *config; + bool repeat; + float segmentDuration; + uint64_t offset; + uint64_t position; + int segment; + int adaptationLogic; + dash::mpd::IMPD *mpd; + ViperGui *gui = NULL; + viper::managers::MultimediaManager *multimediaManager; + settings_t currentSettings; + CRITICAL_SECTION monitorMutex; + const char *url; + bool icn; + std::string icnPrefix; + std::string httpPrefix; + std::string icnSuffix; + std::string httpSuffix; + double alpha; + struct libdash::framework::adaptation::AdaptationParameters *parametersAdaptation; + libdash::framework::adaptation::LogicType adaptLogic; + std::map> mStats; + int qualityDownloading; + bool settingsChanged(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation); + void setSettings(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation); + std::string msec2string(uint64_t milliseconds); + void initSlider(); + +signals: + void videoSegmentBufferFillStateChanged(int fillStateInPercent); + void videoBufferFillStateChanged(int fillStateInPercent); + void audioSegmentBufferFillStateChanged(int fillStateInPercent); + void audioBufferFillStateChanged(int fillStateInPercent); + +private Q_SLOTS: + void updateSlider(qint64 value); + void manageGraph(QtAV::AVPlayer::State state); + void error(const QtAV::AVError &e); +}; +} +#endif /* DASHPLAYER_H_ */ diff --git a/UI/DASHPlayerNoGUI.cpp b/UI/DASHPlayerNoGUI.cpp new file mode 100644 index 00000000..729621ef --- /dev/null +++ b/UI/DASHPlayerNoGUI.cpp @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DASHPlayerNoGUI.h" +#include + +using namespace libdash::framework::adaptation; +using namespace libdash::framework::mpd; +using namespace libdash::framework::buffer; +using namespace viper; +using namespace viper::managers; +using namespace dash::mpd; +using namespace std; + +DASHPlayerNoGUI::DASHPlayerNoGUI(int argc, char ** argv, pthread_cond_t *mainCond, bool nodecoding) : + mainCond (mainCond), + running (true), + noDecoding (nodecoding) +{ + InitializeCriticalSection(&this->monitorMutex); + + this->mpd = NULL; + this->url = NULL; + this->adaptLogic = LogicType::RateBased; + this->isICN = false; + this->alpha = -1; + this->graphData = NULL; + this->parameterAdaptation = (struct libdash::framework::adaptation::AdaptationParameters *)malloc(sizeof(struct libdash::framework::adaptation::AdaptationParameters)); + + this->parameterAdaptation->segmentDuration = 2.0; + this->parameterAdaptation->segmentBufferSize = 10; + + this->parameterAdaptation->Rate_Alpha = 0.8; + + this->parameterAdaptation->Bola_Alpha = 0.8; + this->parameterAdaptation->Bola_bufferTargetSeconds = 8.0; + + this->parameterAdaptation->BufferBased_reservoirThreshold = 25; + this->parameterAdaptation->BufferBased_maxThreshold = 75; + + this->parameterAdaptation->Adaptech_Alpha = 0.8; + this->parameterAdaptation->Adaptech_FirstThreshold = 25; + this->parameterAdaptation->Adaptech_SecondThreshold = 45; + this->parameterAdaptation->Adaptech_SwitchUpThreshold = 5; + this->parameterAdaptation->Adaptech_SlackParameter = 0.8; + + this->parameterAdaptation->BufferThreeThreshold_FirstThreshold = 25; + this->parameterAdaptation->BufferThreeThreshold_SecondThreshold = 50; + this->parameterAdaptation->BufferThreeThreshold_ThirdThreshold = 75; + + this->parameterAdaptation->Panda_Alpha = 0.2; + this->parameterAdaptation->Panda_Beta = 0.2; + this->parameterAdaptation->Panda_Bmin = 44; + this->parameterAdaptation->Panda_K = 0.14; + this->parameterAdaptation->Panda_W = 300000; + this->parameterAdaptation->Panda_Epsilon = 0.15; + + this->repeat = false; + this->parseArgs(argc, argv); + + this->multimediaManager = new MultimediaManager(NULL, this->parameterAdaptation->segmentBufferSize, "/tmp/", noDecoding); + this->multimediaManager->attachManagerObserver(this); + + if(this->url == NULL) + { + this->running = false; + pthread_cond_broadcast(mainCond); + return; + } + else + { + if(this->onDownloadMPDPressed(string(this->url).c_str())) + { + this->graphData = new GraphDataSource(NULL); + WebSocketService webSocketService; + webSocketService.setGraphDataSource(this->graphData); + webSocketService.start(); + this->onStartButtonPressed(0,0,0,0,0); + this->multimediaManager->setLooping(this->repeat); + } + else + { + this->running = false; + pthread_cond_broadcast(mainCond); + } + } +} + +DASHPlayerNoGUI::~DASHPlayerNoGUI() +{ + this->multimediaManager->stop(); + delete(this->multimediaManager); + if(this->graphData) + delete(this->graphData); + DeleteCriticalSection(&this->monitorMutex); +} + +void DASHPlayerNoGUI::onStartButtonPressed(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation) +{ + this->onSettingsChanged(period,videoAdaptationSet,videoRepresentation, audioAdaptationSet, audioRepresentation); + bool setOk = false; + + switch(adaptLogic) + { + case RateBased: + setOk = this->multimediaManager->setVideoAdaptationLogic((LogicType)adaptLogic, this->parameterAdaptation); + break; + case BufferBased: + setOk = this->multimediaManager->setVideoAdaptationLogic((LogicType)adaptLogic, this->parameterAdaptation); + break; + case BufferRateBased: + setOk = this->multimediaManager->setVideoAdaptationLogic((LogicType)adaptLogic, this->parameterAdaptation); + break; + case BufferBasedThreeThreshold: + setOk = this->multimediaManager->setVideoAdaptationLogic((LogicType)adaptLogic, this->parameterAdaptation); + break; + case Panda: + setOk = this->multimediaManager->setVideoAdaptationLogic((LogicType)adaptLogic, this->parameterAdaptation); + break; + case Bola: + setOk = this->multimediaManager->setVideoAdaptationLogic((LogicType)adaptLogic, this->parameterAdaptation); + break; + default: + setOk = this->multimediaManager->setVideoAdaptationLogic((LogicType)adaptLogic, this->parameterAdaptation); + break; + } + + if(!setOk) + { + return; + } + + Debug("DASH PLAYER: STARTING VIDEO\n"); + this->multimediaManager->start(this->isICN, this->alpha, 0); +} + +void DASHPlayerNoGUI::onStopButtonPressed () +{ + this->running = false; + pthread_cond_broadcast(mainCond); +} + +bool DASHPlayerNoGUI::isRunning () +{ + return this->running; +} + +void DASHPlayerNoGUI::notifyStatistics(int segNum, uint32_t bitrate, int fps, uint32_t quality) +{ +} + +void DASHPlayerNoGUI::notifyQualityDownloading(uint32_t quality) +{ + this->graphData->setAnaliticsValues(quality/1000000, 0, quality/1000000, 0); +} + +void DASHPlayerNoGUI::parseArgs(int argc, char ** argv) +{ + if(argc == 1) + { + helpMessage(argv[0]); + return; + } + else + { + int i = 0; + while(i < argc) + { + if(!strcmp(argv[i],"-u")) + { + this->url = argv[i+1]; + i++; + i++; + continue; + } + if(!strcmp(argv[i],"-n")) + { + this->isICN = true; + this->alpha = -1; + i++; + continue; + } + if(!strcmp(argv[i],"-loop")) + { + this->repeat = true; + i++; + continue; + } + if(!strcmp(argv[i],"-nr")) + { + this->isICN = true; + this->alpha = atof(argv[i+1]); + i++; + i++; + continue; + } + if(!strcmp(argv[i], "-b")) + { + this->adaptLogic = LogicType::BufferBased; + this->parameterAdaptation->BufferBased_reservoirThreshold = atoi(argv[i+1]); + this->parameterAdaptation->BufferBased_maxThreshold = atoi(argv[i+2]); + i = i + 3; + continue; + } + if(!strcmp(argv[i], "-br")) + { + this->adaptLogic = LogicType::BufferRateBased; + this->parameterAdaptation->Adaptech_Alpha = atof(argv[i+1]); + this->parameterAdaptation->Adaptech_FirstThreshold = atoi(argv[i+2]); + this->parameterAdaptation->Adaptech_SecondThreshold = atoi(argv[i+3]); + this->parameterAdaptation->Adaptech_SwitchUpThreshold = atoi(argv[i+4]); + i = i + 4; + continue; + } + if(!strcmp(argv[i], "-bola")) + { + this->adaptLogic = LogicType::Bola; + this->parameterAdaptation->Bola_Alpha = atof(argv[i+1]); + this->parameterAdaptation->Bola_bufferTargetSeconds = atoi(argv[i+2]); + i = i + 2; + continue; + } + if(!strcmp(argv[i], "-bt")) + { + this->adaptLogic = LogicType::BufferBasedThreeThreshold; + this->parameterAdaptation->BufferThreeThreshold_FirstThreshold = atoi(argv[i+1]); + this->parameterAdaptation->BufferThreeThreshold_SecondThreshold = atoi(argv[i+2]); + this->parameterAdaptation->BufferThreeThreshold_ThirdThreshold = atoi(argv[i+3]); + i = i + 3; + continue; + } + if(!strcmp(argv[i], "-r")) + { + this->adaptLogic = LogicType::RateBased; + this->parameterAdaptation->Rate_Alpha = atof(argv[i+1]); + i = i + 2; + continue; + } + if(!strcmp(argv[i], "-p")) + { + this->adaptLogic = LogicType::Panda; + this->parameterAdaptation->Panda_Alpha = atof(argv[i+1]); + i = i + 2; + continue; + } + + if(!strcmp(argv[i],"-a")) + { + int j =0; + for(j = 0; j < LogicType::Count; j++) + { + if(!strcmp(LogicType_string[j],argv[i+1])) + { + this->adaptLogic = (LogicType)j; + break; + } + } + if(j == LogicType::Count) + { + std::cout << "the different adaptation logics implemented are:" << std::endl; + for(j = 0;j < LogicType::Count; j++) + { + std::cout << LogicType_string[j] << std::endl; + } + std::cout << "By default, the " << LogicType_string[this->adaptLogic] << " logic is selected." << std::endl; + } + i++; + i++; + continue; + } + i++; + } + } +} + +void DASHPlayerNoGUI::helpMessage(char * name) +{ + std::cout << "Usage: " << name << " -u url -a adaptationLogic -n" << std::endl << \ + "-u:\tThe MPD's url" << std::endl << \ + "-a:\tThe adaptationLogic:" << std::endl << \ + "\t*AlwaysLowest" << std::endl << \ + "\t*RateBased(default)" << std::endl << \ + "\t*BufferBased" << std::endl << \ + "-n:\tFlag to use ICN instead of TCP" << std::endl << \ + "-nr alpha:\tFlag to use ICN instead of TCP and estimation at packet lvl" << std::endl << \ + "-b reservoirThreshold maxThreshold (both in %)" << std::endl << \ + "-br alpha reservoirThreshold maxThreshold" << std::endl << \ + "-r alpha" << std::endl; +} + +void DASHPlayerNoGUI::onSettingsChanged(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation) +{ + if(this->multimediaManager->getMPD() == NULL) + return; // TODO dialog or symbol that indicates that error + + if (!this->settingsChanged(period, videoAdaptationSet, videoRepresentation, audioAdaptationSet, audioRepresentation)) + return; + + IPeriod *currentPeriod = this->multimediaManager->getMPD()->GetPeriods().at(period); + std::vector videoAdaptationSets = AdaptationSetHelper::getVideoAdaptationSets(currentPeriod); + std::vector audioAdaptationSets = AdaptationSetHelper::getAudioAdaptationSets(currentPeriod); + + if (videoAdaptationSet >= 0 && videoRepresentation >= 0 && !videoAdaptationSets.empty()) + { + this->multimediaManager->setVideoQuality(currentPeriod, + videoAdaptationSets.at(videoAdaptationSet), + videoAdaptationSets.at(videoAdaptationSet)->GetRepresentation().at(videoRepresentation)); + } + else + { + this->multimediaManager->setVideoQuality(currentPeriod, NULL, NULL); + } + + if (audioAdaptationSet >= 0 && audioRepresentation >= 0 && !audioAdaptationSets.empty()) + { + this->multimediaManager->setAudioQuality(currentPeriod, + audioAdaptationSets.at(audioAdaptationSet), + audioAdaptationSets.at(audioAdaptationSet)->GetRepresentation().at(audioRepresentation)); + } + else + { + this->multimediaManager->setAudioQuality(currentPeriod, NULL, NULL); + } +} + +void DASHPlayerNoGUI::onEOS() +{ + this->onStopButtonPressed(); +} + +bool DASHPlayerNoGUI::onDownloadMPDPressed(const std::string &url) +{ + if(this->isICN) + { + if(!this->multimediaManager->initICN(url)) + { + std::cout << "Problem parsing the mpd. ICN is enabled." << std::endl; + return false; + } + } + else + { + if(!this->multimediaManager->init(url)) + { + std::cout << "Problem parsing the mpd. ICN is disabled." << std::endl; + return false; + } + } + return true; +} + +bool DASHPlayerNoGUI::settingsChanged(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation) +{ + return true; +} + +void DASHPlayerNoGUI::onVideoBufferStateChanged(uint32_t fillstateInPercent) +{ +} + +void DASHPlayerNoGUI::onVideoSegmentBufferStateChanged(uint32_t fillstateInPercent) +{ +} + +void DASHPlayerNoGUI::onAudioBufferStateChanged(uint32_t fillstateInPercent) +{ +} + +void DASHPlayerNoGUI::onAudioSegmentBufferStateChanged(uint32_t fillstateInPercent) +{ +} diff --git a/UI/DASHPlayerNoGUI.h b/UI/DASHPlayerNoGUI.h new file mode 100644 index 00000000..58ba52c4 --- /dev/null +++ b/UI/DASHPlayerNoGUI.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DASHPLAYERNOGUI_H_ +#define DASHPLAYERNOGUI_H_ + +#include +#include +#include "libdash.h" +#include "DASHPlayer.h" +#include "IDASHPlayerNoGuiObserver.h" +#include "../Managers/IMultimediaManagerObserver.h" +#include "../Managers/MultimediaManager.h" +#include "../Adaptation/IAdaptationLogic.h" +#include "../Buffer/IBufferObserver.h" +#include "../MPD/AdaptationSetHelper.h" +#include "UI/GraphDataSource.h" +#include "Websocket/WebSocketService.h" + +namespace viper +{ +class DASHPlayerNoGUI : public IDASHPlayerNoGuiObserver, public managers::IMultimediaManagerObserver +{ +public: + DASHPlayerNoGUI (int argc, char** argv, pthread_cond_t *mainCond, bool nodecoding); + virtual ~DASHPlayerNoGUI (); + void parseArgs(int argc, char ** argv); + void helpMessage(char *name); + virtual void onStartButtonPressed(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation); + virtual void onStopButtonPressed(); + virtual void onSettingsChanged(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation); + /* IMultimediaManagerObserver */ + virtual void onVideoBufferStateChanged(uint32_t fillstateInPercent); + virtual void onVideoSegmentBufferStateChanged(uint32_t fillstateInPercent); + virtual void onAudioBufferStateChanged(uint32_t fillstateInPercent); + virtual void onAudioSegmentBufferStateChanged(uint32_t fillstateInPercent); + virtual void onEOS(); + virtual void notifyStatistics(int, uint32_t, int, uint32_t); + virtual void notifyQualityDownloading(uint32_t); + virtual bool onDownloadMPDPressed(const std::string &url); + bool isRunning(); + +private: + dash::mpd::IMPD *mpd; + viper::managers::MultimediaManager *multimediaManager; + CRITICAL_SECTION monitorMutex; + char *url; + bool isICN; + libdash::framework::adaptation::LogicType adaptLogic; + pthread_cond_t *mainCond; + bool running; + struct libdash::framework::adaptation::AdaptationParameters *parameterAdaptation; + float segmentDuration; + int segmentBufferSize; + double alpha; + double rateAlpha; + double bolaAlpha; + double bolaBufferTargetSeconds; + int bufferBasedReservoirThreshold; + int bufferBasedMaxThreshold; + double adaptechAlpha; + int adaptechFirstThreshold; + int adaptechSecondThreshold; + int adaptechSwitchUpThreshold; + int bufferThreeThreshold_FirstThreshold; + int bufferThreeThreshold_SecondThreshold; + int bufferThreeThreshold_ThirdThreshold; + double pandaAlpha; + double pandaParam_Beta; + double pandaParam_Bmin; + double pandaParam_K; + double pandaParam_W; + double pandaParamEpsilon; + bool repeat; + GraphDataSource *graphData; + bool noDecoding; + bool settingsChanged(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation); + +}; +} +#endif /* DASHPLAYER_H_ */ diff --git a/UI/GraphDataSource.cpp b/UI/GraphDataSource.cpp new file mode 100644 index 00000000..b35b1869 --- /dev/null +++ b/UI/GraphDataSource.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GraphDataSource.h" +#include +#include +#include +#include +#include +#include +#include + +QT_CHARTS_USE_NAMESPACE + +Q_DECLARE_METATYPE(QAbstractSeries *) +Q_DECLARE_METATYPE(QAbstractAxis *) + +GraphDataSource::GraphDataSource(QQuickView *appViewer, QObject *parent) : + QObject(parent), + m_appViewer(appViewer) +{ + qRegisterMetaType(); + qRegisterMetaType(); + index = 0; + this->bitRate = -1; + this->fps = -1; + this->quality = -1; + this->bufferLevel = -1; +} + +void GraphDataSource::update(QAbstractSeries *bitRateSeries, QAbstractSeries *qualitySeries) +{ + QXYSeries *xyBitRateSeries = static_cast(bitRateSeries); + QXYSeries *xyQualitySeries = static_cast(qualitySeries); + if (bitRatePoints.size() == 1000) + bitRatePoints.remove(0); + if (qualityPoints.size() == 1000) + qualityPoints.remove(0); + + if(bitRate != -1) + { + bitRatePoints.append(QPointF(this->index, bitRate)); + xyBitRateSeries->replace(bitRatePoints); + } + + if(quality != -1) + { + qualityPoints.append(QPointF(this->index, quality)); + xyQualitySeries->replace(qualityPoints); + } + this->index++; +} + +void GraphDataSource::update(QAbstractSeries *bitRateSeries, QAbstractSeries *qualitySeries, QAbstractSeries *bufferLevelSeries) +{ + QXYSeries *xyBitRateSeries = static_cast(bitRateSeries); + QXYSeries *xyBufferLevelSeries = static_cast(bufferLevelSeries); + QXYSeries *xyQualitySeries = static_cast(qualitySeries); + if (bitRatePoints.size() == 1000) + bitRatePoints.remove(0); + if (bufferLevelPoints.size() == 1000) + bufferLevelPoints.remove(0); + if (qualityPoints.size() == 1000) + qualityPoints.remove(0); + if(bitRate != -1) + { + bitRatePoints.append(QPointF(this->index, bitRate)); + xyBitRateSeries->replace(bitRatePoints); + } + if(bufferLevel != -1) + { + bufferLevelPoints.append(QPointF(this->index, bufferLevel)); + xyBufferLevelSeries->replace(bufferLevelPoints); + } + if(quality != -1) + { + qualityPoints.append(QPointF(this->index, quality)); + xyQualitySeries->replace(qualityPoints); + } + + this->index++; + +} + +void GraphDataSource::clearData() +{ + bitRatePoints.clear(); + fpsPoints.clear(); + qualityPoints.clear(); + bufferLevelPoints.clear(); + + this->index = 0; + this->bitRate = -1; + this->fps = -1; + this->quality = -1; + this->bufferLevel = -1; +} + +void GraphDataSource::setAnaliticsValues(uint32_t bitrate, int fps, uint32_t quality, double bufferLevel) +{ + this->bitRate = quality; + this->fps = bufferLevel; + this->quality = bitrate; + this->bufferLevel = bufferLevel; +} + +uint32_t GraphDataSource::getBitRate() +{ + return this->bitRate; +} + +int GraphDataSource::getFps() +{ + return this->fps; +} + +uint32_t GraphDataSource::getQuality() +{ + return this->bitRate; +} + +double GraphDataSource::getBufferLevel() +{ + return this->bufferLevel; +} + +void GraphDataSource::resetGraphValues() +{ + this->bitRate = -1; + this->fps = -1; + this->quality = -1; + this->bufferLevel = -1; +} + diff --git a/UI/GraphDataSource.h b/UI/GraphDataSource.h new file mode 100644 index 00000000..d46a5730 --- /dev/null +++ b/UI/GraphDataSource.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GRAPHDATASOURCE_H +#define GRAPHDATASOURCE_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QQuickView; +QT_END_NAMESPACE + +QT_CHARTS_USE_NAMESPACE + +class GraphDataSource : public QObject +{ + Q_OBJECT +public: + explicit GraphDataSource(QQuickView *appViewer, QObject *parent = 0); + void setAnaliticsValues(uint32_t bitRate, int fps, uint32_t quality, double bufferLevel); + uint32_t getBitRate(); + int getFps(); + double getBufferLevel(); + uint32_t getQuality(); + + +Q_SIGNALS: + +public slots: + void clearData(); + void update(QAbstractSeries *bitRateSeries,QAbstractSeries *qualitySeries, QAbstractSeries *bufferLevelSeries); + void update(QAbstractSeries *bitRateSeries, QAbstractSeries *qualitySeries); + void resetGraphValues(); + +private: + QQuickView *m_appViewer; + QVector bitRatePoints; + QVector fpsPoints; + QVector qualityPoints; + QVector bufferLevelPoints; + long int index; + long int bitRate; + int fps; + int quality; + double bufferLevel; + +}; + +#endif // GRAPHDATASOURCE_H diff --git a/UI/IDASHPlayerGuiObserver.h b/UI/IDASHPlayerGuiObserver.h new file mode 100644 index 00000000..30d5022a --- /dev/null +++ b/UI/IDASHPlayerGuiObserver.h @@ -0,0 +1,35 @@ +/* + * IDASHPlayerGuiObserver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef IDASHPLAYERGUIOBSERVER_H_ +#define IDASHPLAYERGUIOBSERVER_H_ + +#include +#include +#include "ViperGui.h" + +namespace viper +{ +class IDASHPlayerGuiObserver : public QObject +{ + Q_OBJECT + +public: + virtual ~IDASHPlayerGuiObserver() {} + virtual void onSettingsChanged(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation) = 0; + virtual void onStartButtonPressed(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation, int adaptationLogic) = 0; + virtual void onStopButtonPressed() = 0; + virtual bool onDownloadMPDPressed(const std::string &url) = 0; + virtual void onPauseButtonPressed() = 0; + +}; +} +#endif /* IDASHPLAYERGUIOBSERVER_H_ */ diff --git a/UI/IDASHPlayerNoGuiObserver.h b/UI/IDASHPlayerNoGuiObserver.h new file mode 100644 index 00000000..6d3a870d --- /dev/null +++ b/UI/IDASHPlayerNoGuiObserver.h @@ -0,0 +1,29 @@ +/* + * IDASHPlayerNoGuiObserver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef IDASHPLAYERNOGUIOBSERVER_H_ +#define IDASHPLAYERNOGUIOBSERVER_H_ + +#include + +namespace viper +{ +class IDASHPlayerNoGuiObserver +{ +public: + virtual ~IDASHPlayerNoGuiObserver() {} + virtual void onSettingsChanged(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation) = 0; + virtual void onStartButtonPressed(int period, int videoAdaptationSet, int videoRepresentation, int audioAdaptationSet, int audioRepresentation) = 0; + virtual void onStopButtonPressed() = 0; + virtual bool onDownloadMPDPressed(const std::string &url) = 0; +}; +} +#endif /* IDASHPLAYERNOGUIOBSERVER_H_ */ diff --git a/UI/ViperGui.cpp b/UI/ViperGui.cpp new file mode 100644 index 00000000..11ba2e8e --- /dev/null +++ b/UI/ViperGui.cpp @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "ViperGui.h" +#include "IDASHPlayerGuiObserver.h" +#include "libdash.h" +#include +#include +#include +#include + +using namespace viper; +using namespace dash::mpd; +using namespace libdash::framework::mpd; + +ViperGui::ViperGui(QObject *parent) : + QObject (parent), + mpd (NULL) +{ + this->segment = 0; + this->bufferDuration = 0; + this->play = false; + this->pause = false; + this->stop = true; + this->offset = 0; + this->position = 0; + this->videoPlayer = qvariant_cast(parent->property("mediaObject")); + this->streamBuffer = new ViperBuffer(); + pthread_mutex_init(&(this->monitorMutex), NULL); + if (streamBuffer->open(QIODevice::ReadWrite)) { + this->videoPlayer->setIODevice(streamBuffer); + } +} + +ViperGui::~ViperGui() +{ + this->videoPlayer->stop(); + pthread_mutex_destroy(&(this->monitorMutex)); +} + +void ViperGui::setGuiFields(dash::mpd::IMPD* mpd) +{ + this->setPeriodComboBox(mpd); + if (mpd->GetPeriods().size() > 0) + { + IPeriod *period = mpd->GetPeriods().at(0); + this->setVideoAdaptationSetComboBox(period); + if (!AdaptationSetHelper::getVideoAdaptationSets(period).empty()) + { + IAdaptationSet *adaptationSet = AdaptationSetHelper::getVideoAdaptationSets(period).at(0); + this->setRepresentationComoboBox(adaptationSet); + } + if (!AdaptationSetHelper::getAudioAdaptationSets(period).empty()) + { + IAdaptationSet *adaptationSet = AdaptationSetHelper::getAudioAdaptationSets(period).at(0); + this->setRepresentationComoboBox(adaptationSet); + } + } + parse8601(mpd->GetMediaPresentationDuration()); + this->mpd = mpd; + this->lifeLabel->setProperty("text", QVariant(this->durationString.c_str())); +} + +void ViperGui::parse8601(std::string durationISO8601) +{ + int hours, min, sec, millisec; + sscanf(durationISO8601.c_str(), "PT%dH%dM%d.%dS", &hours, &min, &sec, &millisec); + this->durationMilliseconds = millisec + 1000*( + sec + 60 *(min + hours * 60)); + char timeStamp[10]; + sprintf(timeStamp, "%02d:%02d:%02d", hours, min, sec); + this->durationString.assign(timeStamp); +} + +void ViperGui::setRepresentationComoboBox(dash::mpd::IAdaptationSet *adaptationSet) +{ + std::vector represenations = adaptationSet->GetRepresentation(); + for(size_t i = 0; i < represenations.size(); i++) + { + IRepresentation *representation = represenations.at(i); + } +} +void ViperGui::setAdaptationSetComboBox(dash::mpd::IPeriod *period) +{ + std::vector adaptationSets = period->GetAdaptationSets(); + for(size_t i = 0; i < adaptationSets.size(); i++) + { + IAdaptationSet *adaptationSet = adaptationSets.at(i); + } +} + +void ViperGui::setAudioAdaptationSetComboBox(dash::mpd::IPeriod *period) +{ + std::vector adaptationSets = AdaptationSetHelper::getAudioAdaptationSets(period); + for(size_t i = 0; i < adaptationSets.size(); i++) + { + IAdaptationSet *adaptationSet = adaptationSets.at(i); + } +} + +void ViperGui::setVideoAdaptationSetComboBox(dash::mpd::IPeriod *period) +{ + std::vector adaptationSets = AdaptationSetHelper::getVideoAdaptationSets(period); + for(size_t i = 0; i < adaptationSets.size(); i++) + { + IAdaptationSet *adaptationSet = adaptationSets.at(i); + } +} +void ViperGui::setPeriodComboBox(dash::mpd::IMPD *mpd) +{ + std::vector periods = mpd->GetPeriods(); + for(size_t i = 0; i < periods.size(); i++) + { + IPeriod *period = periods.at(i); + } +} + +ViperBuffer* ViperGui::getStreamBuffer() +{ + return this->streamBuffer; +} + + +QtAV::AVPlayer* ViperGui::getVideoPlayer() +{ + return this->videoPlayer; +} + +void ViperGui::setOffset(int offset) +{ + this->offset = offset; +} + +void ViperGui::setPosition(qint64 position) +{ + pthread_mutex_lock(&(this->monitorMutex)); + this->position = position; + pthread_mutex_unlock(&(this->monitorMutex)); + +} + +void ViperGui::setLifeLabel(QObject *lifeLabel) +{ + this->lifeLabel = lifeLabel; +} + + +QObject* ViperGui::getLifeLabel() +{ + return this->lifeLabel; +} + +void ViperGui::setNowLabel(QObject *nowLabel) +{ + this->nowLabel = nowLabel; +} + +QObject* ViperGui::getNowLabel(){ + return this->nowLabel; +} + +void ViperGui::setPlayButton(QObject *playButton) +{ + this->playButton = playButton; +} + +void ViperGui::setProgressBar(QObject *progressBar) +{ + this->progressBar = progressBar; +} + +QObject* ViperGui::getProgressBar() +{ + return this->progressBar; +} + +void ViperGui::initSlider() +{ + this->offset = 0; + this->progressBar->setProperty("value", 0.0); + this->nowLabel->setProperty("text", QVariant("00:00:00")); + this->lifeLabel->setProperty("text", this->lifeLabel->property("text")); + +} + + +void ViperGui::seekSegment(int segment) +{ + pthread_mutex_lock(&(this->monitorMutex)); + this->segment = segment; + this->offset = this->segment * this->segmentDuration; + this->position = this->offset; + this->bufferDuration = this->segment * this->segmentDuration; + pthread_mutex_unlock(&(this->monitorMutex)); +} + +uint64_t ViperGui::getDurationMilliseconds() +{ + return this->durationMilliseconds; +} + +void ViperGui::initVideoPlayer() +{ + this->videoPlayer->stop(); + this->streamBuffer->clear(); +} + +void ViperGui::setVideoStream(managers::MultimediaStream *videoStream) +{ + this->videoStream = videoStream; +} + +void ViperGui::clearGraph() +{ + this->graphDataSource->clearData(); +} + +void ViperGui::setAnaliticsValues(uint32_t bitRate, int fps, uint32_t quality, double bufferSize) +{ + this->graphDataSource->setAnaliticsValues(bitRate, fps, quality, bufferSize); +} + +void ViperGui::setGraphDataSource(GraphDataSource *graphDataSource){ + this->graphDataSource = graphDataSource; +} + +void ViperGui::writeData(libdash::framework::input::MediaObject* media) +{ + this->streamBuffer->writeData(media); + pthread_mutex_lock(&(this->monitorMutex)); + this->segment = (this->segment + 1) % this->listSegmentSize; + if( this->segment > 0) + { + this->bufferDuration += this->segmentDuration; + + if(this->bufferDuration - this->position > 3000) + { + if (this->play == true) + { + this->videoPlayer->pause(false); + this->videoPlayer->play(); + QMetaObject::invokeMethod(this->rootObject, "unSetBuffering"); + } + } + } + else + { + this->bufferDuration += (this->durationMilliseconds - (this->segmentDuration * (this->listSegmentSize - 1))); + + if(this->bufferDuration - this->position >3000) + { + this->videoPlayer->pause(false); + this->videoPlayer->play(); + QMetaObject::invokeMethod(this->rootObject, "unSetBuffering"); + } + else + { + if (this->play == true) + { + this->videoPlayer->pause(false); + this->videoPlayer->play(); + QMetaObject::invokeMethod(this->rootObject, "unSetBuffering"); + } + } + this->streamBuffer->writeToNextBuffer(); + } + pthread_mutex_unlock(&(this->monitorMutex)); + +} + +void ViperGui::setListSegmentSize(int listSegmentSize) +{ + this->listSegmentSize = listSegmentSize; +} + +void ViperGui::setPlay(bool play) +{ + this->play = play; +} + +bool ViperGui::getPlay() +{ + return this->play; +} + +void ViperGui::setStop(bool stop) +{ + this->stop = stop; + this->segment = 0; + this->bufferDuration = 0; +} + +bool ViperGui::getStop() +{ + return this->stop; +} + +void ViperGui::setPause(bool pause) +{ + this->pause = pause; +} + +bool ViperGui::getPause() +{ + return this->pause; +} + +void ViperGui::setRepeat(bool repeat) +{ + this->repeat = repeat; +} + +void ViperGui::setSegmentDuration(qint64 segmentDuration) +{ + this->segmentDuration = segmentDuration; +} + +int64_t ViperGui::getSegmentDuration() +{ + return this->segmentDuration; +} + +int64_t ViperGui::getLastSegmentDuration() +{ + return this->lastSegmentDuration; +} + + +int64_t ViperGui::getBufferDuration() +{ + int64_t bufferDuration; + pthread_mutex_lock(&(this->monitorMutex)); + bufferDuration = this->bufferDuration - this->position; + pthread_mutex_unlock(&(this->monitorMutex)); + return bufferDuration; +} + +void ViperGui::pauseIfBuffering(qint64 position) +{ + pthread_mutex_lock(&(this->monitorMutex)); + this->position = position; + if (this->videoPlayer->isPlaying()) { + if (this->segment == 0) { + if (this->repeat == true && this->bufferDuration - this->position <= 3000) { + this->videoPlayer->pause(true); + QMetaObject::invokeMethod(this->rootObject, "setBuffering"); + } + } else + { + if ((this->bufferDuration - this->position) <= 3000) + { + this->videoPlayer->pause(true); + QMetaObject::invokeMethod(this->rootObject, "setBuffering"); + } + } + } + pthread_mutex_unlock(&(this->monitorMutex)); +} + +void ViperGui::startIfRepeat() +{ + if(this->play) { + pthread_mutex_lock(&(this->monitorMutex)); + this->videoPlayer->setStartPosition(0); + this->bufferDuration -= this->durationMilliseconds; + this->position = 0; + this->offset = 0; + this->videoPlayer->play(); + pthread_mutex_unlock(&(this->monitorMutex)); + } +} + +void ViperGui::setRootObject(QObject *rootObject) +{ + this->rootObject = rootObject; +} + +QObject* ViperGui::getRootObject() +{ + return this->rootObject; +} + +qint64 ViperGui::getPosition() +{ + + qint64 position; + pthread_mutex_lock(&(this->monitorMutex)); + position = this->position; + pthread_mutex_unlock(&(this->monitorMutex)); + return position; +} + +void ViperGui::resetGraphValues() { + this->graphDataSource->resetGraphValues(); +} + + + + + diff --git a/UI/ViperGui.h b/UI/ViperGui.h new file mode 100644 index 00000000..f4586753 --- /dev/null +++ b/UI/ViperGui.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIPER_H +#define VIPER_H + +#include +#include +#include +#include +#include +#include "Common/ViperBuffer.h" +#include +#include "libdash.h" +#include "../MPD/AdaptationSetHelper.h" +#include "../Common/QtQuick2ApplicationViewer.h" +#include "../Managers/MultimediaStream.h" +#include "GraphDataSource.h" + +#include + +namespace viper +{ +class IDASHPlayerGuiObserver; + +class ViperGui : public QObject +{ + Q_OBJECT + +public: + ViperGui(QObject *parent = 0); + virtual ~ViperGui (); + void seekSegment(int segment); + void writeData(libdash::framework::input::MediaObject* media); + void setListSegmentSize(int listSegmentSize); + ViperBuffer* getStreamBuffer(); + QtAV::AVPlayer* getVideoPlayer(); + void setGuiFields(dash::mpd::IMPD* mpd); + void setLifeLabel(QObject *lifeLabel); + void setNowLabel(QObject *nowLabel); + void setPlayButton(QObject *playButton); + void setProgressBar(QObject *progressBar); + void seekVideo(float value); + uint64_t getDurationMilliseconds(); + void initVideoPlayer(); + void setOffset(int offset); + void setPosition(qint64 position); + qint64 getPosition(); + void initSlider(); + void setVideoStream(managers::MultimediaStream *videoStream); + QObject* getLifeLabel(); + + QObject* getNowLabel(); + QObject* getProgressBar(); + void clearGraph(); + void setAnaliticsValues(uint32_t bitRate, int fps, uint32_t quality, double bufferSize); + void setGraphDataSource(GraphDataSource *graphDataSource); + void setPlay(bool play); + bool getPlay(); + void setStop(bool stop); + bool getStop(); + void setPause(bool pause); + bool getPause(); + void setRepeat(bool repeat); + void setSegmentDuration(qint64 segmentDuration); + void initPlayer(int segmentOffset); + void pauseIfBuffering(qint64 position); + void startIfRepeat(); + int64_t getLastSegmentDuration(); + int64_t getBufferDuration(); + int64_t getSegmentDuration(); + void setRootObject(QObject *rootObject); + QObject *getRootObject(); + void resetGraphValues(); + +private: + pthread_mutex_t monitorMutex; + GraphDataSource *graphDataSource; + managers::MultimediaStream *videoStream; + int64_t offset; + int64_t durationMilliseconds; + qint64 position; + std::string durationString; + QtAV::AVPlayer *videoPlayer; + ViperBuffer *streamBuffer; + std::map keyValues; + std::map keyIndices; + std::map> video; + std::map> audio; + QObject *lifeLabel; + QObject *nowLabel; + QObject *progressBar; + QObject *playButton; + std::vector observers; + dash::mpd::IMPD *mpd; + void setPeriodComboBox(dash::mpd::IMPD *mpd); + void setAdaptationSetComboBox(dash::mpd::IPeriod *period); + void setVideoAdaptationSetComboBox(dash::mpd::IPeriod *period); + void setAudioAdaptationSetComboBox(dash::mpd::IPeriod *period); + void setRepresentationComoboBox(dash::mpd::IAdaptationSet *adaptationSet); + void parse8601(std::string durationISO8601); + int listSegmentSize; + int segment; + int64_t bufferDuration; + int64_t segmentDuration; + int64_t lastSegmentDuration; + bool play; + bool stop; + bool pause; + bool repeat; + QObject *rootObject; + +}; +} + +#endif // VIPER_H diff --git a/Websocket/WebSocketService.cpp b/Websocket/WebSocketService.cpp new file mode 100644 index 00000000..b2fd4e16 --- /dev/null +++ b/Websocket/WebSocketService.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WebSocketService.h" +#include + +WebSocketService::WebSocketService() +{ + this->isRunning = false; +} +WebSocketService::~WebSocketService() +{ +} + +bool WebSocketService::start() +{ + if(this->isRunning) + return false; + + this->isRunning = true; + this->webSocketThread = createThreadPortable (listenWebsocket, this); + + if(this->webSocketThread == NULL) + { + this->isRunning = false; + return false; + } + return true; +} + +void WebSocketService::stop() +{ + if(!this->isRunning) + return; + + this->isRunning = false; + + if(this->webSocketThread != NULL) + { + JoinThread(this->webSocketThread); + destroyThreadPortable(this->webSocketThread); + } +} + +void* WebSocketService::listenWebsocket(void *webSocketServiceObject) +{ + WebSocketService *webSocketService = (WebSocketService *) webSocketServiceObject; + CommunicationProtocol protocol; + + protocol.setGraphDataSource(webSocketService->getGraphDataSource()); + + HandlerFunction handler = [&protocol](Server *s, websocketpp::connection_hdl hdl, message_ptr msg, const uint8_t *data, std::size_t size) { + + std::string command((char *) data, size); + + boost::trim(command); + + std::cout << command << std::endl; + + Query query = Query::fromJsonString(command); + + protocol.processQuery(s, hdl, msg, query ); + }; + + ConnectionPool connPool(8999); + connPool.startListeners(handler).processEvents(); + return NULL; +} + + +void WebSocketService::setGraphDataSource(GraphDataSource *graphDataSource) +{ + this->graphDataSource = graphDataSource; +} + + +GraphDataSource* WebSocketService::getGraphDataSource() +{ + return this->graphDataSource; +} + + diff --git a/Websocket/WebSocketService.h b/Websocket/WebSocketService.h new file mode 100644 index 00000000..9b084a24 --- /dev/null +++ b/Websocket/WebSocketService.h @@ -0,0 +1,42 @@ +/* + * DASHReceiver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef _WEBSOCKETSERVICE_H_ +#define _WEBSOCKETSERVICE_H_ + + +#include "../UI/GraphDataSource.h" +#include "../Portable/MultiThreading.h" +#include "connection-pool.h" +#include "query.h" +#include "communication-protocol.h" +#include + + +class WebSocketService +{ +public: + WebSocketService(); + virtual ~WebSocketService(); + void setGraphDataSource(GraphDataSource *graphDataSource); + GraphDataSource* getGraphDataSource(); + bool start(); + void stop(); +private: + GraphDataSource *graphDataSource; + static void* listenWebsocket(void *receiver); + bool isRunning; + THREAD_HANDLE webSocketThread; + +}; + + +#endif /* _WEBSOCKETSERVICE_H_ */ diff --git a/Websocket/communication-protocol.cpp b/Websocket/communication-protocol.cpp new file mode 100644 index 00000000..99bcfa90 --- /dev/null +++ b/Websocket/communication-protocol.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "websocket-server.h" +#include "communication-protocol.h" + + +/////////////////// +//Explanation: A query can select/update 2 objects: +// - Coordinates of a node +// - MCS value +// +// AllowedObjectName select the generic object to update/select. Not all the action are allowed on a generic object. +// Furthermore not al the parameters of an object can be selected/updated. +// So an object has a list of attributes that is possible to update/select. Also There are filter: a node can be updated just if +// matches a certain filter. A filter is a tuple [key operand value]. Not all the operand are allowed on a key. +////////////////// + +// TODO To improve. Right now just basic controls and actions + +std::set ProtocolDetails::AllowedObjectName = {"stats"}; +std::set ProtocolDetails::AllowedActions = { "select", "subscribe"}; +std::set ProtocolDetails::AllowedFields = {"quality", "rate", "all"}; + +std::function CommunicationProtocol::timerCallback; + +CommunicationProtocol::CommunicationProtocol(ProtocolVersion version) + : version(version) +{ +} + +bool +CommunicationProtocol::checkFields(const std::string &field) +{ + if (ProtocolDetails::AllowedFields.find(field) == ProtocolDetails::AllowedFields.end()) { + return false; + } + + return true; +} + +bool +CommunicationProtocol::checkAction(const std::string &action) +{ + if (ProtocolDetails::AllowedActions.find(action) != ProtocolDetails::AllowedActions.end()) { + return true; + } else { + return false; + } +} + +bool +CommunicationProtocol::checkObjectName(const std::string &objectName) +{ + if (ProtocolDetails::AllowedObjectName.find(objectName) != ProtocolDetails::AllowedObjectName.end()) { + return true; + } else { + return false; + } +} + +bool +CommunicationProtocol::checkFilter(const std::vector &filter) +{ + // TODO So far just the filter interface id == something is supported. + if (filter.size() < 3) { + std::cerr << "The format of the filter is not correct." << std::endl; + return false; + } + return true; +} + +bool +CommunicationProtocol::checkParameters(const std::string ¶meter) +{ + + if (ProtocolDetails::AllowedFields.find(parameter) == ProtocolDetails::AllowedFields.end()) { + std::cerr << "The parameter specified [" << parameter << "] cannot be selected (or does not exist)." << std::endl; + return false; + } + + return true; +} + +std::string +CommunicationProtocol::evaluateFilters(const std::list> &filters) +{ + // Switch between operand values + + for (auto filter : filters) { + if (filter[0] == "id" and filter[1] == "==") { + return filter[2]; + } else { + std::cerr << "So far just one filter is supported [==]" << std::endl; + } + } + return std::string(""); +} + +void +CommunicationProtocol::processQuery(Server *s, websocketpp::connection_hdl hdl, message_ptr msg, + Query query) +{ + std::string interface_id; + + std::cout << query.toJsonString() << std::endl; + + const std::string& action = query.getAction(); + + if (!checkAction(action)) { + std::cout << "Error in the action" << std::endl; + return ; + } + + const std::string& objectName = query.getObjectName(); + if (!checkObjectName(objectName)) { + std::cout << "Error in the object name" << std::endl; + return ; + } + + // See the filter for understanding which node update + const std::list> &filters = query.getFilter(); + + for (auto filter : filters) { + if (!checkFilter(filter)) { + std::cout << "Error in the filters." << std::endl; + } + } + + if (action == *ProtocolDetails::AllowedActions.find("select")) { + Query reply = makeReplyQuery(query); + reply.setLast(true); + try { + s->send(hdl, reply.toJsonString(), msg->get_opcode()); + } catch (...) { + std::cerr << "Impossible to connect to the remote endpoint. Closing connection..." << std::endl; + } + + } else if (action == *ProtocolDetails::AllowedActions.find("subscribe")) { + + subscribeTimer = std::shared_ptr(new boost::asio::deadline_timer(s->get_io_service(), + boost::posix_time::milliseconds(1000))); + timerCallback = [this, s, hdl, msg, query] + (const boost::system::error_code &ec) { + if (!ec) { + Query reply = this->makeReplyQuery(query); + + if (reply.isEmpty()) { + std::cerr << "Malformed reply. Closing connection.." << std::endl; + return; + } + + std::cout << "SENDING: " << reply.toJsonString() << std::endl; + + try { + s->send(hdl, reply.toJsonString(), msg->get_opcode()); + } catch (...) { + std::cerr << "Impossible to connect to the remote endpoint. Closing connection..." << std::endl; + return; + } + + subscribeTimer->expires_from_now(boost::posix_time::milliseconds(1000)); + subscribeTimer->async_wait(timerCallback); + } + }; + + subscribeTimer->async_wait(timerCallback); + } +} + +Query +CommunicationProtocol::makeReplyQuery(const Query &request) +{ + const std::list &fields = request.getFields(); + const std::string &objectName = request.getObjectName(); + const std::list> &filters = request.getFilter(); + + std::string station = evaluateFilters(filters); + + if (objectName == "stats") { + + + std::list> fltr; + std::list flds = {}; + + std::map prms; + + for (auto field : fields) { + if (field == "bufferLevel" || field == "*") { + prms["bufferLevel"] = QString::number(graphDataSource->getBufferLevel()).toStdString(); + } + + if (field == "bitRate" || field == "*") + { + prms["bitRate"] = QString::number(graphDataSource->getFps()).toStdString(); + } + + if (field == "quality" || field == "*") + { + uint32_t quality = graphDataSource->getQuality(); + if(quality == 2) + prms["quality"] = "LD"; + else if(quality == 6) + prms["quality"] = "SD"; + else if(quality == 9) + prms["quality"] = "HD"; + else if(quality == 12) + prms["quality"] = "FHD"; + else if(quality == 15) + prms["quality"] = "QHD"; + else if (quality == 18) + prms["quality"] = "UHD"; + else + prms["quality"] = "Unknown"; + } + } + + return Query(*ProtocolDetails::AllowedActions.find("select"), + *ProtocolDetails::AllowedObjectName.find("stats"), + fltr, prms, flds, false); + } + return Query(); + +} + +void +CommunicationProtocol::setGraphDataSource(GraphDataSource *graphDataSource) +{ + this->graphDataSource = graphDataSource; +} diff --git a/Websocket/communication-protocol.h b/Websocket/communication-protocol.h new file mode 100644 index 00000000..f3c488ef --- /dev/null +++ b/Websocket/communication-protocol.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATION_PROTOCOL_HPP +#define COMMUNICATION_PROTOCOL_HPP + +#include +#include +#include +#include "query.h" +#include "../UI/GraphDataSource.h" +#include "websocket-server.h" + +enum ProtocolVersion +{ + CONTROL_PROTOCOL_V1 = 1, + CONTROL_PROTOCOL_V2 = 2 +}; + +typedef struct ProtocolDetails { + static std::set AllowedObjectName; + static std::set AllowedFields; + static std::set AllowedActions; +// static std::list AllowedParameters; // Per action + static std::set AllowedFilters; + static std::set AllowedOperands; // Per filter +} ProtocolDetails; + +class CommunicationProtocol +{ +public: + CommunicationProtocol(ProtocolVersion version = ProtocolVersion::CONTROL_PROTOCOL_V1); + + bool + checkFilter(const std::vector &filter); + + bool + checkFields(const std::string &field); + + bool + checkObjectName(const std::string &objectName); + + bool + checkParameters(const std::string ¶meter); + + bool + checkAction(const std::string &action); + + std::string + evaluateFilters(const std::list> &filters); + + void + processQuery(Server *s, websocketpp::connection_hdl hdl, message_ptr msg, + Query query); + + Query + makeReplyQuery(const Query &request); + + void + setGraphDataSource(GraphDataSource *graphDataSource); + + static std::function timerCallback; + + +private: + GraphDataSource *graphDataSource; + ProtocolVersion version; + + std::shared_ptr subscribeTimer; +}; + + +#endif //COMMUNICATION_PROTOCOL_HPP diff --git a/Websocket/connection-pool.cpp b/Websocket/connection-pool.cpp new file mode 100644 index 00000000..2b274312 --- /dev/null +++ b/Websocket/connection-pool.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "connection-pool.h" + + +ConnectionPool::ConnectionPool(unsigned short websocketPort) +// : m_tcpControlServer(tcpPort) + : m_webSocketControlServer(websocketPort) +{ +} + +ConnectionPool& +ConnectionPool::startListeners(const HandlerFunction &handler) +{ + // m_tcpControlServer.setHandler(handler); + m_webSocketControlServer.setHandler(handler); + + // m_tcpConnectionHandle = std::async(std::launch::async, [this](){ + // + // this->m_tcpControlServer.start(); + // + // }); + + m_webSocketHandle = std::async(std::launch::async, [this](){ + + this->m_webSocketControlServer.start(); + + }); + + return * this; +} + +ConnectionPool& +ConnectionPool::processEvents() +{ + // m_tcpConnectionHandle.get(); + m_webSocketHandle.get(); +} + diff --git a/Websocket/connection-pool.h b/Websocket/connection-pool.h new file mode 100644 index 00000000..4326e292 --- /dev/null +++ b/Websocket/connection-pool.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CONTROLPROTOCOL_H +#define CONTROLPROTOCOL_H + +#include +//#include "tcp-server.h" +#include "websocket-server.h" +#include + +class ConnectionPool +{ +public: + ConnectionPool(unsigned short websocketPort = 23456); + + ConnectionPool& + startListeners(const HandlerFunction &handler); + + ConnectionPool& + processEvents(); + +private: + std::future m_webSocketHandle; + WebSocketServer m_webSocketControlServer; +}; + +#endif CONTROLPROTOCOL_H diff --git a/Websocket/json.h b/Websocket/json.h new file mode 100644 index 00000000..906e4f53 --- /dev/null +++ b/Websocket/json.h @@ -0,0 +1,10842 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 2.0.7 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include + +#ifndef NLOHMANN_JSON_HPP +#define NLOHMANN_JSON_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// exclude unsupported compilers +#if defined(__clang__) +#define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) +#if CLANG_VERSION < 30400 +#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" +#endif +#elif defined(__GNUC__) +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if GCC_VERSION < 40900 +#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" +#endif +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define JSON_DEPRECATED __declspec(deprecated) +#else +#define JSON_DEPRECATED +#endif + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ + + +/*! +@brief unnamed namespace with internal helper functions +@since version 1.0.0 +*/ +namespace +{ +/*! +@brief Helper to determine whether there's a key_type for T. + +Thus helper is used to tell associative containers apart from other containers +such as sequence containers. For instance, `std::map` passes the test as it +contains a `mapped_type`, whereas `std::vector` fails the test. + +@sa http://stackoverflow.com/a/7728728/266378 +@since version 1.0.0, overworked in version 2.0.6 +*/ +template +struct has_mapped_type +{ +private: + template + static int detect(U&&); + + static void detect(...); +public: + static constexpr bool value = + std::is_integral()))>::value; +}; + +/*! +@brief helper class to create locales with decimal point + +This struct is used a default locale during the JSON serialization. JSON +requires the decimal point to be `.`, so this function overloads the +`do_decimal_point()` function to return `.`. This function is called by +float-to-string conversions to retrieve the decimal separator between integer +and fractional parts. + +@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315 +@since version 2.0.0 +*/ +struct DecimalSeparator : std::numpunct +{ + char do_decimal_point() const + { + return '.'; + } +}; + +} + +/*! +@brief a class to store JSON values + +@tparam ObjectType type for JSON objects (`std::map` by default; will be used +in @ref object_t) +@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used +in @ref array_t) +@tparam StringType type for JSON strings and object keys (`std::string` by +default; will be used in @ref string_t) +@tparam BooleanType type for JSON booleans (`bool` by default; will be used +in @ref boolean_t) +@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by +default; will be used in @ref number_integer_t) +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) +@tparam NumberFloatType type for JSON floating-point numbers (`double` by +default; will be used in @ref number_float_t) +@tparam AllocatorType type of the allocator to use (`std::allocator` by +default) + +@requirement The class satisfies the following concept requirements: +- Basic + - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): + JSON values can be default constructed. The result will be a JSON null value. + - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): + A JSON value can be constructed from an rvalue argument. + - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): + A JSON value can be copy-constructed from an lvalue expression. + - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): + A JSON value van be assigned from an rvalue argument. + - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): + A JSON value can be copy-assigned from an lvalue expression. + - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): + JSON values can be destructed. +- Layout + - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): + JSON values have + [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + All non-static data members are private and standard layout types, the class + has no virtual functions or (virtual) base classes. +- Library-wide + - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): + JSON values can be compared with `==`, see @ref + operator==(const_reference,const_reference). + - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): + JSON values can be compared with `<`, see @ref + operator<(const_reference,const_reference). + - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): + Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of + other compatible types, using unqualified function call @ref swap(). + - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): + JSON values can be compared against `std::nullptr_t` objects which are used + to model the `null` value. +- Container + - [Container](http://en.cppreference.com/w/cpp/concept/Container): + JSON values can be used like STL containers and provide iterator access. + - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); + JSON values can be used like STL containers and provide reverse iterator + access. + +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@internal +@note ObjectType trick from http://stackoverflow.com/a/9860911 +@endinternal + +@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange +Format](http://rfc7159.net/rfc7159) + +@since version 1.0.0 + +@nosubgrouping +*/ +template < + template class ObjectType = std::map, + template class ArrayType = std::vector, + class StringType = std::string, + class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator + > +class basic_json +{ +private: + /// workaround type for MSVC + using basic_json_t = basic_json; + +public: + // forward declarations + template class json_reverse_iterator; + class json_pointer; + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + class iterator; + /// a const iterator for a basic_json container + class const_iterator; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + + /*! + @brief returns the allocator associated with the container + */ + static allocator_type get_allocator() + { + return allocator_type(); + } + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + + /*! + @brief a type for an object + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: + > An object is an unordered collection of zero or more name/value pairs, + > where a name is a string and a value is a string, number, boolean, null, + > object, or array. + + To store objects in C++, a type is defined by the template parameters + described below. + + @tparam ObjectType the container to store objects (e.g., `std::map` or + `std::unordered_map`) + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements + inside the container. + @tparam AllocatorType the allocator to use for objects (e.g., + `std::allocator`) + + #### Default type + + With the default values for @a ObjectType (`std::map`), @a StringType + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: + + @code {.cpp} + std::map< + std::string, // key_type + basic_json, // value_type + std::less, // key_compare + std::allocator> // allocator_type + > + @endcode + + #### Behavior + + The choice of @a object_t influences the behavior of the JSON class. With + the default type, objects have the following behavior: + + - When all names are unique, objects will be interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. + - When the names within an object are not unique, later stored name/value + pairs overwrite previously stored name/value pairs, leaving the used + names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will + be treated as equal and both stored as `{"key": 1}`. + - Internally, name/value pairs are stored in lexicographical order of the + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. + - When comparing objects, the order of the name/value pairs is irrelevant. + This makes objects interoperable in the sense that they will not be + affected by these differences. For instance, `{"b": 1, "a": 2}` and + `{"a": 2, "b": 1}` will be treated as equal. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the object's limit of nesting is not constraint explicitly. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. + + #### Storage + + Objects are stored as pointers in a @ref basic_json type. That is, for any + access to object values, a pointer of type `object_t*` must be + dereferenced. + + @sa @ref array_t -- type for an array value + + @since version 1.0.0 + + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC + 7159](http://rfc7159.net/rfc7159), because any order implements the + specified "unordered" nature of JSON objects. + */ + using object_t = ObjectType, + AllocatorType>>; + + /*! + @brief a type for an array + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: + > An array is an ordered sequence of zero or more values. + + To store objects in C++, a type is defined by the template parameters + explained below. + + @tparam ArrayType container type to store arrays (e.g., `std::vector` or + `std::list`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + + #### Default type + + With the default values for @a ArrayType (`std::vector`) and @a + AllocatorType (`std::allocator`), the default value for @a array_t is: + + @code {.cpp} + std::vector< + basic_json, // value_type + std::allocator // allocator_type + > + @endcode + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the array's limit of nesting is not constraint explicitly. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. + + #### Storage + + Arrays are stored as pointers in a @ref basic_json type. That is, for any + access to array values, a pointer of type `array_t*` must be dereferenced. + + @sa @ref object_t -- type for an object value + + @since version 1.0.0 + */ + using array_t = ArrayType>; + + /*! + @brief a type for a string + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: + > A string is a sequence of zero or more Unicode characters. + + To store objects in C++, a type is defined by the template parameter + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. + + @tparam StringType the container to store strings (e.g., `std::string`). + Note this container is used for keys/names in objects, see @ref object_t. + + #### Default type + + With the default values for @a StringType (`std::string`), the default + value for @a string_t is: + + @code {.cpp} + std::string + @endcode + + #### String comparison + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > Software implementations are typically required to test names of object + > members for equality. Implementations that transform the textual + > representation into sequences of Unicode code units and then perform the + > comparison numerically, code unit by code unit, are interoperable in the + > sense that implementations will agree in all cases on equality or + > inequality of two strings. For example, implementations that compare + > strings with escaped characters unconverted may incorrectly find that + > `"a\\b"` and `"a\u005Cb"` are not equal. + + This implementation is interoperable as it does compare strings code unit + by code unit. + + #### Storage + + String values are stored as pointers in a @ref basic_json type. That is, + for any access to string values, a pointer of type `string_t*` must be + dereferenced. + + @since version 1.0.0 + */ + using string_t = StringType; + + /*! + @brief a type for a boolean + + [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a + type which differentiates the two literals `true` and `false`. + + To store objects in C++, a type is defined by the template parameter @a + BooleanType which chooses the type to use. + + #### Default type + + With the default values for @a BooleanType (`bool`), the default value for + @a boolean_t is: + + @code {.cpp} + bool + @endcode + + #### Storage + + Boolean values are stored directly inside a @ref basic_json type. + + @since version 1.0.0 + */ + using boolean_t = BooleanType; + + /*! + @brief a type for a number (integer) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store integer numbers in C++, a type is defined by the template + parameter @a NumberIntegerType which chooses the type to use. + + #### Default type + + With the default values for @a NumberIntegerType (`int64_t`), the default + value for @a number_integer_t is: + + @code {.cpp} + int64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `9223372036854775807` (INT64_MAX) and the minimal integer number + that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange of the exactly supported range [INT64_MIN, + INT64_MAX], this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_integer_t = NumberIntegerType; + + /*! + @brief a type for a number (unsigned) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + + /*! + @brief a type for a number (floating-point) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store floating-point numbers in C++, a type is defined by the template + parameter @a NumberFloatType which chooses the type to use. + + #### Default type + + With the default values for @a NumberFloatType (`double`), the default + value for @a number_float_t is: + + @code {.cpp} + double + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ + floating-point literal `01.2` will be serialized to `1.2`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > This specification allows implementations to set limits on the range and + > precision of numbers accepted. Since software that implements IEEE + > 754-2008 binary64 (double precision) numbers is generally available and + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected + > precision. + + This implementation does exactly follow this approach, as it uses double + precision floating-point numbers. Note values smaller than + `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` + will be stored as NaN internally and be serialized to `null`. + + #### Storage + + Floating-point number values are stored directly inside a @ref basic_json + type. + + @sa @ref number_integer_t -- type for number values (integer) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_float_t = NumberFloatType; + + /// @} + + + /////////////////////////// + // JSON type enumeration // + /////////////////////////// + + /*! + @brief the JSON type enumeration + + This enumeration collects the different JSON types. It is internally used + to distinguish the stored values, and the functions @ref is_null(), @ref + is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref + is_number() (with @ref is_number_integer(), @ref is_number_unsigned(), and + @ref is_number_float()), @ref is_discarded(), @ref is_primitive(), and + @ref is_structured() rely on it. + + @note There are three enumeration entries (number_integer, + number_unsigned, and number_float), because the library distinguishes + these three types for numbers: @ref number_unsigned_t is used for unsigned + integers, @ref number_integer_t is used for signed integers, and @ref + number_float_t is used for floating-point numbers or to approximate + integers which do not fit in the limits of their respective type. + + @sa @ref basic_json(const value_t value_type) -- create a JSON value with + the default value for a given type + + @since version 1.0.0 + */ + enum class value_t : uint8_t + { + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + discarded ///< discarded by the the parser callback function + }; + + +private: + + /// helper for exception-safe object creation + template + static T* create(Args&& ... args) + { + AllocatorType alloc; + auto deleter = [&](T * object) + { + alloc.deallocate(object, 1); + }; + std::unique_ptr object(alloc.allocate(1), deleter); + alloc.construct(object.get(), std::forward(args)...); + assert(object.get() != nullptr); + return object.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::boolean: + { + boolean = boolean_t(false); + break; + } + + case value_t::number_integer: + { + number_integer = number_integer_t(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } + + case value_t::number_float: + { + number_float = number_float_t(0.0); + break; + } + + default: + { + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) + { + string = create(value); + } + + /// constructor for objects + json_value(const object_t& value) + { + object = create(value); + } + + /// constructor for arrays + json_value(const array_t& value) + { + array = create(value); + } + }; + + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + */ + void assert_invariant() const + { + assert(m_type != value_t::object or m_value.object != nullptr); + assert(m_type != value_t::array or m_value.array != nullptr); + assert(m_type != value_t::string or m_value.string != nullptr); + } + +public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /*! + @brief JSON callback events + + This enumeration lists the parser events that can trigger calling a + callback function of type @ref parser_callback_t during parsing. + + @image html callback_events.png "Example when certain parse events are triggered" + + @since version 1.0.0 + */ + enum class parse_event_t : uint8_t + { + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value + }; + + /*! + @brief per-element parser callback type + + With a parser callback function, the result of parsing a JSON text can be + influenced. When passed to @ref parse(std::istream&, const + parser_callback_t) or @ref parse(const char*, const parser_callback_t), + it is called on certain events (passed as @ref parse_event_t via parameter + @a event) with a set recursion depth @a depth and context JSON value + @a parsed. The return value of the callback function is a boolean + indicating whether the element that emitted the callback shall be kept or + not. + + We distinguish six scenarios (determined by the event type) in which the + callback function can be called. The following table describes the values + of the parameters @a depth, @a event, and @a parsed. + + parameter @a event | description | parameter @a depth | parameter @a parsed + ------------------ | ----------- | ------------------ | ------------------- + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded + parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key + parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded + parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array + parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + Discarding a value (i.e., returning `false`) has different effects + depending on the context in which function was called: + + - Discarded values in structured types are skipped. That is, the parser + will behave as if the discarded value was never read. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. + + @param[in] depth the depth of the recursion during parsing + + @param[in] event an event of type parse_event_t indicating the context in + the callback function has been called + + @param[in,out] parsed the current intermediate parse result; note that + writing to this value has no effect for parse_event_t::key events + + @return Whether the JSON value which called the function during parsing + should be kept (`true`) or not (`false`). In the latter case, it is either + skipped completely or replaced by an empty discarded object. + + @sa @ref parse(std::istream&, parser_callback_t) or + @ref parse(const char*, parser_callback_t) for examples + + @since version 1.0.0 + */ + using parser_callback_t = std::function; + + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /*! + @brief create an empty value with a given type + + Create an empty JSON value with a given type. The value will be default + initialized with an empty value which depends on the type: + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + + @param[in] value_type the type of the value to create + + @complexity Constant. + + @throw std::bad_alloc if allocation for object, array, or string value + fails + + @liveexample{The following code shows the constructor for different @ref + value_t values,basic_json__value_t} + + @sa @ref basic_json(std::nullptr_t) -- create a `null` value + @sa @ref basic_json(boolean_t value) -- create a boolean value + @sa @ref basic_json(const string_t&) -- create a string value + @sa @ref basic_json(const object_t&) -- create a object value + @sa @ref basic_json(const array_t&) -- create a array value + @sa @ref basic_json(const number_float_t) -- create a number + (floating-point) value + @sa @ref basic_json(const number_integer_t) -- create a number (integer) + value + @sa @ref basic_json(const number_unsigned_t) -- create a number (unsigned) + value + + @since version 1.0.0 + */ + basic_json(const value_t value_type) + : m_type(value_type), m_value(value_type) + { + assert_invariant(); + } + + /*! + @brief create a null object + + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} + + @since version 1.0.0 + */ + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) + { + assert_invariant(); + } + + /*! + @brief create an object (explicit) + + Create an object JSON value with a given content. + + @param[in] val a value for the object + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for object value fails + + @liveexample{The following code shows the constructor with an @ref + object_t parameter.,basic_json__object_t} + + @sa @ref basic_json(const CompatibleObjectType&) -- create an object value + from a compatible STL container + + @since version 1.0.0 + */ + basic_json(const object_t& val) + : m_type(value_t::object), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create an object (implicit) + + Create an object JSON value with a given content. This constructor allows + any type @a CompatibleObjectType that can be used to construct values of + type @ref object_t. + + @tparam CompatibleObjectType An object type whose `key_type` and + `value_type` is compatible to @ref object_t. Examples include `std::map`, + `std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with + a `key_type` of `std::string`, and a `value_type` from which a @ref + basic_json value can be constructed. + + @param[in] val a value for the object + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for object value fails + + @liveexample{The following code shows the constructor with several + compatible object type parameters.,basic_json__CompatibleObjectType} + + @sa @ref basic_json(const object_t&) -- create an object value + + @since version 1.0.0 + */ + template::value and + std::is_constructible::value, int>::type = 0> + basic_json(const CompatibleObjectType& val) + : m_type(value_t::object) + { + using std::begin; + using std::end; + m_value.object = create(begin(val), end(val)); + assert_invariant(); + } + + /*! + @brief create an array (explicit) + + Create an array JSON value with a given content. + + @param[in] val a value for the array + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for array value fails + + @liveexample{The following code shows the constructor with an @ref array_t + parameter.,basic_json__array_t} + + @sa @ref basic_json(const CompatibleArrayType&) -- create an array value + from a compatible STL containers + + @since version 1.0.0 + */ + basic_json(const array_t& val) + : m_type(value_t::array), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create an array (implicit) + + Create an array JSON value with a given content. This constructor allows + any type @a CompatibleArrayType that can be used to construct values of + type @ref array_t. + + @tparam CompatibleArrayType An object type whose `value_type` is + compatible to @ref array_t. Examples include `std::vector`, `std::deque`, + `std::list`, `std::forward_list`, `std::array`, `std::set`, + `std::unordered_set`, `std::multiset`, and `unordered_multiset` with a + `value_type` from which a @ref basic_json value can be constructed. + + @param[in] val a value for the array + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for array value fails + + @liveexample{The following code shows the constructor with several + compatible array type parameters.,basic_json__CompatibleArrayType} + + @sa @ref basic_json(const array_t&) -- create an array value + + @since version 1.0.0 + */ + template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + std::is_constructible::value, int>::type = 0> + basic_json(const CompatibleArrayType& val) + : m_type(value_t::array) + { + using std::begin; + using std::end; + m_value.array = create(begin(val), end(val)); + assert_invariant(); + } + + /*! + @brief create a string (explicit) + + Create an string JSON value with a given content. + + @param[in] val a value for the string + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for string value fails + + @liveexample{The following code shows the constructor with an @ref + string_t parameter.,basic_json__string_t} + + @sa @ref basic_json(const typename string_t::value_type*) -- create a + string value from a character pointer + @sa @ref basic_json(const CompatibleStringType&) -- create a string value + from a compatible string container + + @since version 1.0.0 + */ + basic_json(const string_t& val) + : m_type(value_t::string), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create a string (explicit) + + Create a string JSON value with a given content. + + @param[in] val a literal value for the string + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for string value fails + + @liveexample{The following code shows the constructor with string literal + parameter.,basic_json__string_t_value_type} + + @sa @ref basic_json(const string_t&) -- create a string value + @sa @ref basic_json(const CompatibleStringType&) -- create a string value + from a compatible string container + + @since version 1.0.0 + */ + basic_json(const typename string_t::value_type* val) + : basic_json(string_t(val)) + { + assert_invariant(); + } + + /*! + @brief create a string (implicit) + + Create a string JSON value with a given content. + + @param[in] val a value for the string + + @tparam CompatibleStringType an string type which is compatible to @ref + string_t, for instance `std::string`. + + @complexity Linear in the size of the passed @a val. + + @throw std::bad_alloc if allocation for string value fails + + @liveexample{The following code shows the construction of a string value + from a compatible type.,basic_json__CompatibleStringType} + + @sa @ref basic_json(const string_t&) -- create a string value + @sa @ref basic_json(const typename string_t::value_type*) -- create a + string value from a character pointer + + @since version 1.0.0 + */ + template::value, int>::type = 0> + basic_json(const CompatibleStringType& val) + : basic_json(string_t(val)) + { + assert_invariant(); + } + + /*! + @brief create a boolean (explicit) + + Creates a JSON boolean type from a given value. + + @param[in] val a boolean value to store + + @complexity Constant. + + @liveexample{The example below demonstrates boolean + values.,basic_json__boolean_t} + + @since version 1.0.0 + */ + basic_json(boolean_t val) noexcept + : m_type(value_t::boolean), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create an integer number (explicit) + + Create an integer number JSON value with a given content. + + @tparam T A helper type to remove this function via SFINAE in case @ref + number_integer_t is the same as `int`. In this case, this constructor + would have the same signature as @ref basic_json(const int value). Note + the helper type @a T is not visible in this constructor's interface. + + @param[in] val an integer to create a JSON number from + + @complexity Constant. + + @liveexample{The example below shows the construction of an integer + number value.,basic_json__number_integer_t} + + @sa @ref basic_json(const int) -- create a number value (integer) + @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number + value (integer) from a compatible number type + + @since version 1.0.0 + */ + template::value) and + std::is_same::value, int>::type = 0> + basic_json(const number_integer_t val) noexcept + : m_type(value_t::number_integer), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create an integer number from an enum type (explicit) + + Create an integer number JSON value with a given content. + + @param[in] val an integer to create a JSON number from + + @note This constructor allows to pass enums directly to a constructor. As + C++ has no way of specifying the type of an anonymous enum explicitly, we + can only rely on the fact that such values implicitly convert to int. As + int may already be the same type of number_integer_t, we may need to + switch off the constructor @ref basic_json(const number_integer_t). + + @complexity Constant. + + @liveexample{The example below shows the construction of an integer + number value from an anonymous enum.,basic_json__const_int} + + @sa @ref basic_json(const number_integer_t) -- create a number value + (integer) + @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number + value (integer) from a compatible number type + + @since version 1.0.0 + */ + basic_json(const int val) noexcept + : m_type(value_t::number_integer), + m_value(static_cast(val)) + { + assert_invariant(); + } + + /*! + @brief create an integer number (implicit) + + Create an integer number JSON value with a given content. This constructor + allows any type @a CompatibleNumberIntegerType that can be used to + construct values of type @ref number_integer_t. + + @tparam CompatibleNumberIntegerType An integer type which is compatible to + @ref number_integer_t. Examples include the types `int`, `int32_t`, + `long`, and `short`. + + @param[in] val an integer to create a JSON number from + + @complexity Constant. + + @liveexample{The example below shows the construction of several integer + number values from compatible + types.,basic_json__CompatibleIntegerNumberType} + + @sa @ref basic_json(const number_integer_t) -- create a number value + (integer) + @sa @ref basic_json(const int) -- create a number value (integer) + + @since version 1.0.0 + */ + template::value and + std::numeric_limits::is_integer and + std::numeric_limits::is_signed, + CompatibleNumberIntegerType>::type = 0> + basic_json(const CompatibleNumberIntegerType val) noexcept + : m_type(value_t::number_integer), + m_value(static_cast(val)) + { + assert_invariant(); + } + + /*! + @brief create an unsigned integer number (explicit) + + Create an unsigned integer number JSON value with a given content. + + @tparam T helper type to compare number_unsigned_t and unsigned int (not + visible in) the interface. + + @param[in] val an integer to create a JSON number from + + @complexity Constant. + + @sa @ref basic_json(const CompatibleNumberUnsignedType) -- create a number + value (unsigned integer) from a compatible number type + + @since version 2.0.0 + */ + template::value) and + std::is_same::value, int>::type = 0> + basic_json(const number_unsigned_t val) noexcept + : m_type(value_t::number_unsigned), m_value(val) + { + assert_invariant(); + } + + /*! + @brief create an unsigned number (implicit) + + Create an unsigned number JSON value with a given content. This + constructor allows any type @a CompatibleNumberUnsignedType that can be + used to construct values of type @ref number_unsigned_t. + + @tparam CompatibleNumberUnsignedType An integer type which is compatible + to @ref number_unsigned_t. Examples may include the types `unsigned int`, + `uint32_t`, or `unsigned short`. + + @param[in] val an unsigned integer to create a JSON number from + + @complexity Constant. + + @sa @ref basic_json(const number_unsigned_t) -- create a number value + (unsigned) + + @since version 2.0.0 + */ + template::value and + std::numeric_limits::is_integer and + not std::numeric_limits::is_signed, + CompatibleNumberUnsignedType>::type = 0> + basic_json(const CompatibleNumberUnsignedType val) noexcept + : m_type(value_t::number_unsigned), + m_value(static_cast(val)) + { + assert_invariant(); + } + + /*! + @brief create a floating-point number (explicit) + + Create a floating-point number JSON value with a given content. + + @param[in] val a floating-point value to create a JSON number from + + @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6 + disallows NaN values: + > Numeric values that cannot be represented in the grammar below (such as + > Infinity and NaN) are not permitted. + In case the parameter @a val is not a number, a JSON null value is created + instead. + + @complexity Constant. + + @liveexample{The following example creates several floating-point + values.,basic_json__number_float_t} + + @sa @ref basic_json(const CompatibleNumberFloatType) -- create a number + value (floating-point) from a compatible number type + + @since version 1.0.0 + */ + basic_json(const number_float_t val) noexcept + : m_type(value_t::number_float), m_value(val) + { + // replace infinity and NAN by null + if (not std::isfinite(val)) + { + m_type = value_t::null; + m_value = json_value(); + } + + assert_invariant(); + } + + /*! + @brief create an floating-point number (implicit) + + Create an floating-point number JSON value with a given content. This + constructor allows any type @a CompatibleNumberFloatType that can be used + to construct values of type @ref number_float_t. + + @tparam CompatibleNumberFloatType A floating-point type which is + compatible to @ref number_float_t. Examples may include the types `float` + or `double`. + + @param[in] val a floating-point to create a JSON number from + + @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6 + disallows NaN values: + > Numeric values that cannot be represented in the grammar below (such as + > Infinity and NaN) are not permitted. + In case the parameter @a val is not a number, a JSON null value is + created instead. + + @complexity Constant. + + @liveexample{The example below shows the construction of several + floating-point number values from compatible + types.,basic_json__CompatibleNumberFloatType} + + @sa @ref basic_json(const number_float_t) -- create a number value + (floating-point) + + @since version 1.0.0 + */ + template::value and + std::is_floating_point::value>::type> + basic_json(const CompatibleNumberFloatType val) noexcept + : basic_json(number_float_t(val)) + { + assert_invariant(); + } + + /*! + @brief create a container (array or object) from an initializer list + + Creates a JSON value of type array or object from the passed initializer + list @a init. In case @a type_deduction is `true` (default), the type of + the JSON value to be created is deducted from the initializer list @a init + according to the following rules: + + 1. If the list is empty, an empty JSON object value `{}` is created. + 2. If the list consists of pairs whose first element is a string, a JSON + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. + 3. In all other cases, an array is created. + + The rules aim to create the best fit between a C++ initializer list and + JSON values. The rationale is as follows: + + 1. The empty initializer list is written as `{}` which is exactly an empty + JSON object. + 2. C++ has now way of describing mapped types other than to list a list of + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. + 3. In all other cases, the initializer list could not be interpreted as + JSON object type, so interpreting it as JSON array type is safe. + + With the rules described above, the following JSON values cannot be + expressed by an initializer list: + + - the empty array (`[]`): use @ref array(std::initializer_list) + with an empty initializer list in this case + - arrays whose elements satisfy rule 2: use @ref + array(std::initializer_list) with the same initializer list + in this case + + @note When used without parentheses around an empty initializer list, @ref + basic_json() is called instead of this function, yielding the JSON null + value. + + @param[in] init initializer list with JSON values + + @param[in] type_deduction internal parameter; when set to `true`, the type + of the JSON value is deducted from the initializer list @a init; when set + to `false`, the type provided via @a manual_type is forced. This mode is + used by the functions @ref array(std::initializer_list) and + @ref object(std::initializer_list). + + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref + value_t::array and @ref value_t::object are valid); when @a type_deduction + is set to `true`, this parameter has no effect + + @throw std::domain_error if @a type_deduction is `false`, @a manual_type + is `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string; example: `"cannot create object from + initializer list"` + + @complexity Linear in the size of the initializer list @a init. + + @liveexample{The example below shows how JSON values are created from + initializer lists.,basic_json__list_init_t} + + @sa @ref array(std::initializer_list) -- create a JSON array + value from an initializer list + @sa @ref object(std::initializer_list) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + basic_json(std::initializer_list init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const basic_json & element) + { + return element.is_array() and element.size() == 2 and element[0].is_string(); + }); + + // adjust type if type deduction is not wanted + if (not type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (manual_type == value_t::object and not is_an_object) + { + throw std::domain_error("cannot create object from initializer list"); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + std::for_each(init.begin(), init.end(), [this](const basic_json & element) + { + m_value.object->emplace(*(element[0].m_value.string), element[1]); + }); + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init); + } + + assert_invariant(); + } + + /*! + @brief explicitly create an array from an initializer list + + Creates a JSON array value from a given initializer list. That is, given a + list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the + initializer list is empty, the empty array `[]` is created. + + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref + basic_json(std::initializer_list, bool, value_t)). These cases + are: + 1. creating an array whose elements are all pairs whose first element is a + string -- in this case, the initializer list constructor would create an + object, taking the first elements as keys + 2. creating an empty array -- passing the empty initializer list to the + initializer list constructor yields an empty object + + @param[in] init initializer list with JSON values to create an array from + (optional) + + @return JSON array value + + @complexity Linear in the size of @a init. + + @liveexample{The following code shows an example for the `array` + function.,array} + + @sa @ref basic_json(std::initializer_list, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref object(std::initializer_list) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + static basic_json array(std::initializer_list init = + std::initializer_list()) + { + return basic_json(init, false, value_t::array); + } + + /*! + @brief explicitly create an object from an initializer list + + Creates a JSON object value from a given initializer list. The initializer + lists elements must be pairs, and their first elements must be strings. If + the initializer list is empty, the empty object `{}` is created. + + @note This function is only added for symmetry reasons. In contrast to the + related function @ref array(std::initializer_list), there are + no cases which can only be expressed by this function. That is, any + initializer list @a init can also be passed to the initializer list + constructor @ref basic_json(std::initializer_list, bool, + value_t). + + @param[in] init initializer list to create an object from (optional) + + @return JSON object value + + @throw std::domain_error if @a init is not a pair whose first elements are + strings; thrown by + @ref basic_json(std::initializer_list, bool, value_t) + + @complexity Linear in the size of @a init. + + @liveexample{The following code shows an example for the `object` + function.,object} + + @sa @ref basic_json(std::initializer_list, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref array(std::initializer_list) -- create a JSON array + value from an initializer list + + @since version 1.0.0 + */ + static basic_json object(std::initializer_list init = + std::initializer_list()) + { + return basic_json(init, false, value_t::object); + } + + /*! + @brief construct an array with count copies of given value + + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. As postcondition, + `std::distance(begin(),end()) == cnt` holds. + + @param[in] cnt the number of JSON copies of @a val to create + @param[in] val the JSON value to copy + + @complexity Linear in @a cnt. + + @liveexample{The following code shows examples for the @ref + basic_json(size_type\, const basic_json&) + constructor.,basic_json__size_type_basic_json} + + @since version 1.0.0 + */ + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + assert_invariant(); + } + + /*! + @brief construct a JSON container given an iterator range + + Constructs the JSON value with the contents of the range `[first, last)`. + The semantics depends on the different types a JSON value can have: + - In case of primitive types (number, boolean, or string), @a first must + be `begin()` and @a last must be `end()`. In this case, the value is + copied. Otherwise, std::out_of_range is thrown. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector`. + - In case of a null type, std::domain_error is thrown. + + @tparam InputIT an input iterator type (@ref iterator or @ref + const_iterator) + + @param[in] first begin of the range to copy from (included) + @param[in] last end of the range to copy from (excluded) + + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** + + @throw std::domain_error if iterators are not compatible; that is, do not + belong to the same JSON value; example: `"iterators are not compatible"` + @throw std::out_of_range if iterators are for a primitive type (number, + boolean, or string) where an out of range error can be detected easily; + example: `"iterators out of range"` + @throw std::bad_alloc if allocation for object, array, or string fails + @throw std::domain_error if called with a null value; example: `"cannot + use construct with iterators from null"` + + @complexity Linear in distance between @a first and @a last. + + @liveexample{The example below shows several ways to create JSON values by + specifying a subrange with iterators.,basic_json__InputIt_InputIt} + + @since version 1.0.0 + */ + template::value or + std::is_same::value, int>::type = 0> + basic_json(InputIT first, InputIT last) + { + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); + + // make sure iterator fits the current value + if (first.m_object != last.m_object) + { + throw std::domain_error("iterators are not compatible"); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + { + throw std::out_of_range("iterators out of range"); + } + break; + } + + default: + { + break; + } + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); + break; + } + + default: + { + throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name()); + } + } + + assert_invariant(); + } + + /*! + @brief construct a JSON value given an input stream + + @param[in,out] i stream to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @deprecated This constructor is deprecated and will be removed in version + 3.0.0 to unify the interface of the library. Deserialization will be + done by stream operators or by calling one of the `parse` functions, + e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls + like `json j(i);` for an input stream @a i need to be replaced by + `json j = json::parse(i);`. See the example below. + + @liveexample{The example below demonstrates constructing a JSON value from + a `std::stringstream` with and without callback + function.,basic_json__istream} + + @since version 2.0.0, deprecated in version 2.0.3, to be removed in + version 3.0.0 + */ + JSON_DEPRECATED + explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) + { + *this = parser(i, cb).parse(); + assert_invariant(); + } + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + /*! + @brief copy constructor + + Creates a copy of a given JSON value. + + @param[in] other the JSON value to copy + + @complexity Linear in the size of @a other. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - As postcondition, it holds: `other == basic_json(other)`. + + @throw std::bad_alloc if allocation for object, array, or string fails. + + @liveexample{The following code shows an example for the copy + constructor.,basic_json__basic_json} + + @since version 1.0.0 + */ + basic_json(const basic_json& other) + : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + default: + { + break; + } + } + + assert_invariant(); + } + + /*! + @brief move constructor + + Move constructor. Constructs a JSON value with the contents of the given + value @a other using move semantics. It "steals" the resources from @a + other and leaves it as JSON null value. + + @param[in,out] other value to move to this object + + @post @a other is a JSON null value + + @complexity Constant. + + @liveexample{The code below shows the move constructor explicitly called + via std::move.,basic_json__moveconstructor} + + @since version 1.0.0 + */ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + assert_invariant(); + } + + /*! + @brief copy assignment + + Copy assignment operator. Copies a JSON value via the "copy and swap" + strategy: It is expressed in terms of the copy constructor, destructor, + and the swap() member function. + + @param[in] other value to copy from + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + + @liveexample{The code below shows and example for the copy assignment. It + creates a copy of value `a` which is then swapped with `b`. Finally\, the + copy of `a` (which is the null value after the swap) is + destroyed.,basic_json__copyassignment} + + @since version 1.0.0 + */ + reference& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + assert_invariant(); + return *this; + } + + /*! + @brief destructor + + Destroys the JSON value and frees all allocated memory. + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory is freed. + + @since version 1.0.0 + */ + ~basic_json() + { + assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + AllocatorType alloc; + alloc.destroy(m_value.object); + alloc.deallocate(m_value.object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + alloc.destroy(m_value.array); + alloc.deallocate(m_value.array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + break; + } + + default: + { + // all other types need no specific destructor + break; + } + } + } + + /// @} + +public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /*! + @brief serialization + + Serialization function for JSON values. The function tries to mimic + Python's `json.dumps()` function, and currently supports its @a indent + parameter. + + @param[in] indent If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. + + @return string containing the serialization of the JSON value + + @complexity Linear. + + @liveexample{The following example shows the effect of different @a indent + parameters to the result of the serialization.,dump} + + @see https://docs.python.org/2/library/json.html#json.dump + + @since version 1.0.0 + */ + string_t dump(const int indent = -1) const + { + std::stringstream ss; + // fix locale problems + const static std::locale loc(std::locale(), new DecimalSeparator); + ss.imbue(loc); + + // 6, 15 or 16 digits of precision allows round-trip IEEE 754 + // string->float->string, string->double->string or string->long + // double->string; to be safe, we read this value from + // std::numeric_limits::digits10 + ss.precision(std::numeric_limits::digits10); + + if (indent >= 0) + { + dump(ss, true, static_cast(indent)); + } + else + { + dump(ss, false, 0); + } + + return ss.str(); + } + + /*! + @brief return the type of the JSON value (explicit) + + Return the type of the JSON value as a value from the @ref value_t + enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON + types.,type} + + @since version 1.0.0 + */ + constexpr value_t type() const noexcept + { + return m_type; + } + + /*! + @brief return whether type is primitive + + This function returns true iff the JSON type is primitive (string, number, + boolean, or null). + + @return `true` if type is primitive (string, number, boolean, or null), + `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_primitive()` for all JSON + types.,is_primitive} + + @sa @ref is_structured() -- returns whether JSON value is structured + @sa @ref is_null() -- returns whether JSON value is `null` + @sa @ref is_string() -- returns whether JSON value is a string + @sa @ref is_boolean() -- returns whether JSON value is a boolean + @sa @ref is_number() -- returns whether JSON value is a number + + @since version 1.0.0 + */ + constexpr bool is_primitive() const noexcept + { + return is_null() or is_string() or is_boolean() or is_number(); + } + + /*! + @brief return whether type is structured + + This function returns true iff the JSON type is structured (array or + object). + + @return `true` if type is structured (array or object), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_structured()` for all JSON + types.,is_structured} + + @sa @ref is_primitive() -- returns whether value is primitive + @sa @ref is_array() -- returns whether value is an array + @sa @ref is_object() -- returns whether value is an object + + @since version 1.0.0 + */ + constexpr bool is_structured() const noexcept + { + return is_array() or is_object(); + } + + /*! + @brief return whether value is null + + This function returns true iff the JSON value is null. + + @return `true` if type is null, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_null()` for all JSON + types.,is_null} + + @since version 1.0.0 + */ + constexpr bool is_null() const noexcept + { + return m_type == value_t::null; + } + + /*! + @brief return whether value is a boolean + + This function returns true iff the JSON value is a boolean. + + @return `true` if type is boolean, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON + types.,is_boolean} + + @since version 1.0.0 + */ + constexpr bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + /*! + @brief return whether value is a number + + This function returns true iff the JSON value is a number. This includes + both integer and floating-point values. + + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number()` for all JSON + types.,is_number} + + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number() const noexcept + { + return is_number_integer() or is_number_float(); + } + + /*! + @brief return whether value is an integer number + + This function returns true iff the JSON value is an integer or unsigned + integer number. This excludes floating-point values. + + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_integer()` for all + JSON types.,is_number_integer} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer or m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is an unsigned integer number + + This function returns true iff the JSON value is an unsigned integer + number. This excludes floating-point and (signed) integer values. + + @return `true` if type is an unsigned integer number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 2.0.0 + */ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is a floating-point number + + This function returns true iff the JSON value is a floating-point number. + This excludes integer and unsigned integer values. + + @return `true` if type is a floating-point number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_float()` for all + JSON types.,is_number_float} + + @sa @ref is_number() -- check if value is number + @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + + @since version 1.0.0 + */ + constexpr bool is_number_float() const noexcept + { + return m_type == value_t::number_float; + } + + /*! + @brief return whether value is an object + + This function returns true iff the JSON value is an object. + + @return `true` if type is object, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON + types.,is_object} + + @since version 1.0.0 + */ + constexpr bool is_object() const noexcept + { + return m_type == value_t::object; + } + + /*! + @brief return whether value is an array + + This function returns true iff the JSON value is an array. + + @return `true` if type is array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_array()` for all JSON + types.,is_array} + + @since version 1.0.0 + */ + constexpr bool is_array() const noexcept + { + return m_type == value_t::array; + } + + /*! + @brief return whether value is a string + + This function returns true iff the JSON value is a string. + + @return `true` if type is string, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON + types.,is_string} + + @since version 1.0.0 + */ + constexpr bool is_string() const noexcept + { + return m_type == value_t::string; + } + + /*! + @brief return whether value is discarded + + This function returns true iff the JSON value was discarded during parsing + with a callback function (see @ref parser_callback_t). + + @note This function will always be `false` for JSON values after parsing. + That is, discarded values can only occur during parsing, but will be + removed when inside a structured value or replaced by null in other cases. + + @return `true` if type is discarded, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_discarded()` for all JSON + types.,is_discarded} + + @since version 1.0.0 + */ + constexpr bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } + + /*! + @brief return the type of the JSON value (implicit) + + Implicitly return the type of the JSON value as a value from the @ref + value_t enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} + + @since version 1.0.0 + */ + constexpr operator value_t() const noexcept + { + return m_type; + } + + /// @} + +private: + ////////////////// + // value access // + ////////////////// + + /// get an object (explicit) + template::value and + std::is_convertible::value, int>::type = 0> + T get_impl(T*) const + { + if (is_object()) + { + return T(m_value.object->begin(), m_value.object->end()); + } + else + { + throw std::domain_error("type must be object, but is " + type_name()); + } + } + + /// get an object (explicit) + object_t get_impl(object_t*) const + { + if (is_object()) + { + return *(m_value.object); + } + else + { + throw std::domain_error("type must be object, but is " + type_name()); + } + } + + /// get an array (explicit) + template::value and + not std::is_same::value and + not std::is_arithmetic::value and + not std::is_convertible::value and + not has_mapped_type::value, int>::type = 0> + T get_impl(T*) const + { + if (is_array()) + { + T to_vector; + std::transform(m_value.array->begin(), m_value.array->end(), + std::inserter(to_vector, to_vector.end()), [](basic_json i) + { + return i.get(); + }); + return to_vector; + } + else + { + throw std::domain_error("type must be array, but is " + type_name()); + } + } + + /// get an array (explicit) + template::value and + not std::is_same::value, int>::type = 0> + std::vector get_impl(std::vector*) const + { + if (is_array()) + { + std::vector to_vector; + to_vector.reserve(m_value.array->size()); + std::transform(m_value.array->begin(), m_value.array->end(), + std::inserter(to_vector, to_vector.end()), [](basic_json i) + { + return i.get(); + }); + return to_vector; + } + else + { + throw std::domain_error("type must be array, but is " + type_name()); + } + } + + /// get an array (explicit) + template::value and + not has_mapped_type::value, int>::type = 0> + T get_impl(T*) const + { + if (is_array()) + { + return T(m_value.array->begin(), m_value.array->end()); + } + else + { + throw std::domain_error("type must be array, but is " + type_name()); + } + } + + /// get an array (explicit) + array_t get_impl(array_t*) const + { + if (is_array()) + { + return *(m_value.array); + } + else + { + throw std::domain_error("type must be array, but is " + type_name()); + } + } + + /// get a string (explicit) + template::value, int>::type = 0> + T get_impl(T*) const + { + if (is_string()) + { + return *m_value.string; + } + else + { + throw std::domain_error("type must be string, but is " + type_name()); + } + } + + /// get a number (explicit) + template::value, int>::type = 0> + T get_impl(T*) const + { + switch (m_type) + { + case value_t::number_integer: + { + return static_cast(m_value.number_integer); + } + + case value_t::number_unsigned: + { + return static_cast(m_value.number_unsigned); + } + + case value_t::number_float: + { + return static_cast(m_value.number_float); + } + + default: + { + throw std::domain_error("type must be number, but is " + type_name()); + } + } + } + + /// get a boolean (explicit) + constexpr boolean_t get_impl(boolean_t*) const + { + return is_boolean() + ? m_value.boolean + : throw std::domain_error("type must be boolean, but is " + type_name()); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t*) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t*) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t*) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t*) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t*) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t*) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t*) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t*) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t*) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t*) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t*) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This funcion helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw std::domain_error if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // helper type + using PointerType = typename std::add_pointer::type; + + // delegate the call to get_ptr<>() + auto ptr = obj.template get_ptr(); + + if (ptr != nullptr) + { + return *ptr; + } + else + { + throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + + obj.type_name()); + } + } + +public: + + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays + + @return copy of the JSON value, converted to type @a ValueType + + @throw std::domain_error in case passed type @a ValueType is incompatible + to JSON; example: `"type must be object, but is null"` + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @internal + The idea of using a casted null pointer to choose the correct + implementation is from . + @endinternal + + @sa @ref operator ValueType() const for implicit conversion + @sa @ref get() for pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + ValueType get() const + { + return get_impl(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + PointerType get() noexcept + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, int>::type = 0> + constexpr const PointerType get() const noexcept + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (implicit) + + Implicit pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 + */ + template::value, int>::type = 0> + PointerType get_ptr() noexcept + { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); + + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template::value and + std::is_const::type>::value, int>::type = 0> + constexpr const PointerType get_ptr() const noexcept + { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); + + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a reference value (implicit) + + Implict reference access to the internally stored JSON value. No copies + are made. + + @warning Writing data to the referee of the result yields an undefined + state. + + @tparam ReferenceType reference type; must be a reference to @ref array_t, + @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or + @ref number_float_t. Enforced by static assertion. + + @return reference to the internally stored JSON value if the requested + reference type @a ReferenceType fits to the JSON value; throws + std::domain_error otherwise + + @throw std::domain_error in case passed type @a ReferenceType is + incompatible with the stored JSON value + + @complexity Constant. + + @liveexample{The example shows several calls to `get_ref()`.,get_ref} + + @since version 1.1.0 + */ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template::value and + std::is_const::type>::value, int>::type = 0> + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw std::domain_error in case passed type @a ValueType is incompatible + to JSON, thrown by @ref get() const + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + not std::is_pointer::value and + not std::is_same::value + #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 + and not std::is_same>::value + #endif + , int >::type = 0 > + operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /// @} + + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /*! + @brief access specified array element with bounds checking + + Returns a reference to the element at specified location @a idx, with + bounds checking. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw std::domain_error if the JSON value is not an array; example: + `"cannot use at() with string"` + @throw std::out_of_range if the index @a idx is out of range of the array; + that is, `idx >= size()`; example: `"array index 7 is out of range"` + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read and + written using `at()`.,at__size_type} + + @since version 1.0.0 + */ + reference at(size_type idx) + { + // at only works for arrays + if (is_array()) + { + try + { + return m_value.array->at(idx); + } + catch (std::out_of_range&) + { + // create better exception explanation + //throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); + throw std::out_of_range("array index " + QString::number(idx).toStdString() + " is out of range"); + + } + } + else + { + throw std::domain_error("cannot use at() with " + type_name()); + } + } + + /*! + @brief access specified array element with bounds checking + + Returns a const reference to the element at specified location @a idx, + with bounds checking. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw std::domain_error if the JSON value is not an array; example: + `"cannot use at() with string"` + @throw std::out_of_range if the index @a idx is out of range of the array; + that is, `idx >= size()`; example: `"array index 7 is out of range"` + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read using + `at()`.,at__size_type_const} + + @since version 1.0.0 + */ + const_reference at(size_type idx) const + { + // at only works for arrays + if (is_array()) + { + try + { + return m_value.array->at(idx); + } + catch (std::out_of_range&) + { + // create better exception explanation + //throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); + throw std::out_of_range("array index " + QString::number(idx).toStdString() + " is out of range"); + } + } + else + { + throw std::domain_error("cannot use at() with " + type_name()); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a reference to the element at with specified key @a key, with + bounds checking. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw std::domain_error if the JSON value is not an object; example: + `"cannot use at() with boolean"` + @throw std::out_of_range if the key @a key is is not stored in the object; + that is, `find(key) == end()`; example: `"key "the fast" not found"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using `at()`.,at__object_t_key_type} + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (is_object()) + { + try + { + return m_value.object->at(key); + } + catch (std::out_of_range&) + { + // create better exception explanation + throw std::out_of_range("key '" + key + "' not found"); + } + } + else + { + throw std::domain_error("cannot use at() with " + type_name()); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a const reference to the element at with specified key @a key, + with bounds checking. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @throw std::domain_error if the JSON value is not an object; example: + `"cannot use at() with boolean"` + @throw std::out_of_range if the key @a key is is not stored in the object; + that is, `find(key) == end()`; example: `"key "the fast" not found"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + `at()`.,at__object_t_key_type_const} + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (is_object()) + { + try + { + return m_value.object->at(key); + } + catch (std::out_of_range&) + { + // create better exception explanation + throw std::out_of_range("key '" + key + "' not found"); + } + } + else + { + throw std::domain_error("cannot use at() with " + type_name()); + } + } + + /*! + @brief access specified array element + + Returns a reference to the element at specified location @a idx. + + @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), + then the array is silently filled up with `null` values to make `idx` a + valid reference to the last stored element. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw std::domain_error if JSON is not an array or null; example: + `"cannot use operator[] with string"` + + @complexity Constant if @a idx is in the range of the array. Otherwise + linear in `idx - size()`. + + @liveexample{The example below shows how array elements can be read and + written using `[]` operator. Note the addition of `null` + values.,operatorarray__size_type} + + @since version 1.0.0 + */ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (is_array()) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { + m_value.array->insert(m_value.array->end(), + idx - m_value.array->size() + 1, + basic_json()); + } + + return m_value.array->operator[](idx); + } + else + { + throw std::domain_error("cannot use operator[] with " + type_name()); + } + } + + /*! + @brief access specified array element + + Returns a const reference to the element at specified location @a idx. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw std::domain_error if JSON is not an array; example: `"cannot use + operator[] with null"` + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read using + the `[]` operator.,operatorarray__size_type_const} + + @since version 1.0.0 + */ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (is_array()) + { + return m_value.array->operator[](idx); + } + else + { + throw std::domain_error("cannot use operator[] with " + type_name()); + } + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw std::domain_error if JSON is not an object or null; example: + `"cannot use operator[] with string"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (is_object()) + { + return m_value.object->operator[](key); + } + else + { + throw std::domain_error("cannot use operator[] with " + type_name()); + } + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw std::domain_error if JSON is not an object; example: `"cannot use + operator[] with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (is_object()) + { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + else + { + throw std::domain_error("cannot use operator[] with " + type_name()); + } + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw std::domain_error if JSON is not an object or null; example: + `"cannot use operator[] with string"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + template + reference operator[](T * (&key)[n]) + { + return operator[](static_cast(key)); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @note This function is required for compatibility reasons with Clang. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @throw std::domain_error if JSON is not an object; example: `"cannot use + operator[] with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + template + const_reference operator[](T * (&key)[n]) const + { + return operator[](static_cast(key)); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw std::domain_error if JSON is not an object or null; example: + `"cannot use operator[] with string"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (is_object()) + { + return m_value.object->operator[](key); + } + else + { + throw std::domain_error("cannot use operator[] with " + type_name()); + } + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw std::domain_error if JSON is not an object; example: `"cannot use + operator[] with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + const_reference operator[](T* key) const + { + // at only works for objects + if (is_object()) + { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + else + { + throw std::domain_error("cannot use operator[] with " + type_name()); + } + } + + /*! + @brief access specified object element with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(key); + } catch(std::out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const typename object_t::key_type&), this function + does not throw if the given key @a key was not found. + + @note Unlike @ref operator[](const typename object_t::key_type& key), this + function does not implicitly add an element to the position defined by @a + key. This function is furthermore also applicable to const objects. + + @param[in] key key of the element to access + @param[in] default_value the value to return if @a key is not found + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw std::domain_error if JSON is not an object; example: `"cannot use + value() with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + + @since version 1.0.0 + */ + template::value, int>::type = 0> + ValueType value(const typename object_t::key_type& key, ValueType default_value) const + { + // at only works for objects + if (is_object()) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return *it; + } + else + { + return default_value; + } + } + else + { + throw std::domain_error("cannot use value() with " + type_name()); + } + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const + */ + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /*! + @brief access specified object element via JSON Pointer with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(std::out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. + + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw std::domain_error if JSON is not an object; example: `"cannot use + value() with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 + */ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, ValueType default_value) const + { + // at only works for objects + if (is_object()) + { + // if pointer resolves a value, return it or use default value + try + { + return ptr.get_checked(this); + } + catch (std::out_of_range&) + { + return default_value; + } + } + else + { + throw std::domain_error("cannot use value() with " + type_name()); + } + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /*! + @brief access the first element + + Returns a reference to the first element in the container. For a JSON + container `c`, the expression `c.front()` is equivalent to `*c.begin()`. + + @return In case of a structured type (array or object), a reference to the + first element is returned. In cast of number, string, or boolean values, a + reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw std::out_of_range when called on `null` value + + @liveexample{The following code shows an example for `front()`.,front} + + @sa @ref back() -- access the last element + + @since version 1.0.0 + */ + reference front() + { + return *begin(); + } + + /*! + @copydoc basic_json::front() + */ + const_reference front() const + { + return *cbegin(); + } + + /*! + @brief access the last element + + Returns a reference to the last element in the container. For a JSON + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode + + @return In case of a structured type (array or object), a reference to the + last element is returned. In cast of number, string, or boolean values, a + reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw std::out_of_range when called on `null` value. + + @liveexample{The following code shows an example for `back()`.,back} + + @sa @ref front() -- access the first element + + @since version 1.0.0 + */ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /*! + @copydoc basic_json::back() + */ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /*! + @brief remove element given an iterator + + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] pos iterator to the element to remove + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw std::domain_error if called on a `null` value; example: `"cannot + use erase() with null"` + @throw std::domain_error if called on an iterator which does not belong to + the current JSON value; example: `"iterator does not fit current value"` + @throw std::out_of_range if called on a primitive type with invalid + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` + + @complexity The complexity depends on the type: + - objects: amortized constant + - arrays: linear in distance between pos and the end of the container + - strings: linear in the length of the string + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType} + + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (this != pos.m_object) + { + throw std::domain_error("iterator does not fit current value"); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not pos.m_it.primitive_iterator.is_begin()) + { + throw std::out_of_range("iterator out of range"); + } + + if (is_string()) + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + m_value.string = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + default: + { + throw std::domain_error("cannot use erase() with " + type_name()); + } + } + + return result; + } + + /*! + @brief remove elements given an iterator range + + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] first iterator to the beginning of the range to remove + @param[in] last iterator past the end of the range to remove + @return Iterator following the last removed element. If the iterator @a + second refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw std::domain_error if called on a `null` value; example: `"cannot + use erase() with null"` + @throw std::domain_error if called on iterators which does not belong to + the current JSON value; example: `"iterators do not fit current value"` + @throw std::out_of_range if called on a primitive type with invalid + iterators (i.e., if `first != begin()` and `last != end()`); example: + `"iterators out of range"` + + @complexity The complexity depends on the type: + - objects: `log(size()) + std::distance(first, last)` + - arrays: linear in the distance between @a first and @a last, plus linear + in the distance between @a last and end of the container + - strings: linear in the length of the string + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType_IteratorType} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (this != first.m_object or this != last.m_object) + { + throw std::domain_error("iterators do not fit current value"); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + { + throw std::out_of_range("iterators out of range"); + } + + if (is_string()) + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + m_value.string = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + default: + { + throw std::domain_error("cannot use erase() with " + type_name()); + } + } + + return result; + } + + /*! + @brief remove element from a JSON object given a key + + Removes elements from a JSON object with the key value @a key. + + @param[in] key value of the elements to remove + + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). + + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. + + @throw std::domain_error when called on a type other than JSON object; + example: `"cannot use erase() with null"` + + @complexity `log(size()) + count(key)` + + @liveexample{The example shows the effect of `erase()`.,erase__key_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (is_object()) + { + return m_value.object->erase(key); + } + else + { + throw std::domain_error("cannot use erase() with " + type_name()); + } + } + + /*! + @brief remove element from a JSON array given an index + + Removes element from a JSON array at the index @a idx. + + @param[in] idx index of the element to remove + + @throw std::domain_error when called on a type other than JSON array; + example: `"cannot use erase() with null"` + @throw std::out_of_range when `idx >= size()`; example: `"array index 17 + is out of range"` + + @complexity Linear in distance between @a idx and the end of the container. + + @liveexample{The example shows the effect of `erase()`.,erase__size_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + + @since version 1.0.0 + */ + void erase(const size_type idx) + { + // this erase only works for arrays + if (is_array()) + { + if (idx >= size()) + { + //throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); + throw std::out_of_range("array index " + QString::number(idx).toStdString() + " is out of range"); + + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + throw std::domain_error("cannot use erase() with " + type_name()); + } + } + + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /*! + @brief find an element in a JSON object + + Finds an element in a JSON object with key equivalent to @a key. If the + element is not found or the JSON value is not an object, end() is + returned. + + @param[in] key key value of the element to search for + + @return Iterator to an element with key equivalent to @a key. If no such + element is found, past-the-end (see end()) iterator is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `find()` is used.,find__key_type} + + @since version 1.0.0 + */ + iterator find(typename object_t::key_type key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(key); + } + + return result; + } + + /*! + @brief find an element in a JSON object + @copydoc find(typename object_t::key_type) + */ + const_iterator find(typename object_t::key_type key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(key); + } + + return result; + } + + /*! + @brief returns the number of occurrences of a key in a JSON object + + Returns the number of elements with key @a key. If ObjectType is the + default `std::map` type, the return value will always be `0` (@a key was + not found) or `1` (@a key was found). + + @param[in] key key value of the element to count + + @return Number of elements with key @a key. If the JSON value is not an + object, the return value will be `0`. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `count()` is used.,count} + + @since version 1.0.0 + */ + size_type count(typename object_t::key_type key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(key) : 0; + } + + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /*! + @brief returns an iterator to the first element + + Returns an iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /*! + @copydoc basic_json::cbegin() + */ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /*! + @brief returns a const iterator to the first element + + Returns a const iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).begin()`. + + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /*! + @brief returns an iterator to one past the last element + + Returns an iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `end()`.,end} + + @sa @ref cend() -- returns a const iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /*! + @copydoc basic_json::cend() + */ + const_iterator end() const noexcept + { + return cend(); + } + + /*! + @brief returns a const iterator to one past the last element + + Returns a const iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).end()`. + + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa @ref end() -- returns an iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /*! + @brief returns an iterator to the reverse-beginning + + Returns an iterator to the reverse-beginning; that is, the last element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(end())`. + + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /*! + @copydoc basic_json::crbegin() + */ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /*! + @brief returns an iterator to the reverse-end + + Returns an iterator to the reverse-end; that is, one before the first + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(begin())`. + + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa @ref crend() -- returns a const reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /*! + @copydoc basic_json::crend() + */ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /*! + @brief returns a const reverse iterator to the last element + + Returns a const iterator to the reverse-beginning; that is, the last + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rbegin()`. + + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /*! + @brief returns a const reverse iterator to one before the first + + Returns a const reverse iterator to the reverse-end; that is, one before + the first element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rend()`. + + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + +private: + // forward declaration + template class iteration_proxy; + +public: + /*! + @brief wrapper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + @note The name of this function is not yet final and may change in the + future. + */ + static iteration_proxy iterator_wrapper(reference cont) + { + return iteration_proxy(cont); + } + + /*! + @copydoc iterator_wrapper(reference) + */ + static iteration_proxy iterator_wrapper(const_reference cont) + { + return iteration_proxy(cont); + } + + /// @} + + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /*! + @brief checks whether the container is empty + + Checks if a JSON value has no elements. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `true` + boolean | `false` + string | `false` + number | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` + + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant + complexity. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `begin() == end()`. + + @liveexample{The following code uses `empty()` to check if a JSON + object contains any elements.,empty} + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + default: + { + // all other types are nonempty + return false; + } + } + } + + /*! + @brief returns the number of elements + + Returns the number of elements in a JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` + boolean | `1` + string | `1` + number | `1` + object | result of function object_t::size() + array | result of function array_t::size() + + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `std::distance(begin(), end())`. + + @liveexample{The following code calls `size()` on the different value + types.,size} + + @sa @ref empty() -- checks whether the container is empty + @sa @ref max_size() -- returns the maximal number of elements + + @since version 1.0.0 + */ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + default: + { + // all other types have size 1 + return 1; + } + } + } + + /*! + @brief returns the maximum possible number of elements + + Returns the maximum number of elements a JSON value is able to hold due to + system or library implementation limitations, i.e. `std::distance(begin(), + end())` for the JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant + complexity. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of returning `b.size()` where `b` is the largest + possible JSON value. + + @liveexample{The following code calls `max_size()` on the different value + types. Note the output is implementation specific.,max_size} + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /*! + @brief clears the contents + + Clears the content of a JSON value and resets it to the default value as + if @ref basic_json(value_t) would have been called: + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + + @note Floating-point numbers are set to `0.0` which will be serialized to + `0`. The vale type remains @ref number_float_t. + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows the effect of `clear()` to different + JSON types.,clear} + + @since version 1.0.0 + */ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_value.boolean = false; + break; + } + + case value_t::string: + { + m_value.string->clear(); + break; + } + + case value_t::array: + { + m_value.array->clear(); + break; + } + + case value_t::object: + { + m_value.object->clear(); + break; + } + + default: + { + break; + } + } + } + + /*! + @brief add an object to an array + + Appends the given element @a val to the end of the JSON value. If the + function is called on a JSON null value, an empty array is created before + appending @a val. + + @param[in] val the value to add to the JSON array + + @throw std::domain_error when called on a type other than JSON array or + null; example: `"cannot use push_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} + + @since version 1.0.0 + */ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + throw std::domain_error("cannot use push_back() with " + type_name()); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + m_value.array->push_back(std::move(val)); + // invalidate object + val.m_type = value_t::null; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + throw std::domain_error("cannot use push_back() with " + type_name()); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + m_value.array->push_back(val); + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + Inserts the given element @a val to the JSON object. If the function is + called on a JSON null value, an empty object is created before inserting + @a val. + + @param[in] val the value to add to the JSON object + + @throw std::domain_error when called on a type other than JSON object or + null; example: `"cannot use push_back() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} + + @since version 1.0.0 + */ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (not(is_null() or is_object())) + { + throw std::domain_error("cannot use push_back() with " + type_name()); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array + m_value.object->insert(val); + } + + /*! + @brief add an object to an object + @copydoc push_back(const typename object_t::value_type&) + */ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + This function allows to use `push_back` with an initializer list. In case + + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, + + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). + + @param init an initializer list + + @complexity Linear in the size of the initializer list @a init. + + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. + + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(std::initializer_list init) + { + if (is_object() and init.size() == 2 and init.begin()->is_string()) + { + const string_t key = *init.begin(); + push_back(typename object_t::value_type(key, *(init.begin() + 1))); + } + else + { + push_back(basic_json(init)); + } + } + + /*! + @brief add an object to an object + @copydoc push_back(std::initializer_list) + */ + reference operator+=(std::initializer_list init) + { + push_back(init); + return *this; + } + + /*! + @brief inserts element + + Inserts element @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. + + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` + + @complexity Constant plus linear in the distance between pos and end of the + container. + + @liveexample{The example shows how `insert()` is used.,insert} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (is_array()) + { + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); + return result; + } + else + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + } + + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /*! + @brief inserts elements + + Inserts @a cnt copies of @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] cnt number of copies of @a val to insert + @param[in] val element to insert + @return iterator pointing to the first element inserted, or @a pos if + `cnt==0` + + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` + + @complexity Linear in @a cnt plus linear in the distance between @a pos + and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__count} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (is_array()) + { + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + return result; + } + else + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` + @throw std::domain_error if @a first and @a last do not belong to the same + JSON value; example: `"iterators do not fit"` + @throw std::domain_error if @a first or @a last are iterators into + container for which insert is called; example: `"passed iterators may not + belong to container"` + + @return iterator pointing to the first element inserted, or @a pos if + `first==last` + + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__range} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (not is_array()) + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // check if range iterators belong to the same JSON object + if (first.m_object != last.m_object) + { + throw std::domain_error("iterators do not fit"); + } + + if (first.m_object == this or last.m_object == this) + { + throw std::domain_error("passed iterators may not belong to container"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert( + pos.m_it.array_iterator, + first.m_it.array_iterator, + last.m_it.array_iterator); + return result; + } + + /*! + @brief inserts elements + + Inserts elements from initializer list @a ilist before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] ilist initializer list to insert the values from + + @throw std::domain_error if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw std::domain_error if @a pos is not an iterator of *this; example: + `"iterator does not fit current value"` + + @return iterator pointing to the first element inserted, or @a pos if + `ilist` is empty + + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__ilist} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, std::initializer_list ilist) + { + // insert only works for arrays + if (not is_array()) + { + throw std::domain_error("cannot use insert() with " + type_name()); + } + + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + throw std::domain_error("iterator does not fit current value"); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); + return result; + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + assert_invariant(); + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON array with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other array to exchange the contents with + + @throw std::domain_error when JSON value is not an array; example: `"cannot + use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} + + @since version 1.0.0 + */ + void swap(array_t& other) + { + // swap only works for arrays + if (is_array()) + { + std::swap(*(m_value.array), other); + } + else + { + throw std::domain_error("cannot use swap() with " + type_name()); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON object with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other object to exchange the contents with + + @throw std::domain_error when JSON value is not an object; example: + `"cannot use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} + + @since version 1.0.0 + */ + void swap(object_t& other) + { + // swap only works for objects + if (is_object()) + { + std::swap(*(m_value.object), other); + } + else + { + throw std::domain_error("cannot use swap() with " + type_name()); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other string to exchange the contents with + + @throw std::domain_error when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} + + @since version 1.0.0 + */ + void swap(string_t& other) + { + // swap only works for strings + if (is_string()) + { + std::swap(*(m_value.string), other); + } + else + { + throw std::domain_error("cannot use swap() with " + type_name()); + } + } + + /// @} + + + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + +private: + /*! + @brief comparison operator for JSON types + + Returns an ordering that is similar to Python: + - order: null < boolean < number < object < array < string + - furthermore, each type is not smaller than itself + + @since version 1.0.0 + */ + friend bool operator<(const value_t lhs, const value_t rhs) noexcept + { + static constexpr std::array order = {{ + 0, // null + 3, // object + 4, // array + 5, // string + 1, // boolean + 2, // integer + 2, // unsigned + 2, // float + } + }; + + // discarded values are not comparable + if (lhs == value_t::discarded or rhs == value_t::discarded) + { + return false; + } + + return order[static_cast(lhs)] < order[static_cast(rhs)]; + } + +public: + /*! + @brief comparison: equal + + Compares two JSON values for equality according to the following rules: + - Two JSON values are equal if (1) they are from the same type and (2) + their stored values are the same. + - Integer and floating-point numbers are automatically converted before + comparison. Floating-point numbers are compared indirectly: two + floating-point numbers `f1` and `f2` are considered equal if neither + `f1 > f2` nor `f2 > f1` holds. + - Two JSON null values are equal. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are equal + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__equal} + + @since version 1.0.0 + */ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + { + return *lhs.m_value.array == *rhs.m_value.array; + } + case value_t::object: + { + return *lhs.m_value.object == *rhs.m_value.object; + } + case value_t::null: + { + return true; + } + case value_t::string: + { + return *lhs.m_value.string == *rhs.m_value.string; + } + case value_t::boolean: + { + return lhs.m_value.boolean == rhs.m_value.boolean; + } + case value_t::number_integer: + { + return lhs.m_value.number_integer == rhs.m_value.number_integer; + } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + } + case value_t::number_float: + { + return lhs.m_value.number_float == rhs.m_value.number_float; + } + default: + { + return false; + } + } + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + + return false; + } + + /*! + @brief comparison: equal + + The functions compares the given JSON value against a null pointer. As the + null pointer can be used to initialize a JSON value to null, a comparison + of JSON value @a v with a null pointer should be equivalent to call + `v.is_null()`. + + @param[in] v JSON value to consider + @return whether @a v is null + + @complexity Constant. + + @liveexample{The example compares several JSON types to the null pointer. + ,operator__equal__nullptr_t} + + @since version 1.0.0 + */ + friend bool operator==(const_reference v, std::nullptr_t) noexcept + { + return v.is_null(); + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, std::nullptr_t) + */ + friend bool operator==(std::nullptr_t, const_reference v) noexcept + { + return v.is_null(); + } + + /*! + @brief comparison: not equal + + Compares two JSON values for inequality by calculating `not (lhs == rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are not equal + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__notequal} + + @since version 1.0.0 + */ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs == rhs); + } + + /*! + @brief comparison: not equal + + The functions compares the given JSON value against a null pointer. As the + null pointer can be used to initialize a JSON value to null, a comparison + of JSON value @a v with a null pointer should be equivalent to call + `not v.is_null()`. + + @param[in] v JSON value to consider + @return whether @a v is not null + + @complexity Constant. + + @liveexample{The example compares several JSON types to the null pointer. + ,operator__notequal__nullptr_t} + + @since version 1.0.0 + */ + friend bool operator!=(const_reference v, std::nullptr_t) noexcept + { + return not v.is_null(); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, std::nullptr_t) + */ + friend bool operator!=(std::nullptr_t, const_reference v) noexcept + { + return not v.is_null(); + } + + /*! + @brief comparison: less than + + Compares whether one JSON value @a lhs is less than another JSON value @a + rhs according to the following rules: + - If @a lhs and @a rhs have the same type, the values are compared using + the default `<` operator. + - Integer and floating-point numbers are automatically converted before + comparison + - In case @a lhs and @a rhs have different types, the values are ignored + and the order of the types is considered, see + @ref operator<(const value_t, const value_t). + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than @a rhs + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__less} + + @since version 1.0.0 + */ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + { + return *lhs.m_value.array < *rhs.m_value.array; + } + case value_t::object: + { + return *lhs.m_value.object < *rhs.m_value.object; + } + case value_t::null: + { + return false; + } + case value_t::string: + { + return *lhs.m_value.string < *rhs.m_value.string; + } + case value_t::boolean: + { + return lhs.m_value.boolean < rhs.m_value.boolean; + } + case value_t::number_integer: + { + return lhs.m_value.number_integer < rhs.m_value.number_integer; + } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + } + case value_t::number_float: + { + return lhs.m_value.number_float < rhs.m_value.number_float; + } + default: + { + return false; + } + } + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } + + /*! + @brief comparison: less than or equal + + Compares whether one JSON value @a lhs is less than or equal to another + JSON value by calculating `not (rhs < lhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than or equal to @a rhs + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greater} + + @since version 1.0.0 + */ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return not (rhs < lhs); + } + + /*! + @brief comparison: greater than + + Compares whether one JSON value @a lhs is greater than another + JSON value by calculating `not (lhs <= rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than to @a rhs + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__lessequal} + + @since version 1.0.0 + */ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs <= rhs); + } + + /*! + @brief comparison: greater than or equal + + Compares whether one JSON value @a lhs is greater than or equal to another + JSON value by calculating `not (lhs < rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than or equal to @a rhs + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greaterequal} + + @since version 1.0.0 + */ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs < rhs); + } + + /// @} + + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ + + /*! + @brief serialize to stream + + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. The + indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. + + @note During serializaion, the locale and the precision of the output + stream @a o are changed. The original values are restored when the + function returns. + + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize + + @return the stream @a o + + @complexity Linear. + + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} + + @since version 1.0.0 + */ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = (o.width() > 0); + const auto indentation = (pretty_print ? o.width() : 0); + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // fix locale problems + const auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator)); + // set precision + + // 6, 15 or 16 digits of precision allows round-trip IEEE 754 + // string->float->string, string->double->string or string->long + // double->string; to be safe, we read this value from + // std::numeric_limits::digits10 + const auto old_precision = o.precision(std::numeric_limits::digits10); + + // do the actual serialization + j.dump(o, pretty_print, static_cast(indentation)); + + // reset locale and precision + o.imbue(old_locale); + o.precision(old_precision); + return o; + } + + /*! + @brief serialize to stream + @copydoc operator<<(std::ostream&, const basic_json&) + */ + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } + + /// @} + + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /*! + @brief deserialize from an array + + This function reads from an array of 1-byte values. + + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @since version 2.0.3 + */ + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + + /*! + @brief deserialize from string literal + + @tparam CharT character/literal type with size of 1 byte + @param[in] s string literal to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} + + @sa @ref parse(std::istream&, const parser_callback_t) for a version that + reads from an input stream + + @since version 1.0.0 (originally for @ref string_t) + */ + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> + static basic_json parse(const CharPT s, + const parser_callback_t cb = nullptr) + { + return parser(reinterpret_cast(s), cb).parse(); + } + + /*! + @brief deserialize from stream + + @param[in,out] i stream to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} + + @sa @ref parse(const char*, const parser_callback_t) for a version + that reads from a string + + @since version 1.0.0 + */ + static basic_json parse(std::istream& i, + const parser_callback_t cb = nullptr) + { + return parser(i, cb).parse(); + } + + /*! + @copydoc parse(std::istream&, const parser_callback_t) + */ + static basic_json parse(std::istream&& i, + const parser_callback_t cb = nullptr) + { + return parser(i, cb).parse(); + } + + /*! + @brief deserialize from an iterator range with contiguous storage + + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. + + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam IteratorType iterator of container with contiguous storage + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} + + @since version 2.0.3 + */ + template::iterator_category>::value, int>::type = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr) + { + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + assert(std::accumulate(first, last, std::make_pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + // assertion to check that each element is 1 byte long + static_assert(sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + // if iterator range is empty, create a parser with an empty string + // to generate "unexpected EOF" error message + if (std::distance(first, last) <= 0) + { + return parser("").parse(); + } + + return parser(first, last, cb).parse(); + } + + /*! + @brief deserialize from a container with contiguous storage + + This function reads from a container with contiguous storage of 1-byte + values. Compatible container types include `std::vector`, `std::string`, + `std::array`, and `std::initializer_list`. User-defined containers can be + used as long as they implement random-access iterators and a contiguous + storage. + + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam ContiguousContainer container type with contiguous storage + @param[in] c container to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 + */ + template::value and + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits()))>::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } + + /*! + @brief deserialize from stream + + Deserializes an input stream to a JSON value. + + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to + + @throw std::invalid_argument in case of parse errors + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} + + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing + + @since version 1.0.0 + */ + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + j = parser(i).parse(); + return i; + } + + /*! + @brief deserialize from stream + @copydoc operator<<(basic_json&, std::istream&) + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + j = parser(i).parse(); + return i; + } + + /// @} + + +private: + /////////////////////////// + // convenience functions // + /////////////////////////// + + /*! + @brief return the type as string + + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. + + @return basically a string representation of a the @a m_type member + + @complexity Constant. + + @since version 1.0.0 + */ + std::string type_name() const + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::discarded: + return "discarded"; + default: + return "number"; + } + } + + /*! + @brief calculates the extra space to escape a JSON string + + @param[in] s the string to escape + @return the number of characters required to escape string @a s + + @complexity Linear in the length of string @a s. + */ + static std::size_t extra_space(const string_t& s) noexcept + { + return std::accumulate(s.begin(), s.end(), size_t{}, + [](size_t res, typename string_t::value_type c) + { + switch (c) + { + case '"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + { + // from c (1 byte) to \x (2 bytes) + return res + 1; + } + + default: + { + if (c >= 0x00 and c <= 0x1f) + { + // from c (1 byte) to \uxxxx (6 bytes) + return res + 5; + } + else + { + return res; + } + } + } + }); + } + + /*! + @brief escape a string + + Escape a string by replacing certain special characters by a sequence of + an escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. + + @param[in] s the string to escape + @return the escaped string + + @complexity Linear in the length of string @a s. + */ + static string_t escape_string(const string_t& s) + { + const auto space = extra_space(s); + if (space == 0) + { + return s; + } + + // create a result string of necessary size + string_t result(s.size() + space, '\\'); + std::size_t pos = 0; + + for (const auto& c : s) + { + switch (c) + { + // quotation mark (0x22) + case '"': + { + result[pos + 1] = '"'; + pos += 2; + break; + } + + // reverse solidus (0x5c) + case '\\': + { + // nothing to change + pos += 2; + break; + } + + // backspace (0x08) + case '\b': + { + result[pos + 1] = 'b'; + pos += 2; + break; + } + + // formfeed (0x0c) + case '\f': + { + result[pos + 1] = 'f'; + pos += 2; + break; + } + + // newline (0x0a) + case '\n': + { + result[pos + 1] = 'n'; + pos += 2; + break; + } + + // carriage return (0x0d) + case '\r': + { + result[pos + 1] = 'r'; + pos += 2; + break; + } + + // horizontal tab (0x09) + case '\t': + { + result[pos + 1] = 't'; + pos += 2; + break; + } + + default: + { + if (c >= 0x00 and c <= 0x1f) + { + // convert a number 0..15 to its hex representation + // (0..f) + static const char hexify[16] = + { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + // print character c as \uxxxx + for (const char m : + { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f] + }) + { + result[++pos] = m; + } + + ++pos; + } + else + { + // all other characters are added as-is + result[pos++] = c; + } + break; + } + } + } + + return result; + } + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. Note that + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + + @param[out] o stream to write to + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(std::ostream& o, + const bool pretty_print, + const unsigned int indent_step, + const unsigned int current_indent = 0) const + { + // variable to hold indentation for recursive calls + unsigned int new_indent = current_indent; + + switch (m_type) + { + case value_t::object: + { + if (m_value.object->empty()) + { + o << "{}"; + return; + } + + o << "{"; + + // increase indentation + if (pretty_print) + { + new_indent += indent_step; + o << "\n"; + } + + for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i) + { + if (i != m_value.object->cbegin()) + { + o << (pretty_print ? ",\n" : ","); + } + o << string_t(new_indent, ' ') << "\"" + << escape_string(i->first) << "\":" + << (pretty_print ? " " : ""); + i->second.dump(o, pretty_print, indent_step, new_indent); + } + + // decrease indentation + if (pretty_print) + { + new_indent -= indent_step; + o << "\n"; + } + + o << string_t(new_indent, ' ') + "}"; + return; + } + + case value_t::array: + { + if (m_value.array->empty()) + { + o << "[]"; + return; + } + + o << "["; + + // increase indentation + if (pretty_print) + { + new_indent += indent_step; + o << "\n"; + } + + for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i) + { + if (i != m_value.array->cbegin()) + { + o << (pretty_print ? ",\n" : ","); + } + o << string_t(new_indent, ' '); + i->dump(o, pretty_print, indent_step, new_indent); + } + + // decrease indentation + if (pretty_print) + { + new_indent -= indent_step; + o << "\n"; + } + + o << string_t(new_indent, ' ') << "]"; + return; + } + + case value_t::string: + { + o << string_t("\"") << escape_string(*m_value.string) << "\""; + return; + } + + case value_t::boolean: + { + o << (m_value.boolean ? "true" : "false"); + return; + } + + case value_t::number_integer: + { + o << m_value.number_integer; + return; + } + + case value_t::number_unsigned: + { + o << m_value.number_unsigned; + return; + } + + case value_t::number_float: + { + if (m_value.number_float == 0) + { + // special case for zero to get "0.0"/"-0.0" + o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0"); + } + else + { + o << m_value.number_float; + } + return; + } + + case value_t::discarded: + { + o << ""; + return; + } + + case value_t::null: + { + o << "null"; + return; + } + } + } + +private: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + + +private: + /////////////// + // iterators // + /////////////// + + /*! + @brief an iterator for primitive JSON types + + This class models an iterator for primitive JSON types (boolean, number, + string). It's only purpose is to allow the iterator/const_iterator classes + to "iterate" over primitive values. Internally, the iterator is modeled by + a `difference_type` variable. Value begin_value (`0`) models the begin, + end_value (`1`) models past the end. + */ + class primitive_iterator_t + { + public: + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return (m_it == begin_value); + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return (m_it == end_value); + } + + /// return reference to the value to change and compare + operator difference_type& () noexcept + { + return m_it; + } + + /// return value to compare + constexpr operator difference_type () const noexcept + { + return m_it; + } + + private: + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = std::numeric_limits::denorm_min(); + }; + + /*! + @brief an iterator value + + @note This structure could easily be a union, but MSVC currently does not + allow unions members with complex constructors, see + https://github.com/nlohmann/json/pull/105. + */ + struct internal_iterator + { + /// iterator for JSON objects + typename object_t::iterator object_iterator; + /// iterator for JSON arrays + typename array_t::iterator array_iterator; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator; + + /// create an uninitialized internal_iterator + internal_iterator() noexcept + : object_iterator(), array_iterator(), primitive_iterator() + {} + }; + + /// proxy class for the iterator_wrapper functions + template + class iteration_proxy + { + private: + /// helper class for iteration + class iteration_proxy_internal + { + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + size_t array_index = 0; + + public: + explicit iteration_proxy_internal(IteratorType it) noexcept + : anchor(it) + {} + + /// dereference operator (needed for range-based for) + iteration_proxy_internal& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_internal& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// inequality operator (needed for range-based for) + bool operator!= (const iteration_proxy_internal& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + typename basic_json::string_t key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + //return std::to_string(array_index); + return QString::number(array_index).toInt(); + } + + // use key from the object + case value_t::object: + { + return anchor.key(); + } + + // use an empty key for all primitive types + default: + { + return ""; + } + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } + }; + + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) + : container(cont) + {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_internal begin() noexcept + { + return iteration_proxy_internal(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_internal end() noexcept + { + return iteration_proxy_internal(container.end()); + } + }; + +public: + /*! + @brief a const random access iterator for the @ref basic_json class + + This class implements a const iterator for the @ref basic_json class. From + this class, the @ref iterator class is derived. + + @note An iterator is called *initialized* when a pointer to a JSON value + has been set (e.g., by a constructor or a copy assignment). If the + iterator is default-constructed, it is *uninitialized* and most + methods are undefined. **The library uses assertions to detect calls + on uninitialized iterators.** + + @requirement The class satisfies the following concept requirements: + - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): + The iterator that can be moved to point (forward and backward) to any + element in constant time. + + @since version 1.0.0 + */ + class const_iterator : public std::iterator + { + /// allow basic_json to access private members + friend class basic_json; + + public: + /// the type of the values when the iterator is dereferenced + using value_type = typename basic_json::value_type; + /// a type to represent differences between iterators + using difference_type = typename basic_json::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename basic_json::const_pointer; + /// defines a reference to the type iterated over (value_type) + using reference = typename basic_json::const_reference; + /// the category of the iterator + using iterator_category = std::bidirectional_iterator_tag; + + /// default constructor + const_iterator() = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit const_iterator(pointer object) noexcept + : m_object(object) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case basic_json::value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @brief copy constructor given a non-const iterator + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + explicit const_iterator(const iterator& other) noexcept + : m_object(other.m_object) + { + if (m_object != nullptr) + { + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + m_it.object_iterator = other.m_it.object_iterator; + break; + } + + case basic_json::value_t::array: + { + m_it.array_iterator = other.m_it.array_iterator; + break; + } + + default: + { + m_it.primitive_iterator = other.m_it.primitive_iterator; + break; + } + } + } + } + + /*! + @brief copy constructor + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + const_iterator(const const_iterator& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief copy assignment + @param[in,out] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + const_iterator& operator=(const_iterator other) noexcept( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_object, other.m_object); + std::swap(m_it, other.m_it); + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case basic_json::value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case basic_json::value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case basic_json::value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case basic_json::value_t::null: + { + throw std::out_of_range("cannot get value"); + } + + default: + { + if (m_it.primitive_iterator.is_begin()) + { + return *m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case basic_json::value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (m_it.primitive_iterator.is_begin()) + { + return m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator& operator++() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator operator--(int) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator& operator--() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const const_iterator& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { + throw std::domain_error("cannot compare iterators of different containers"); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + return (m_it.object_iterator == other.m_it.object_iterator); + } + + case basic_json::value_t::array: + { + return (m_it.array_iterator == other.m_it.array_iterator); + } + + default: + { + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const const_iterator& other) const + { + return not operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const const_iterator& other) const + { + // if objects are not the same, the comparison is undefined + if (m_object != other.m_object) + { + throw std::domain_error("cannot compare iterators of different containers"); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + throw std::domain_error("cannot compare order of object iterators"); + } + + case basic_json::value_t::array: + { + return (m_it.array_iterator < other.m_it.array_iterator); + } + + default: + { + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const const_iterator& other) const + { + return not other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const const_iterator& other) const + { + return not operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const const_iterator& other) const + { + return not operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator& operator+=(difference_type i) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + throw std::domain_error("cannot use offsets with object iterators"); + } + + case basic_json::value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const_iterator operator-(difference_type i) + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const const_iterator& other) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + throw std::domain_error("cannot use offsets with object iterators"); + } + + case basic_json::value_t::array: + { + return m_it.array_iterator - other.m_it.array_iterator; + } + + default: + { + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case basic_json::value_t::object: + { + throw std::domain_error("cannot use operator[] for object iterators"); + } + + case basic_json::value_t::array: + { + return *std::next(m_it.array_iterator, n); + } + + case basic_json::value_t::null: + { + throw std::out_of_range("cannot get value"); + } + + default: + { + if (m_it.primitive_iterator == -n) + { + return *m_object; + } + else + { + throw std::out_of_range("cannot get value"); + } + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + typename object_t::key_type key() const + { + assert(m_object != nullptr); + + if (m_object->is_object()) + { + return m_it.object_iterator->first; + } + else + { + throw std::domain_error("cannot use key() for non-object iterators"); + } + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator m_it = internal_iterator(); + }; + + /*! + @brief a mutable random access iterator for the @ref basic_json class + + @requirement The class satisfies the following concept requirements: + - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): + The iterator that can be moved to point (forward and backward) to any + element in constant time. + - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): + It is possible to write to the pointed-to element. + + @since version 1.0.0 + */ + class iterator : public const_iterator + { + public: + using base_iterator = const_iterator; + using pointer = typename basic_json::pointer; + using reference = typename basic_json::reference; + + /// default constructor + iterator() = default; + + /// constructor for a given JSON instance + explicit iterator(pointer object) noexcept + : base_iterator(object) + {} + + /// copy constructor + iterator(const iterator& other) noexcept + : base_iterator(other) + {} + + /// copy assignment + iterator& operator=(iterator other) noexcept( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + base_iterator::operator=(other); + return *this; + } + + /// return a reference to the value pointed to by the iterator + reference operator*() const + { + return const_cast(base_iterator::operator*()); + } + + /// dereference the iterator + pointer operator->() const + { + return const_cast(base_iterator::operator->()); + } + + /// post-increment (it++) + iterator operator++(int) + { + iterator result = *this; + base_iterator::operator++(); + return result; + } + + /// pre-increment (++it) + iterator& operator++() + { + base_iterator::operator++(); + return *this; + } + + /// post-decrement (it--) + iterator operator--(int) + { + iterator result = *this; + base_iterator::operator--(); + return result; + } + + /// pre-decrement (--it) + iterator& operator--() + { + base_iterator::operator--(); + return *this; + } + + /// add to iterator + iterator& operator+=(difference_type i) + { + base_iterator::operator+=(i); + return *this; + } + + /// subtract from iterator + iterator& operator-=(difference_type i) + { + base_iterator::operator-=(i); + return *this; + } + + /// add to iterator + iterator operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + /// subtract from iterator + iterator operator-(difference_type i) + { + auto result = *this; + result -= i; + return result; + } + + /// return difference + difference_type operator-(const iterator& other) const + { + return base_iterator::operator-(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return const_cast(base_iterator::operator[](n)); + } + + /// return the value of an iterator + reference value() const + { + return const_cast(base_iterator::value()); + } + }; + + /*! + @brief a template for a reverse iterator class + + @tparam Base the base iterator type to reverse. Valid types are @ref + iterator (to create @ref reverse_iterator) and @ref const_iterator (to + create @ref const_reverse_iterator). + + @requirement The class satisfies the following concept requirements: + - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): + The iterator that can be moved to point (forward and backward) to any + element in constant time. + - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + + @since version 1.0.0 + */ + template + class json_reverse_iterator : public std::reverse_iterator + { + public: + /// shortcut to the reverse iterator adaptor + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) + {} + + /// create reverse iterator from base class + json_reverse_iterator(const base_iterator& it) noexcept + : base_iterator(it) + {} + + /// post-increment (it++) + json_reverse_iterator operator++(int) + { + return base_iterator::operator++(1); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + base_iterator::operator++(); + return *this; + } + + /// post-decrement (it--) + json_reverse_iterator operator--(int) + { + return base_iterator::operator--(1); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + base_iterator::operator--(); + return *this; + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + base_iterator::operator+=(i); + return *this; + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return this->base() - other.base(); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + typename object_t::key_type key() const + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } + }; + + +private: + ////////////////////// + // lexer and parser // + ////////////////////// + + /*! + @brief lexical analysis + + This class organizes the lexical analysis during JSON deserialization. The + core of it is a scanner generated by [re2c](http://re2c.org) that + processes a buffer and recognizes tokens according to RFC 7159. + */ + class lexer + { + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_number, ///< a number -- use get_number() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input ///< indicating the end of the input buffer + }; + + /// the char type to use in the lexer + using lexer_char_t = unsigned char; + + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_content(buff) + { + assert(m_content != nullptr); + m_start = m_cursor = m_content; + m_limit = m_content + len; + } + + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_line_buffer() + { + // fill buffer + fill_line_buffer(); + + // skip UTF-8 byte-order mark + if (m_line_buffer.size() >= 3 and m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF") + { + m_line_buffer[0] = ' '; + m_line_buffer[1] = ' '; + m_line_buffer[2] = ' '; + } + } + + // switch off unwanted functions (due to pointer members) + lexer() = delete; + lexer(const lexer&) = delete; + lexer operator=(const lexer&) = delete; + + /*! + @brief create a string from one or two Unicode code points + + There are two cases: (1) @a codepoint1 is in the Basic Multilingual + Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2) + @a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to + represent a code point above U+FFFF. + + @param[in] codepoint1 the code point (can be high surrogate) + @param[in] codepoint2 the code point (can be low surrogate or 0) + + @return string representation of the code point; the length of the + result string is between 1 and 4 characters. + + @throw std::out_of_range if code point is > 0x10ffff; example: `"code + points above 0x10FFFF are invalid"` + @throw std::invalid_argument if the low surrogate is invalid; example: + `""missing or wrong low surrogate""` + + @complexity Constant. + + @see + */ + static string_t to_unicode(const std::size_t codepoint1, + const std::size_t codepoint2 = 0) + { + // calculate the code point from the given code points + std::size_t codepoint = codepoint1; + + // check if codepoint1 is a high surrogate + if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) + { + // check if codepoint2 is a low surrogate + if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) + { + codepoint = + // high surrogate occupies the most significant 22 bits + (codepoint1 << 10) + // low surrogate occupies the least significant 15 bits + + codepoint2 + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00; + } + else + { + throw std::invalid_argument("missing or wrong low surrogate"); + } + } + + string_t result; + + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + result.append(1, static_cast(codepoint)); + } + else if (codepoint <= 0x7ff) + { + // 2-byte characters: 110xxxxx 10xxxxxx + result.append(1, static_cast(0xC0 | ((codepoint >> 6) & 0x1F))); + result.append(1, static_cast(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0xffff) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast(0xE0 | ((codepoint >> 12) & 0x0F))); + result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast(0x80 | (codepoint & 0x3F))); + } + else if (codepoint <= 0x10ffff) + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + result.append(1, static_cast(0xF0 | ((codepoint >> 18) & 0x07))); + result.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + result.append(1, static_cast(0x80 | (codepoint & 0x3F))); + } + else + { + throw std::out_of_range("code points above 0x10FFFF are invalid"); + } + + return result; + } + + /// return name of values of type token_type (only used for errors) + static std::string token_type_name(const token_type t) + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_number: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + default: + { + // catch non-enum values + return "unknown token"; // LCOV_EXCL_LINE + } + } + } + + /*! + This function implements a scanner for JSON. It is specified using + regular expressions that try to follow RFC 7159 as close as possible. + These regular expressions are then translated into a minimized + deterministic finite automaton (DFA) by the tool + [re2c](http://re2c.org). As a result, the translated code for this + function consists of a large block of code with `goto` jumps. + + @return the class of the next token read from the buffer + + @complexity Linear in the length of the input.\n + + Proposition: The loop below will always terminate for finite input.\n + + Proof (by contradiction): Assume a finite input. To loop forever, the + loop must never hit code with a `break` statement. The only code + snippets without a `break` statement are the continue statements for + whitespace and byte-order-marks. To loop forever, the input must be an + infinite sequence of whitespace or byte-order-marks. This contradicts + the assumption of finite input, q.e.d. + */ + token_type scan() + { + while (true) + { + // pointer for backtracking information + m_marker = nullptr; + + // remember the begin of the token + m_start = m_cursor; + assert(m_start != nullptr); + + + { + lexer_char_t yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 32, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 160, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + if ((m_limit - m_cursor) < 5) + { + fill_line_buffer(5); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + if (yych <= '[') + { + if (yych <= '-') + { + if (yych <= '"') + { + if (yych <= 0x00) + { + goto basic_json_parser_2; + } + if (yych <= '!') + { + goto basic_json_parser_4; + } + goto basic_json_parser_9; + } + else + { + if (yych <= '+') + { + goto basic_json_parser_4; + } + if (yych <= ',') + { + goto basic_json_parser_10; + } + goto basic_json_parser_12; + } + } + else + { + if (yych <= '9') + { + if (yych <= '/') + { + goto basic_json_parser_4; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + goto basic_json_parser_15; + } + else + { + if (yych <= ':') + { + goto basic_json_parser_17; + } + if (yych <= 'Z') + { + goto basic_json_parser_4; + } + goto basic_json_parser_19; + } + } + } + else + { + if (yych <= 'n') + { + if (yych <= 'e') + { + if (yych == ']') + { + goto basic_json_parser_21; + } + goto basic_json_parser_4; + } + else + { + if (yych <= 'f') + { + goto basic_json_parser_23; + } + if (yych <= 'm') + { + goto basic_json_parser_4; + } + goto basic_json_parser_24; + } + } + else + { + if (yych <= 'z') + { + if (yych == 't') + { + goto basic_json_parser_25; + } + goto basic_json_parser_4; + } + else + { + if (yych <= '{') + { + goto basic_json_parser_26; + } + if (yych == '}') + { + goto basic_json_parser_28; + } + goto basic_json_parser_4; + } + } + } +basic_json_parser_2: + ++m_cursor; + { + last_token_type = token_type::end_of_input; + break; + } +basic_json_parser_4: + ++m_cursor; +basic_json_parser_5: + { + last_token_type = token_type::parse_error; + break; + } +basic_json_parser_6: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + { + continue; + } +basic_json_parser_9: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych <= 0x1F) + { + goto basic_json_parser_5; + } + if (yych <= 0x7F) + { + goto basic_json_parser_31; + } + if (yych <= 0xC1) + { + goto basic_json_parser_5; + } + if (yych <= 0xF4) + { + goto basic_json_parser_31; + } + goto basic_json_parser_5; +basic_json_parser_10: + ++m_cursor; + { + last_token_type = token_type::value_separator; + break; + } +basic_json_parser_12: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_5; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + if (yych <= '9') + { + goto basic_json_parser_15; + } + goto basic_json_parser_5; +basic_json_parser_13: + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_43; + } + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + } +basic_json_parser_14: + { + last_token_type = token_type::value_number; + break; + } +basic_json_parser_15: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + fill_line_buffer(3); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 64) + { + goto basic_json_parser_15; + } + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_43; + } + goto basic_json_parser_14; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + goto basic_json_parser_14; + } +basic_json_parser_17: + ++m_cursor; + { + last_token_type = token_type::name_separator; + break; + } +basic_json_parser_19: + ++m_cursor; + { + last_token_type = token_type::begin_array; + break; + } +basic_json_parser_21: + ++m_cursor; + { + last_token_type = token_type::end_array; + break; + } +basic_json_parser_23: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'a') + { + goto basic_json_parser_45; + } + goto basic_json_parser_5; +basic_json_parser_24: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'u') + { + goto basic_json_parser_46; + } + goto basic_json_parser_5; +basic_json_parser_25: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'r') + { + goto basic_json_parser_47; + } + goto basic_json_parser_5; +basic_json_parser_26: + ++m_cursor; + { + last_token_type = token_type::begin_object; + break; + } +basic_json_parser_28: + ++m_cursor; + { + last_token_type = token_type::end_object; + break; + } +basic_json_parser_30: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; +basic_json_parser_31: + if (yybm[0 + yych] & 128) + { + goto basic_json_parser_30; + } + if (yych <= 0xE0) + { + if (yych <= '\\') + { + if (yych <= 0x1F) + { + goto basic_json_parser_32; + } + if (yych <= '"') + { + goto basic_json_parser_33; + } + goto basic_json_parser_35; + } + else + { + if (yych <= 0xC1) + { + goto basic_json_parser_32; + } + if (yych <= 0xDF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_37; + } + } + else + { + if (yych <= 0xEF) + { + if (yych == 0xED) + { + goto basic_json_parser_39; + } + goto basic_json_parser_38; + } + else + { + if (yych <= 0xF0) + { + goto basic_json_parser_40; + } + if (yych <= 0xF3) + { + goto basic_json_parser_41; + } + if (yych <= 0xF4) + { + goto basic_json_parser_42; + } + } + } +basic_json_parser_32: + m_cursor = m_marker; + if (yyaccept == 0) + { + goto basic_json_parser_5; + } + else + { + goto basic_json_parser_14; + } +basic_json_parser_33: + ++m_cursor; + { + last_token_type = token_type::value_string; + break; + } +basic_json_parser_35: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 'e') + { + if (yych <= '/') + { + if (yych == '"') + { + goto basic_json_parser_30; + } + if (yych <= '.') + { + goto basic_json_parser_32; + } + goto basic_json_parser_30; + } + else + { + if (yych <= '\\') + { + if (yych <= '[') + { + goto basic_json_parser_32; + } + goto basic_json_parser_30; + } + else + { + if (yych == 'b') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + } + } + else + { + if (yych <= 'q') + { + if (yych <= 'f') + { + goto basic_json_parser_30; + } + if (yych == 'n') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 's') + { + if (yych <= 'r') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 't') + { + goto basic_json_parser_30; + } + if (yych <= 'u') + { + goto basic_json_parser_48; + } + goto basic_json_parser_32; + } + } + } +basic_json_parser_36: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; +basic_json_parser_37: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x9F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_38: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_39: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x9F) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_40: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x8F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_41: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_42: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x8F) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_43: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_49; + } + goto basic_json_parser_32; +basic_json_parser_44: + yych = *++m_cursor; + if (yych <= ',') + { + if (yych == '+') + { + goto basic_json_parser_51; + } + goto basic_json_parser_32; + } + else + { + if (yych <= '-') + { + goto basic_json_parser_51; + } + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_52; + } + goto basic_json_parser_32; + } +basic_json_parser_45: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_54; + } + goto basic_json_parser_32; +basic_json_parser_46: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_55; + } + goto basic_json_parser_32; +basic_json_parser_47: + yych = *++m_cursor; + if (yych == 'u') + { + goto basic_json_parser_56; + } + goto basic_json_parser_32; +basic_json_parser_48: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_57; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_57; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_57; + } + goto basic_json_parser_32; + } +basic_json_parser_49: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + fill_line_buffer(3); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 'D') + { + if (yych <= '/') + { + goto basic_json_parser_14; + } + if (yych <= '9') + { + goto basic_json_parser_49; + } + goto basic_json_parser_14; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + goto basic_json_parser_14; + } +basic_json_parser_51: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych >= ':') + { + goto basic_json_parser_32; + } +basic_json_parser_52: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '/') + { + goto basic_json_parser_14; + } + if (yych <= '9') + { + goto basic_json_parser_52; + } + goto basic_json_parser_14; +basic_json_parser_54: + yych = *++m_cursor; + if (yych == 's') + { + goto basic_json_parser_58; + } + goto basic_json_parser_32; +basic_json_parser_55: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_59; + } + goto basic_json_parser_32; +basic_json_parser_56: + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_61; + } + goto basic_json_parser_32; +basic_json_parser_57: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_63; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_63; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_63; + } + goto basic_json_parser_32; + } +basic_json_parser_58: + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_64; + } + goto basic_json_parser_32; +basic_json_parser_59: + ++m_cursor; + { + last_token_type = token_type::literal_null; + break; + } +basic_json_parser_61: + ++m_cursor; + { + last_token_type = token_type::literal_true; + break; + } +basic_json_parser_63: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_66; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_66; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_66; + } + goto basic_json_parser_32; + } +basic_json_parser_64: + ++m_cursor; + { + last_token_type = token_type::literal_false; + break; + } +basic_json_parser_66: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_30; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + } + + } + + return last_token_type; + } + + /*! + @brief append data from the stream to the line buffer + + This function is called by the scan() function when the end of the + buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be + incremented without leaving the limits of the line buffer. Note re2c + decides when to call this function. + + If the lexer reads from contiguous storage, there is no trailing null + byte. Therefore, this function must make sure to add these padding + null bytes. + + If the lexer reads from an input stream, this function reads the next + line of the input. + + @pre + p p p p p p u u u u u x . . . . . . + ^ ^ ^ ^ + m_content m_start | m_limit + m_cursor + + @post + u u u u u x x x x x x x . . . . . . + ^ ^ ^ + | m_cursor m_limit + m_start + m_content + */ + void fill_line_buffer(size_t n = 0) + { + // number of processed characters (p) + const auto offset_start = m_start - m_content; + // offset for m_marker wrt. to m_start + const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start; + // number of unprocessed characters (u) + const auto offset_cursor = m_cursor - m_start; + + // no stream is used or end of file is reached + if (m_stream == nullptr or m_stream->eof()) + { + // skip this part if we are already using the line buffer + if (m_start != reinterpret_cast(m_line_buffer.data())) + { + // copy unprocessed characters to line buffer + m_line_buffer.clear(); + for (m_cursor = m_start; m_cursor != m_limit; ++m_cursor) + { + m_line_buffer.append(1, static_cast(*m_cursor)); + } + } + + // append n characters to make sure that there is sufficient + // space between m_cursor and m_limit + m_line_buffer.append(1, '\x00'); + m_line_buffer.append(n - 1, '\x01'); + } + else + { + // delete processed characters from line buffer + m_line_buffer.erase(0, static_cast(offset_start)); + // read next line from input stream + std::string line; + std::getline(*m_stream, line, '\n'); + // add line with newline symbol to the line buffer + m_line_buffer += line + "\n"; + } + + // set pointers + m_content = reinterpret_cast(m_line_buffer.c_str()); + assert(m_content != nullptr); + m_start = m_content; + m_marker = m_start + offset_marker; + m_cursor = m_start + offset_cursor; + m_limit = m_start + m_line_buffer.size(); + } + + /// return string representation of last read token + string_t get_token_string() const + { + assert(m_start != nullptr); + return string_t(reinterpret_cast(m_start), + static_cast(m_cursor - m_start)); + } + + /*! + @brief return string value for string tokens + + The function iterates the characters between the opening and closing + quotes of the string value. The complete string is the range + [m_start,m_cursor). Consequently, we iterate from m_start+1 to + m_cursor-1. + + We differentiate two cases: + + 1. Escaped characters. In this case, a new character is constructed + according to the nature of the escape. Some escapes create new + characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied + as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape + `"\\uxxxx"` need special care. In this case, to_unicode takes care + of the construction of the values. + 2. Unescaped characters are copied as is. + + @pre `m_cursor - m_start >= 2`, meaning the length of the last token + is at least 2 bytes which is trivially true for any string (which + consists of at least two quotes). + + " c1 c2 c3 ... " + ^ ^ + m_start m_cursor + + @complexity Linear in the length of the string.\n + + Lemma: The loop body will always terminate.\n + + Proof (by contradiction): Assume the loop body does not terminate. As + the loop body does not contain another loop, one of the called + functions must never return. The called functions are `std::strtoul` + and to_unicode. Neither function can loop forever, so the loop body + will never loop forever which contradicts the assumption that the loop + body does not terminate, q.e.d.\n + + Lemma: The loop condition for the for loop is eventually false.\n + + Proof (by contradiction): Assume the loop does not terminate. Due to + the above lemma, this can only be due to a tautological loop + condition; that is, the loop condition i < m_cursor - 1 must always be + true. Let x be the change of i for any loop iteration. Then + m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This + can be rephrased to m_cursor - m_start - 2 > x. With the + precondition, we x <= 0, meaning that the loop condition holds + indefinitly if i is always decreased. However, observe that the value + of i is strictly increasing with each iteration, as it is incremented + by 1 in the iteration expression and never decremented inside the loop + body. Hence, the loop condition will eventually be false which + contradicts the assumption that the loop condition is a tautology, + q.e.d. + + @return string value of current token without opening and closing + quotes + @throw std::out_of_range if to_unicode fails + */ + string_t get_string() const + { + assert(m_cursor - m_start >= 2); + + string_t result; + result.reserve(static_cast(m_cursor - m_start - 2)); + + // iterate the result between the quotes + for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i) + { + // process escaped characters + if (*i == '\\') + { + // read next character + ++i; + + switch (*i) + { + // the default escapes + case 't': + { + result += "\t"; + break; + } + case 'b': + { + result += "\b"; + break; + } + case 'f': + { + result += "\f"; + break; + } + case 'n': + { + result += "\n"; + break; + } + case 'r': + { + result += "\r"; + break; + } + case '\\': + { + result += "\\"; + break; + } + case '/': + { + result += "/"; + break; + } + case '"': + { + result += "\""; + break; + } + + // unicode + case 'u': + { + // get code xxxx from uxxxx + auto codepoint = std::strtoul(std::string(reinterpret_cast(i + 1), + 4).c_str(), nullptr, 16); + + // check if codepoint is a high surrogate + if (codepoint >= 0xD800 and codepoint <= 0xDBFF) + { + // make sure there is a subsequent unicode + if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') + { + throw std::invalid_argument("missing low surrogate"); + } + + // get code yyyy from uxxxx\uyyyy + auto codepoint2 = std::strtoul(std::string(reinterpret_cast + (i + 7), 4).c_str(), nullptr, 16); + result += to_unicode(codepoint, codepoint2); + // skip the next 10 characters (xxxx\uyyyy) + i += 10; + } + else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) + { + // we found a lone low surrogate + throw std::invalid_argument("missing high surrogate"); + } + else + { + // add unicode character(s) + result += to_unicode(codepoint); + // skip the next four characters (xxxx) + i += 4; + } + break; + } + } + } + else + { + // all other characters are just copied to the end of the + // string + result.append(1, static_cast(*i)); + } + } + + return result; + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast(nullptr). + + @param[in] type the @ref number_float_t in use + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + long double str_to_float_t(long double* /* type */, char** endptr) const + { + return QString::number(reinterpret_cast(m_start)).toDouble(); + + //return std::strtold(reinterpret_cast(m_start), endptr); + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast(nullptr). + + @param[in] type the @ref number_float_t in use + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + double str_to_float_t(double* /* type */, char** endptr) const + { + return std::strtod(reinterpret_cast(m_start), endptr); + } + + /*! + @brief parse floating point number + + This function (and its overloads) serves to select the most approprate + standard floating point number parsing function based on the type + supplied via the first parameter. Set this to @a + static_cast(nullptr). + + @param[in] type the @ref number_float_t in use + + @param[in,out] endptr recieves a pointer to the first character after + the number + + @return the floating point number + */ + float str_to_float_t(float* /* type */, char** endptr) const + { + return QString::number(reinterpret_cast(m_start)).toFloat(); + //return std::strtof(reinterpret_cast(m_start), endptr); + } + + /*! + @brief return number value for number tokens + + This function translates the last token into the most appropriate + number type (either integer, unsigned integer or floating point), + which is passed back to the caller via the result parameter. + + This function parses the integer component up to the radix point or + exponent while collecting information about the 'floating point + representation', which it stores in the result parameter. If there is + no radix point or exponent, and the number can fit into a @ref + number_integer_t or @ref number_unsigned_t then it sets the result + parameter accordingly. + + If the number is a floating point number the number is then parsed + using @a std:strtod (or @a std:strtof or @a std::strtold). + + @param[out] result @ref basic_json object to receive the number, or + NAN if the conversion read past the current token. The latter case + needs to be treated by the caller function. + */ + void get_number(basic_json& result) const + { + assert(m_start != nullptr); + + const lexer::lexer_char_t* curptr = m_start; + + // accumulate the integer conversion result (unsigned for now) + number_unsigned_t value = 0; + + // maximum absolute value of the relevant integer type + number_unsigned_t max; + + // temporarily store the type to avoid unecessary bitfield access + value_t type; + + // look for sign + if (*curptr == '-') + { + type = value_t::number_integer; + max = static_cast((std::numeric_limits::max)()) + 1; + curptr++; + } + else + { + type = value_t::number_unsigned; + max = static_cast((std::numeric_limits::max)()); + } + + // count the significant figures + for (; curptr < m_cursor; curptr++) + { + // quickly skip tests if a digit + if (*curptr < '0' || *curptr > '9') + { + if (*curptr == '.') + { + // don't count '.' but change to float + type = value_t::number_float; + continue; + } + // assume exponent (if not then will fail parse): change to + // float, stop counting and record exponent details + type = value_t::number_float; + break; + } + + // skip if definitely not an integer + if (type != value_t::number_float) + { + // multiply last value by ten and add the new digit + auto temp = value * 10 + *curptr - '0'; + + // test for overflow + if (temp < value || temp > max) + { + // overflow + type = value_t::number_float; + } + else + { + // no overflow - save it + value = temp; + } + } + } + + // save the value (if not a float) + if (type == value_t::number_unsigned) + { + result.m_value.number_unsigned = value; + } + else if (type == value_t::number_integer) + { + result.m_value.number_integer = -static_cast(value); + } + else + { + // parse with strtod + result.m_value.number_float = str_to_float_t(static_cast(nullptr), NULL); + + // replace infinity and NAN by null + if (not std::isfinite(result.m_value.number_float)) + { + type = value_t::null; + result.m_value = basic_json::json_value(); + } + } + + // save the type + result.m_type = type; + } + + private: + /// optional input stream + std::istream* m_stream = nullptr; + /// line buffer buffer for m_stream + string_t m_line_buffer {}; + /// the buffer pointer + const lexer_char_t* m_content = nullptr; + /// pointer to the beginning of the current symbol + const lexer_char_t* m_start = nullptr; + /// pointer for backtracking information + const lexer_char_t* m_marker = nullptr; + /// pointer to the current symbol + const lexer_char_t* m_cursor = nullptr; + /// pointer to the end of the buffer + const lexer_char_t* m_limit = nullptr; + /// the last token type + token_type last_token_type = token_type::end_of_input; + }; + + /*! + @brief syntax analysis + + This class implements a recursive decent parser. + */ + class parser + { + public: + /// a parser reading from a string literal + parser(const char* buff, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast(buff), strlen(buff)) + {} + + /// a parser reading from an input stream + parser(std::istream& is, const parser_callback_t cb = nullptr) + : callback(cb), m_lexer(is) + {} + + /// a parser reading from an iterator range with contiguous storage + template::iterator_category, std::random_access_iterator_tag>::value + , int>::type + = 0> + parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast(&(*first)), + static_cast(std::distance(first, last))) + {} + + /// public parser interface + basic_json parse() + { + // read first token + get_token(); + + basic_json result = parse_internal(true); + result.assert_invariant(); + + expect(lexer::token_type::end_of_input); + + // return parser result and replace it with null in case the + // top-level value was discarded by the callback function + return result.is_discarded() ? basic_json() : std::move(result); + } + + private: + /// the actual parser + basic_json parse_internal(bool keep) + { + auto result = basic_json(value_t::discarded); + + switch (last_token) + { + case lexer::token_type::begin_object: + { + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) + { + // explicitly set result to object to cope with {} + result.m_type = value_t::object; + result.m_value = value_t::object; + } + + // read next token + get_token(); + + // closing } -> we are done + if (last_token == lexer::token_type::end_object) + { + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) + { + result = basic_json(value_t::discarded); + } + return result; + } + + // no comma is expected here + unexpect(lexer::token_type::value_separator); + + // otherwise: parse key-value pairs + do + { + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) + { + get_token(); + } + + // store key + expect(lexer::token_type::value_string); + const auto key = m_lexer.get_string(); + + bool keep_tag = false; + if (keep) + { + if (callback) + { + basic_json k(key); + keep_tag = callback(depth, parse_event_t::key, k); + } + else + { + keep_tag = true; + } + } + + // parse separator (:) + get_token(); + expect(lexer::token_type::name_separator); + + // parse and add value + get_token(); + auto value = parse_internal(keep); + if (keep and keep_tag and not value.is_discarded()) + { + result[key] = std::move(value); + } + } + while (last_token == lexer::token_type::value_separator); + + // closing } + expect(lexer::token_type::end_object); + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) + { + result = basic_json(value_t::discarded); + } + + return result; + } + + case lexer::token_type::begin_array: + { + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) + { + // explicitly set result to object to cope with [] + result.m_type = value_t::array; + result.m_value = value_t::array; + } + + // read next token + get_token(); + + // closing ] -> we are done + if (last_token == lexer::token_type::end_array) + { + get_token(); + if (callback and not callback(--depth, parse_event_t::array_end, result)) + { + result = basic_json(value_t::discarded); + } + return result; + } + + // no comma is expected here + unexpect(lexer::token_type::value_separator); + + // otherwise: parse values + do + { + // ugly, but could be fixed with loop reorganization + if (last_token == lexer::token_type::value_separator) + { + get_token(); + } + + // parse value + auto value = parse_internal(keep); + if (keep and not value.is_discarded()) + { + result.push_back(std::move(value)); + } + } + while (last_token == lexer::token_type::value_separator); + + // closing ] + expect(lexer::token_type::end_array); + get_token(); + if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) + { + result = basic_json(value_t::discarded); + } + + return result; + } + + case lexer::token_type::literal_null: + { + get_token(); + result.m_type = value_t::null; + break; + } + + case lexer::token_type::value_string: + { + const auto s = m_lexer.get_string(); + get_token(); + result = basic_json(s); + break; + } + + case lexer::token_type::literal_true: + { + get_token(); + result.m_type = value_t::boolean; + result.m_value = true; + break; + } + + case lexer::token_type::literal_false: + { + get_token(); + result.m_type = value_t::boolean; + result.m_value = false; + break; + } + + case lexer::token_type::value_number: + { + m_lexer.get_number(result); + get_token(); + break; + } + + default: + { + // the last token was unexpected + unexpect(last_token); + } + } + + if (keep and callback and not callback(depth, parse_event_t::value, result)) + { + result = basic_json(value_t::discarded); + } + return result; + } + + /// get next token from lexer + typename lexer::token_type get_token() + { + last_token = m_lexer.scan(); + return last_token; + } + + void expect(typename lexer::token_type t) const + { + if (t != last_token) + { + std::string error_msg = "parse error - unexpected "; + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : + lexer::token_type_name(last_token)); + error_msg += "; expected " + lexer::token_type_name(t); + throw std::invalid_argument(error_msg); + } + } + + void unexpect(typename lexer::token_type t) const + { + if (t == last_token) + { + std::string error_msg = "parse error - unexpected "; + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : + lexer::token_type_name(last_token)); + throw std::invalid_argument(error_msg); + } + } + + private: + /// current level of recursion + int depth = 0; + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + typename lexer::token_type last_token = lexer::token_type::uninitialized; + /// the lexer + lexer m_lexer; + }; + +public: + /*! + @brief JSON Pointer + + A JSON pointer defines a string syntax for identifying a specific value + within a JSON document. It can be used with functions `at` and + `operator[]`. Furthermore, JSON pointers are the base for JSON patches. + + @sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + class json_pointer + { + /// allow basic_json to access private members + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the + empty string is assumed which references the whole JSON + value + + @throw std::domain_error if reference token is nonempty and does not + begin with a slash (`/`); example: `"JSON pointer must be empty or + begin with /"` + @throw std::domain_error if a tilde (`~`) is not followed by `0` + (representing `~`) or `1` (representing `/`); example: `"escape error: + ~ must be followed with 0 or 1"` + + @liveexample{The example shows the construction several valid JSON + pointers as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`., + json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const noexcept + { + return std::accumulate(reference_tokens.begin(), + reference_tokens.end(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + private: + /// remove and return last reference pointer + std::string pop_back() + { + if (is_root()) + { + throw std::domain_error("JSON pointer has no parent"); + } + + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; + } + + /// return whether pointer points to the root document + bool is_root() const + { + return reference_tokens.empty(); + } + + json_pointer top() const + { + if (is_root()) + { + throw std::domain_error("JSON pointer has no parent"); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + */ + reference get_and_create(reference j) const + { + pointer result = &j; + + // in case no reference tokens exist, return a reference to the + // JSON value j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->m_type) + { + case value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case value_t::array: + { + // create an entry in the array + // + QString refToken(reference_token); + + result = &result->operator[](static_cast(refToken.toInt())); + break; + } + + /* + The following code is only reached if there exists a + reference token _and_ the current value is primitive. In + this case, we have an error situation, because primitive + values may only occur as single value; that is, with an + empty list of reference tokens. + */ + default: + { + throw std::domain_error("invalid value to unflatten"); + } + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries + to create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + */ + reference get_unchecked(pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->m_type == value_t::null) + { + // check if reference token is a number + const bool nums = std::all_of(reference_token.begin(), + reference_token.end(), + [](const char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object + // otherwise + if (nums or reference_token == "-") + { + *ptr = value_t::array; + } + else + { + *ptr = value_t::object; + } + } + + switch (ptr->m_type) + { + case value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case value_t::array: + { + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + throw std::domain_error("array index must not begin with '0'"); + } + + if (reference_token == "-") + { + // explicityly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + //ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + QString refToken(reference_token); + + ptr = &ptr->operator[](static_cast(refToken.toInt())); + } + break; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + reference get_checked(pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" always fails the range check + //throw std::out_of_range("array index '-' (" + + // std::to_string(ptr->m_value.array->size()) + + // ") is out of range"); + throw std::out_of_range("array index '-' (" + + QString::number(ptr->m_value.array->size()).toStdString() + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + throw std::domain_error("array index must not begin with '0'"); + } + + // note: at performs range check + //ptr = &ptr->at(static_cast(std::stoi(reference_token))); + + QString refToken(reference_token); + ptr = &ptr->at(static_cast(refToken.toInt())); + break; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + */ + const_reference get_unchecked(const_pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" cannot be used for const access + //throw std::out_of_range("array index '-' (" + + // std::to_string(ptr->m_value.array->size()) + + // ") is out of range"); + throw std::out_of_range("array index '-' (" + + QString::number(ptr->m_value.array->size()).toStdString() + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + throw std::domain_error("array index must not begin with '0'"); + } + + // use unchecked array access + //ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + + QString refToken(reference_token); + ptr = &ptr->operator[](static_cast(refToken.toInt())); + break; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + const_reference get_checked(const_pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" always fails the range check + //throw std::out_of_range("array index '-' (" + + // std::to_string(ptr->m_value.array->size()) + + // ") is out of range"); + throw std::out_of_range("array index '-' (" + + QString::number(ptr->m_value.array->size()).toStdString() + + ") is out of range"); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + throw std::domain_error("array index must not begin with '0'"); + } + + // note: at performs range check + QString refToken(reference_token); + ptr = &ptr->at(static_cast(refToken.toInt())); + + //ptr = &ptr->at(static_cast(std::stoi(reference_token))); + break; + } + + default: + { + throw std::out_of_range("unresolved reference token '" + reference_token + "'"); + } + } + } + + return *ptr; + } + + /// split the string input to reference tokens + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (reference_string[0] != '/') + { + throw std::domain_error("JSON pointer must be empty or begin with '/'"); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + size_t slash = reference_string.find_first_of("/", 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == string::npos+1 = 0 + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = slash + 1, + // find next slash + slash = reference_string.find_first_of("/", start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (size_t pos = reference_token.find_first_of("~"); + pos != std::string::npos; + pos = reference_token.find_first_of("~", pos + 1)) + { + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1')) + { + throw std::domain_error("escape error: '~' must be followed with '0' or '1'"); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @brief replace all occurrences of a substring by another string + + @param[in,out] s the string to manipulate + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f + + @return The string @a s where all occurrences of @a f are replaced + with @a t. + + @pre The search string @a f must not be empty. + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, + const std::string& f, + const std::string& t) + { + assert(not f.empty()); + + for ( + size_t pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t + pos = s.find(f, pos + t.size()) // find next occurrence of f + ); + } + + /// escape tilde and slash + static std::string escape(std::string s) + { + // escape "~"" to "~0" and "/" to "~1" + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape tilde and slash + static void unescape(std::string& s) + { + // first transform any occurrence of the sequence '~1' to '/' + replace_substring(s, "~1", "/"); + // then transform any occurrence of the sequence '~0' to '~' + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const basic_json& value, + basic_json& result) + { + switch (value.m_type) + { + case value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (size_t i = 0; i < value.m_value.array->size(); ++i) + { + //flatten(reference_string + "/" + std::to_string(i), + // value.m_value.array->operator[](i), result); + flatten(reference_string + "/" + QString::number(i).toStdString(), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), + element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + */ + static basic_json unflatten(const basic_json& value) + { + if (not value.is_object()) + { + throw std::domain_error("only objects can be unflattened"); + } + + basic_json result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (not element.second.is_primitive()) + { + throw std::domain_error("values in object must be primitive"); + } + + // assign value to reference pointed to by JSON pointer; Note + // that if the JSON pointer is "" (i.e., points to the whole + // value), function get_and_create returns a reference to + // result itself. An assignment will then create a primitive + // value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + private: + /// the reference tokens + std::vector reference_tokens {}; + }; + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. Similar to @ref operator[](const typename + object_t::key_type&), `null` values are created in arrays and objects if + necessary. + + In particular: + - If the JSON pointer points to an object key that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. + - If the JSON pointer points to an array index that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. All indices between the current maximum and the given + index are also filled with `null`. + - The special value `-` is treated as a synonym for the index past the + end. + + @param[in] ptr a JSON pointer + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,operatorjson_pointer} + + @since version 2.0.0 + */ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. The function does not change the JSON + value; no `null` values are created. In particular, the the special value + `-` yields an exception. + + @param[in] ptr JSON pointer to the desired element + + @return const reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} + + @since version 2.0.0 + */ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a reference to the element at with specified JSON pointer @a ptr, + with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,at_json_pointer} + + @since version 2.0.0 + */ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a const reference to the element at with specified JSON pointer @a + ptr, with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,at_json_pointer_const} + + @since version 2.0.0 + */ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /*! + @brief return flattened JSON value + + The function creates a JSON object whose keys are JSON pointers (see [RFC + 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all + primitive. The original JSON value can be restored using the @ref + unflatten() function. + + @return an object that maps JSON pointers to primitve values + + @note Empty objects and arrays are flattened to `null` and will not be + reconstructed correctly by the @ref unflatten() function. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a JSON object is flattened to an + object whose keys consist of JSON pointers.,flatten} + + @sa @ref unflatten() for the reverse function + + @since version 2.0.0 + */ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /*! + @brief unflatten a previously flattened JSON value + + The function restores the arbitrary nesting of a JSON value that has been + flattened before using the @ref flatten() function. The JSON value must + meet certain constraints: + 1. The value must be an object. + 2. The keys must be JSON pointers (see + [RFC 6901](https://tools.ietf.org/html/rfc6901)) + 3. The mapped values must be primitive JSON types. + + @return the original JSON from a flattened version + + @note Empty objects and arrays are flattened by @ref flatten() to `null` + values and can not unflattened to their original type. Apart from + this example, for a JSON value `j`, the following is always true: + `j == j.flatten().unflatten()`. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a flattened JSON object is + unflattened into the original nested JSON object.,unflatten} + + @sa @ref flatten() for the reverse function + + @since version 2.0.0 + */ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /*! + @brief applies a JSON patch + + [JSON Patch](http://jsonpatch.com) defines a JSON document structure for + expressing a sequence of operations to apply to a JSON) document. With + this funcion, a JSON Patch is applied to the current JSON value by + executing all operations from the patch. + + @param[in] json_patch JSON patch document + @return patched document + + @note The application of a patch is atomic: Either all operations succeed + and the patched document is returned or an exception is thrown. In + any case, the original value is not changed: the patch is applied + to a copy of the value. + + @throw std::out_of_range if a JSON pointer inside the patch could not + be resolved successfully in the current JSON value; example: `"key baz + not found"` + @throw invalid_argument if the JSON patch is malformed (e.g., mandatory + attributes are missing); example: `"operation add must have member path"` + + @complexity Linear in the size of the JSON value and the length of the + JSON patch. As usually only a fraction of the JSON value is affected by + the patch, the complexity can usually be neglected. + + @liveexample{The following code shows how a JSON patch is applied to a + value.,patch} + + @sa @ref diff -- create a JSON patch by comparing two JSON values + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const std::string op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.is_root()) + { + result = val; + } + else + { + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + + //const auto idx = std::stoi(last_path); + QString str(last_path); + const auto idx = str.toInt(); + if (static_cast(idx) > parent.size()) + { + // avoid undefined behavior + //throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); + throw std::out_of_range("array index " + QString::number(idx).toStdString() + " is out of range"); + + } + else + { + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + } + break; + } + + default: + { + // if there exists a parent it cannot be primitive + assert(false); // LCOV_EXCL_LINE + } + } + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [&result](json_pointer & ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (it != parent.end()) + { + parent.erase(it); + } + else + { + throw std::out_of_range("key '" + last_path + "' not found"); + } + } + else if (parent.is_array()) + { + // note erase performs range check + //parent.erase(static_cast(std::stoi(last_path))); + QString str(last_path); + parent.erase(static_cast(str.toInt())); + } + }; + + // type check + if (not json_patch.is_array()) + { + // a JSON patch must be an array of objects + throw std::invalid_argument("JSON patch must be an array of objects"); + } + + // iterate and apply th eoperations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json& + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (it == val.m_value.object->end()) + { + throw std::invalid_argument(error_msg + " must have member '" + member + "'"); + } + + // check if result is of type string + if (string_type and not it->second.is_string()) + { + throw std::invalid_argument(error_msg + " must have string member '" + member + "'"); + } + + // no error: return value + return it->second; + }; + + // type check + if (not val.is_object()) + { + throw std::invalid_argument("JSON patch must be an array of objects"); + } + + // collect mandatory members + const std::string op = get_value("op", "op", true); + const std::string path = get_value(op, "path", true); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const std::string from_path = get_value("move", "from", true); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const std::string from_path = get_value("copy", "from", true);; + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + result[ptr] = result.at(from_ptr); + break; + } + + case patch_operations::test: + { + bool success = false; + try + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + catch (std::out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (not success) + { + throw std::domain_error("unsuccessful: " + val.dump()); + } + + break; + } + + case patch_operations::invalid: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + throw std::invalid_argument("operation value '" + op + "' is invalid"); + } + } + } + + return result; + } + + /*! + @brief creates a diff as a JSON patch + + Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can + be changed into the value @a target by calling @ref patch function. + + @invariant For two JSON values @a source and @a target, the following code + yields always `true`: + @code {.cpp} + source.patch(diff(source, target)) == target; + @endcode + + @note Currently, only `remove`, `add`, and `replace` operations are + generated. + + @param[in] source JSON value to copare from + @param[in] target JSON value to copare against + @param[in] path helper value to create JSON pointers + + @return a JSON patch to convert the @a source to @a target + + @complexity Linear in the lengths of @a source and @a target. + + @liveexample{The following code shows how a JSON patch is created as a + diff for two JSON values.,diff} + + @sa @ref patch -- apply a JSON patch + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + + @since version 2.0.0 + */ + static basic_json diff(const basic_json& source, + const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, + {"path", path}, + {"value", target} + }); + } + else + { + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + size_t i = 0; + while (i < source.size() and i < target.size()) + { + // recursive call to compare array values at index i + //auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + auto temp_diff = diff(source[i], target[i], path + "/" + QString::number(i).toStdString()); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // i now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + QString::number(i).toStdString()} + //{"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/" + QString::number(i).toStdString()}, + //{"path", path + "/" + std::to_string(i)}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.begin(); it != source.end(); ++it) + { + // escape the key name to be used in a JSON patch + const auto key = json_pointer::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, + {"path", path + "/" + key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.begin(); it != target.end(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto key = json_pointer::escape(it.key()); + result.push_back( + { + {"op", "add"}, + {"path", path + "/" + key}, + {"value", it.value()} + }); + } + } + + break; + } + + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, + {"path", path}, + {"value", target} + }); + break; + } + } + } + + return result; + } + + /// @} +}; + + +///////////// +// presets // +///////////// + +/*! +@brief default JSON class + +This type is the default specialization of the @ref basic_json class which +uses the standard template types. + +@since version 1.0.0 +*/ +using json = basic_json<>; +} + + +/////////////////////// +// nonmember support // +/////////////////////// + +// specialization of std::swap, and std::hash +namespace std +{ +/*! +@brief exchanges the values of two JSON objects + +@since version 1.0.0 +*/ +template<> +inline void swap(nlohmann::json& j1, + nlohmann::json& j2) noexcept( + is_nothrow_move_constructible::value and + is_nothrow_move_assignable::value + ) +{ + j1.swap(j2); +} + +/// hash value for JSON objects +template<> +struct hash +{ + /*! + @brief return a hash value for a JSON object + + @since version 1.0.0 + */ + std::size_t operator()(const nlohmann::json& j) const + { + // a naive hashing via the string representation + const auto& h = hash(); + return h(j.dump()); + } +}; +} + +/*! +@brief user-defined string literal for JSON values + +This operator implements a user-defined string literal for JSON objects. It +can be used by adding `"_json"` to a string literal and returns a JSON object +if no parse error occurred. + +@param[in] s a string representation of a JSON object +@param[in] n the length of string @a s +@return a JSON object + +@since version 1.0.0 +*/ +inline nlohmann::json operator "" _json(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +/*! +@brief user-defined string literal for JSON pointer + +This operator implements a user-defined string literal for JSON Pointers. It +can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer +object if no parse error occurred. + +@param[in] s a string representation of a JSON Pointer +@param[in] n the length of string @a s +@return a JSON pointer object + +@since version 2.0.0 +*/ +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +// restore GCC/clang diagnostic settings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#endif + +#endif diff --git a/Websocket/query.cpp b/Websocket/query.cpp new file mode 100644 index 00000000..d7221ff8 --- /dev/null +++ b/Websocket/query.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "query.h" + +const std::string QueryKeys::ACTION = "action"; +const std::string QueryKeys::OBJECT_NAME = "object_name"; +const std::string QueryKeys::FILTER = "filter"; +const std::string QueryKeys::PARAMS = "params"; +const std::string QueryKeys::FIELD_NAMES = "field_names"; +const std::string QueryKeys::LAST = "last"; + +Query::Query() + : query(Json::object()) +{ +} + +Query::Query(const std::string &action, const std::string &objectName, const std::list> &filter, + const std::map ¶ms, const std::list &fields, bool last) +{ + this->action = action; + this->objectName = objectName; + this->filter = filter; + this->params = params; + this->fields = fields; + this->last = last; + + if (fields.size() == 0) { + this->fields.push_back("*"); + } + + query[QueryKeys::ACTION] = action; + query[QueryKeys::OBJECT_NAME] = objectName; + query[QueryKeys::FILTER] = Json(filter); + query[QueryKeys::PARAMS] = Json(params); + query[QueryKeys::FIELD_NAMES] = Json(fields); + query[QueryKeys::LAST] = last; +} + +Query +Query::fromJsonString(const std::string &jsonString) +{ + Query query; + Json jsonQuery = Json::parse(jsonString); + + std::cout << jsonQuery << std::endl; + + std::cout << jsonQuery[QueryKeys::LAST] << std::endl; + + query.setAction(jsonQuery[QueryKeys::ACTION]); + query.setLast(jsonQuery[QueryKeys::LAST]); + query.setObjectName(jsonQuery[QueryKeys::OBJECT_NAME]); + + Json list = jsonQuery[QueryKeys::FIELD_NAMES]; + + for (Json::iterator it =list.begin(); it != list.end(); ++it) { + query.fields.push_back(*it); + } + + if (query.fields.size() == 0) { + query.fields.push_back("*"); + } + + Json list2 = jsonQuery[QueryKeys::FILTER]; + + for (Json::iterator it = list2.begin(); it != list2.end(); ++it) { + query.filter.push_back(*it); + } + + Json map = jsonQuery[QueryKeys::PARAMS]; + + for (Json::iterator it = map.begin(); it != map.end(); ++it) { + std::cout << it.key() << " " << it.value().dump() << std::endl; + query.params[it.key()] = it.value().dump(); + } + + query.query[QueryKeys::ACTION] = query.action; + query.query[QueryKeys::OBJECT_NAME] = query.objectName; + query.query[QueryKeys::FILTER] = Json(query.filter); + query.query[QueryKeys::PARAMS] = Json(query.params); + query.query[QueryKeys::FIELD_NAMES] = Json(query.fields); + query.query[QueryKeys::LAST] = query.last; + + return query; +} + +void +Query::removeQuotes(std::string& string) +{ + std::cout << string.front() << std::endl; + string.erase(remove(string.begin(), string.end(), '\"'), string.end()); +} + +std::string +Query::toJsonString(const Query &query) +{ + return query.query.dump(); +} + +std::string +Query::toJsonString(const std::string &action, const std::string &objectName, const std::list> &filter, + const std::map ¶ms, const std::list &fields, bool last) +{ + Json jsonQuery; + + jsonQuery[QueryKeys::ACTION] = action; + jsonQuery[QueryKeys::OBJECT_NAME] = objectName; + jsonQuery[QueryKeys::FILTER] = Json(filter); + jsonQuery[QueryKeys::PARAMS] = Json(params); + jsonQuery[QueryKeys::FIELD_NAMES] = Json(fields); + jsonQuery[QueryKeys::LAST] = last; + + return jsonQuery.dump(); +} + +std::string +Query::toJsonString() +{ + return query.dump(); +} + +const std::string& +Query::getAction() const +{ + return action; +} + +void +Query::setAction(const std::string &action) +{ + Query::action = action; + removeQuotes(this->action); + query[QueryKeys::ACTION] = this->action; +} + +const std::string& +Query::getObjectName() const +{ + return objectName; +} + +void +Query::setObjectName(const std::string &objectName) +{ + Query::objectName = objectName; + removeQuotes(this->objectName); + query[QueryKeys::OBJECT_NAME] = this->objectName; +} + +const std::list>& +Query::getFilter() const +{ + return filter; +} + +void +Query::setFilter(const std::list> &filter) +{ + Query::filter = filter; + + for (auto f : this->filter) { + for (auto field : f) { + removeQuotes(field); + } + } + + query[QueryKeys::FILTER] = this->filter; +} + +const std::map& +Query::getParams() const +{ + return params; +} + +void +Query::setParams(const std::map ¶ms) +{ + Query::params = params; + query[QueryKeys::PARAMS] = this->params; +} + +const std::list& +Query::getFields() const +{ + return fields; +} + +void +Query::setFields(const std::list &fields) +{ + Query::fields = fields; + + for (auto field : this->fields) { + removeQuotes(field); + } + + query[QueryKeys::FIELD_NAMES] = this->fields; +} + +bool +Query::isLast() const +{ + return last; +} + +void +Query::setLast(int last) +{ + Query::last = last; + query[QueryKeys::LAST] = this->last; +} + +bool +Query::isEmpty() +{ + return query.empty(); +} diff --git a/Websocket/query.h b/Websocket/query.h new file mode 100644 index 00000000..193610b4 --- /dev/null +++ b/Websocket/query.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef QUERY_HPP +#define QUERY_HPP + +#include +#include +#include +#include + +#include "json.h" + +typedef struct QueryKeys { + static const std::string ACTION; + static const std::string OBJECT_NAME; + static const std::string FILTER; + static const std::string PARAMS; + static const std::string FIELD_NAMES; + static const std::string LAST; +} QueryKeys; + +// for convenience +typedef nlohmann::json Json; + +class Query +{ +public: + Query(); + + Query(const std::string& action, + const std::string& objectName, + const std::list>& filter, + const std::map& params, + const std::list& fields, + bool last); + + static Query + fromJsonString(const std::string &jsonString); + + static void + removeQuotes(std::string& string); + + static std::string + toJsonString(const Query &query); + + static std::string + toJsonString(const std::string& action, + const std::string& objectName, + const std::list>& filter, + const std::map& params, + const std::list& fields, + bool last); + + std::string + toJsonString(); + + const std::string& + getAction() const; + + void + setAction(const std::string &action); + + const std::string& + getObjectName() const; + + void + setObjectName(const std::string &objectName); + + const std::list>& + getFilter() const; + + void + setFilter(const std::list>& filter); + + const std::map& + getParams() const; + + void + setParams(const std::map& params); + + const std::list& + getFields() const; + + void + setFields(const std::list& fields); + + bool + isLast() const; + + void + setLast(int last); + + bool + isEmpty(); + +private: + Json query; + + std::string action; + std::string objectName; + std::list> filter; + std::map params; + std::list fields; + bool last; +}; + + +#endif //QUERY_HPP diff --git a/Websocket/tcp-server.cpp b/Websocket/tcp-server.cpp new file mode 100644 index 00000000..4eb5d29d --- /dev/null +++ b/Websocket/tcp-server.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "tcp-server.h" + +TcpServer::TcpServer(unsigned short port, long read_timeout) + : port(port) + , acceptor(io_service) + , read_timeout(read_timeout) +{} + +TcpServer::~TcpServer() +{} + +void +TcpServer::setHandler(const HandlerFunction &handler) +{ + this->handler = handler; +} + +void +TcpServer::start() +{ + if (io_service.stopped()) + io_service.reset(); + + boost::asio::ip::tcp::endpoint endpoint; + endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port); + + acceptor.open(endpoint.protocol()); + acceptor.set_option(boost::asio::socket_base::reuse_address(true)); + acceptor.bind(endpoint); + acceptor.listen(); + + accept(); + + //Set interrupt callbacks + + boost::asio::io_service io_service; + boost::asio::signal_set signals(io_service, SIGINT, SIGQUIT); + + signals.async_wait([this] (const boost::system::error_code &errorCode, int) { + std::cout << "Gracefully terminating tcp server" << std::endl; + this->io_service.reset(); + this->acceptor.cancel(); + }); + + io_service.run(); +} + +void +TcpServer::accept() +{ + //Create new socket for this connection + //Shared_ptr is used to pass temporary objects to the asynchronous functions + std::shared_ptr socket(new boost::asio::ip::tcp::socket(io_service)); + acceptor.async_accept(*socket, [this, socket](const boost::system::error_code &ec) { + accept(); + + if (ec) { + if (ec == boost::asio::error::operation_aborted) // when the socket is closed by someone + return; + } + + processIncomingData(socket); + }); +} + +void +TcpServer::processIncomingData(std::shared_ptr socket) +{ + // Set timeout on the following boost::asio::async-read or write function + std::shared_ptr timer; + if (read_timeout > 0) + timer = set_timeout_on_socket(socket, read_timeout); + + std::shared_ptr buffer(new boost::asio::streambuf()); + + boost::asio::async_read_until(*socket, *buffer, "\r\n\r\n", + [this, timer, buffer, socket](const boost::system::error_code& error, std::size_t bytes_transferred) { + if (read_timeout > 0) + timer->cancel(); + + if (error) { + std::cerr << "Boost error code is not null! ERROR: " << error << std::endl; + return; + } + + std::size_t bufferSize = buffer->size(); + buffer->commit(buffer->size()); + const uint8_t *data = boost::asio::buffer_cast(buffer->data()); + + std::string reply = handler(data, bufferSize); + + if (reply != "") { + + boost::asio::async_write(*socket, boost::asio::buffer(reply.c_str(), reply.size()), [this] + (boost::system::error_code ec, std::size_t /*length*/) + { + if (!ec) { + std::cout << "Reply sent!" << std::endl; + } else { + std::cerr << "ERROR! Reply not sent." << std::endl; + } + }); + + } + }); + +} + +std::shared_ptr +TcpServer::set_timeout_on_socket(std::shared_ptr socket, long seconds) +{ + std::shared_ptr timer(new boost::asio::deadline_timer(io_service)); + timer->expires_from_now(boost::posix_time::seconds(seconds)); + timer->async_wait([socket](const boost::system::error_code &ec) { + if (!ec) { + boost::system::error_code ec; + std::cout << "Connection timeout!" << std::endl; + socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + socket->lowest_layer().close(); + } + }); + return timer; +} diff --git a/Websocket/tcp-server.h b/Websocket/tcp-server.h new file mode 100644 index 00000000..f93d1b43 --- /dev/null +++ b/Websocket/tcp-server.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TCP_SERVER_H +#define TCP_SERVER_H + +#include +#include +#include + +typedef std::function HandlerFunction; + +class TcpServer +{ +public: + explicit + TcpServer(unsigned short port, long read_timeout = 5); + + void + setHandler(const HandlerFunction &handler); + + ~TcpServer(); + + void + start(); + +private: + + void + accept(); + + void + processIncomingData(std::shared_ptr socket); + + std::shared_ptr + set_timeout_on_socket(std::shared_ptr socket, long seconds); + + unsigned short port; + boost::asio::io_service io_service; + boost::asio::ip::tcp::acceptor acceptor; + long read_timeout; + HandlerFunction handler; +}; + + +#endif //TCP_SERVER_H diff --git a/Websocket/websocket-server.cpp b/Websocket/websocket-server.cpp new file mode 100644 index 00000000..ee79ccdb --- /dev/null +++ b/Websocket/websocket-server.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "websocket-server.h" + +WebSocketServer::WebSocketServer(unsigned short port) + : m_isRunning(false) +{ + try { + // Initialize Asio + server.init_asio(); + + // Set reuse address option + server.set_reuse_addr(true); + + // Register our message handler + server.set_message_handler(bind(&WebSocketServer::onMessage, this, &server, ::_1, ::_2)); + + // Listen on port + server.listen(boost::asio::ip::tcp::v4(),port); + + } catch (websocketpp::exception const & e) { + std::cout << e.what() << std::endl; + } catch (...) { + std::cout << "Exception occurred.." << std::endl; + } +} + +void +WebSocketServer::setHandler(const HandlerFunction &handler) +{ + this->handler = handler; +} + +void +WebSocketServer::start() +{ + m_isRunning = true; + int retries = 5; + + while (m_isRunning && retries > 0) { + + try { + // Start the server accept loop + server.start_accept(); + + //Set interrupt callbacks + + boost::asio::io_service io_service; + boost::asio::signal_set signals(server.get_io_service(), SIGINT, SIGQUIT); + + signals.async_wait([this](const boost::system::error_code &errorCode, int) { + std::cout << "Gracefully terminating websocket server" << std::endl; + this->m_isRunning = false; + this->server.stop(); + }); + + // Start the ASIO io_service run loop + server.run(); + + } catch (websocketpp::exception const &e) { + std::cout << e.what() << std::endl; + } + catch (...) { + std::cout << "Exception occurred.." << std::endl; + } + + retries--; + } +} + +void +WebSocketServer::onMessage(Server *s, websocketpp::connection_hdl hdl, message_ptr msg) +{ + try { + handler(s, hdl, msg, (uint8_t *) msg->get_payload().c_str(), msg->get_payload().size()); + } catch (const websocketpp::lib::error_code &e) { + + std::cout << "Reply sent failed because: " << e + << "(" << e.message() << ")" << std::endl; + + } +} diff --git a/Websocket/websocket-server.h b/Websocket/websocket-server.h new file mode 100644 index 00000000..9f34a2b8 --- /dev/null +++ b/Websocket/websocket-server.h @@ -0,0 +1,51 @@ +// +// Created by msardara on 28/11/16. +// + +#ifndef WEBSOCKETSERVER_HPP +#define WEBSOCKETSERVER_HPP + +#include "websocketpp/config/asio_no_tls.hpp" +#include "websocketpp/server.hpp" + +typedef websocketpp::server Server; +typedef Server::message_ptr message_ptr; + +typedef std::function HandlerFunction; + + +using websocketpp::lib::placeholders::_1; +using websocketpp::lib::placeholders::_2; +using websocketpp::lib::bind; + +class WebSocketServer +{ +public: + explicit + WebSocketServer(unsigned short port); + + void + setHandler(const HandlerFunction &handler); + + ~WebSocketServer() {}; + + void + start(); + +private: + void + onMessage(Server* s, websocketpp::connection_hdl hdl, message_ptr msg); + + Server server; + + HandlerFunction handler; + + volatile bool m_isRunning; +}; + + +#endif WEBSOCKETSERVER_HPP diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml new file mode 100644 index 00000000..f954847c --- /dev/null +++ b/android/AndroidManifest.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 00000000..8b01d077 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,9 @@ +## This file is automatically generated by QtCreator. +# +# This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. + +androidBuildToolsVersion=25.0.2 +androidCompileSdkVersion=25 +buildDir=.build +qt5AndroidDir=/Users/angelomantellini/Qt/5.7/android_armv7/src/android/java diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..8c0fb64a Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..0c71e760 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 00000000..91a7e269 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/res/drawable-hdpi/icon.png b/android/res/drawable-hdpi/icon.png new file mode 100755 index 00000000..ecd7fe96 Binary files /dev/null and b/android/res/drawable-hdpi/icon.png differ diff --git a/android/res/drawable-ldpi/icon.png b/android/res/drawable-ldpi/icon.png new file mode 100755 index 00000000..8a4388e5 Binary files /dev/null and b/android/res/drawable-ldpi/icon.png differ diff --git a/android/res/drawable-mdpi/icon.png b/android/res/drawable-mdpi/icon.png new file mode 100755 index 00000000..a1519295 Binary files /dev/null and b/android/res/drawable-mdpi/icon.png differ diff --git a/android/res/values/libs.xml b/android/res/values/libs.xml new file mode 100644 index 00000000..43296f2e --- /dev/null +++ b/android/res/values/libs.xml @@ -0,0 +1,25 @@ + + + + https://download.qt.io/ministro/android/qt5/qt-5.7 + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/org/player/viper/ViperActivity.java b/android/src/org/player/viper/ViperActivity.java new file mode 100755 index 00000000..a29025e4 --- /dev/null +++ b/android/src/org/player/viper/ViperActivity.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.player.viper; +import org.qtproject.qt5.android.bindings.QtActivity; +import android.content.Context; +import android.content.Intent; +import android.app.PendingIntent; +import android.util.Log; +import android.os.Bundle; +import android.view.WindowManager; +import android.content.pm.ActivityInfo; + +public class ViperActivity extends QtActivity +{ + private final static String TAG = "ViperPlayer"; + private static String m_request_url; + private static QMLPlayerActivity m_instance; + public ViperActivity() { + m_instance = this; + } + public static String getUrl() { + return m_instance.m_request_url; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + Intent intent = getIntent(); + String action = intent.getAction(); + + if (intent.ACTION_VIEW.equals(action)) { + m_request_url = intent.getDataString(); + } + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } +} diff --git a/debug.h b/debug.h new file mode 100644 index 00000000..ed129fc1 --- /dev/null +++ b/debug.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DEBUGCONFFILE +#define DEBUGCONFFILE + +#ifdef DEBUG_BUILD +#define Debug(...) do{ printf(__VA_ARGS__); } while(0) +#else +#define Debug(...) do { } while(0) +#endif +#endif //DEBUGCONFFILE diff --git a/libdash/Authors.txt b/libdash/Authors.txt new file mode 100644 index 00000000..3e02b3d6 --- /dev/null +++ b/libdash/Authors.txt @@ -0,0 +1,10 @@ + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Authors: + * Christopher Mueller + * Joerg Poecher + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + ****************************************************************************** \ No newline at end of file diff --git a/libdash/CMakeLists.txt b/libdash/CMakeLists.txt new file mode 100644 index 00000000..92ca8d57 --- /dev/null +++ b/libdash/CMakeLists.txt @@ -0,0 +1,174 @@ +cmake_minimum_required(VERSION 3.5) +project(libdash) +set(CMAKE_CXX_FLAGS "-std=c++0x -g -DLOG_BUILD") +set(DEBUG_BUILD FALSE) +set(HEADER_FILES + include/config.h + include/IAdaptationSet.h + include/IBaseUrl.h + include/IChunk.h + include/IConnection.h + include/IContentComponent.h + include/IDASHManager.h + include/IDASHMetrics.h + include/IDescriptor.h + include/IDownloadableChunk.h + include/IDownloadObserver.h + include/IHTTPTransaction.h + include/IMetrics.h + include/IMPD.h + include/IMPDElement.h + include/IMultipleSegmentBase.h + include/INode.h + include/IPeriod.h + include/IProgramInformation.h + include/IRange.h + include/IRepresentation.h + include/IRepresentationBase.h + include/ISegment.h + include/ISegmentBase.h + include/ISegmentList.h + include/ISegmentTemplate.h + include/ISegmentTimeline.h + include/ISegmentURL.h + include/ISubRepresentation.h + include/ISubset.h + include/ITCPConnection.h + include/IThroughputMeasurement.h + include/ITimeline.h + include/IURLType.h + include/libdash.h) + +set(SOURCE_FILES + include/config.h + include/IAdaptationSet.h + include/IBaseUrl.h + include/IChunk.h + include/IConnection.h + include/IContentComponent.h + include/IDASHManager.h + include/IDASHMetrics.h + include/IDescriptor.h + include/IDownloadableChunk.h + include/IDownloadObserver.h + include/IHTTPTransaction.h + include/IMetrics.h + include/IMPD.h + include/IMPDElement.h + include/IMultipleSegmentBase.h + include/INode.h + include/IPeriod.h + include/IProgramInformation.h + include/IRange.h + include/IRepresentation.h + include/IRepresentationBase.h + include/ISegment.h + include/ISegmentBase.h + include/ISegmentList.h + include/ISegmentTemplate.h + include/ISegmentTimeline.h + include/ISegmentURL.h + include/ISubRepresentation.h + include/ISubset.h + include/ITCPConnection.h + include/IThroughputMeasurement.h + include/ITimeline.h + include/IURLType.h + include/libdash.h + source/helpers/Block.h + source/helpers/BlockStream.cpp + source/helpers/BlockStream.h + source/helpers/Path.cpp + source/helpers/Path.h + source/helpers/String.cpp + source/helpers/String.h + source/helpers/SyncedBlockStream.cpp + source/helpers/SyncedBlockStream.h + source/helpers/Time.cpp + source/helpers/Time.h + source/manager/DASHManager.cpp + source/manager/DASHManager.h + source/metrics/HTTPTransaction.cpp + source/metrics/HTTPTransaction.h + source/metrics/TCPConnection.cpp + source/metrics/TCPConnection.h + source/metrics/ThroughputMeasurement.cpp + source/metrics/ThroughputMeasurement.h + source/mpd/AbstractMPDElement.cpp + source/mpd/AbstractMPDElement.h + source/mpd/AdaptationSet.cpp + source/mpd/AdaptationSet.h + source/mpd/BaseUrl.cpp + source/mpd/BaseUrl.h + source/mpd/ContentComponent.cpp + source/mpd/ContentComponent.h + source/mpd/Descriptor.cpp + source/mpd/Descriptor.h + source/mpd/Metrics.cpp + source/mpd/Metrics.h + source/mpd/MPD.cpp + source/mpd/MPD.h + source/mpd/MultipleSegmentBase.cpp + source/mpd/MultipleSegmentBase.h + source/mpd/Period.cpp + source/mpd/Period.h + source/mpd/ProgramInformation.cpp + source/mpd/ProgramInformation.h + source/mpd/Range.cpp + source/mpd/Range.h + source/mpd/Representation.cpp + source/mpd/Representation.h + source/mpd/RepresentationBase.cpp + source/mpd/RepresentationBase.h + source/mpd/Segment.cpp + source/mpd/Segment.h + source/mpd/SegmentBase.cpp + source/mpd/SegmentBase.h + source/mpd/SegmentList.cpp + source/mpd/SegmentList.h + source/mpd/SegmentTemplate.cpp + source/mpd/SegmentTemplate.h + source/mpd/SegmentTimeline.cpp + source/mpd/SegmentTimeline.h + source/mpd/SegmentURL.cpp + source/mpd/SegmentURL.h + source/mpd/SubRepresentation.cpp + source/mpd/SubRepresentation.h + source/mpd/Subset.cpp + source/mpd/Subset.h + source/mpd/Timeline.cpp + source/mpd/Timeline.h + source/mpd/URLType.cpp + source/mpd/URLType.h + source/network/AbstractChunk.cpp + source/network/AbstractChunk.h + source/network/DownloadStateManager.cpp + source/network/DownloadStateManager.h + source/portable/MultiThreading.cpp + source/portable/MultiThreading.h + source/portable/Networking.h + source/xml/DOMHelper.cpp + source/xml/DOMHelper.h + source/xml/DOMParser.cpp + source/xml/DOMParser.h + source/xml/Node.cpp + source/xml/Node.h + source/dllmain.cpp + source/libdash.cpp + source/targetver.h) + +find_package(LibXml2 REQUIRED) +find_package(ZLIB REQUIRED) +find_package(CURL REQUIRED) +include_directories(${LIBXML2_INCLUDE_DIR}) +include_directories(${WLIB_INCLUDE_DIRS}) +include_directories(${CURL_INCLUDE_DIRS}) +include_directories(include) + +add_library(dash SHARED ${SOURCE_FILES}) +set_target_properties(dash PROPERTIES LINKER_LANGUAGE CXX) +target_link_libraries(dash ${CURL_LIBRARIES} ${ZLIB_LIBRARIES} ${LIBXML2_LIBRARIES}) +message("libxml ${LIBXML2_LIBRARIES} ${LIBXML2_INCLUDE_DIR}") +install(TARGETS dash DESTINATION ${CMAKE_INSTALL_PREFIX}/lib PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) +install(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/libdash) + diff --git a/libdash/include/IAdaptationSet.h b/libdash/include/IAdaptationSet.h new file mode 100644 index 00000000..eff9c7ec --- /dev/null +++ b/libdash/include/IAdaptationSet.h @@ -0,0 +1,367 @@ +/** + * @class dash::mpd::IAdaptationSet + * @brief This interface is needed for accessing the attributes and elements of the AdaptationSet element + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.3.2, table 5 + * @details Each Period consists of one or more Adaptation Sets. An Adaptation Set is described by an AdaptationSet element. + * AdaptationSet elements are contained in a Period element.\n\n + * An Adaptation Set contains alternate Representations, i.e. only one Representation within an Adaptation Set is expected to be presented at a time. + * All Representations contained in one Adaptation Set represent the same media content components and therefore contain media streams that are considered to be perceptually equivalent.\n\n + * Representations are arranged into Adaptation Sets according to the media content component properties of the media content components present in the Representations, namely + *
    + *
  • the language as described by the \c \@lang attribute, + *
  • the media component type described by the \c \@contentType attribute, + *
  • the picture aspect ratio as described by the \c \@par attribute, + *
  • the role property as described by the Role elements, + *
  • the accessibility property as described by the Accessibility elements, + *
  • the viewpoint property as described by the Viewpoint elements, + *
  • the rating property as described by the Rating elements. + *
+ * Representations shall appear in the same Adaptation Set if and only if they have identical values for all of these media content component properties for each media content component.\n\n + * The values for the elements Role, Accessibility, Viewpoint and Rating are generally not provided + * within the scope of this part of ISO/IEC 23009. However, a number of simple schemes are defined in section 5.8.5. of ISO/IEC 23009-1, Part 1, 2012.\n\n + * If there exist multiple media content components then the properties of each media content component shall be described by a separate ContentComponent element as defined in 5.5.4. + * The ContentComponent element shares common elements and attributes with the AdaptationSet element. Default values, or values applicable to all media content components, + * may be provided directly in the AdaptationSet element. Attributes present in the AdaptationSet shall not be repeated in the ContentComponent element.\n\n + * The AdaptationSet element may contain default values for elements and attributes associated to the contained Representations. + * The list of possible present elements and attributes that are common to AdaptationSet and Representation (and also SubRepresentation) + * are collected in 5.3.7. Any of the common attributes shall only be present either in the AdaptationSet element or in the Representation element, + * but not in both.\n\n + * The AdaptationSet element also supports the description of ranges for the \c \@bandwidth, \c \@width, \c \@height and \c \@frameRate attributes + * associated to the contained Representations, which provide a summary of all values for all the Representations within this Adaptation Set. + * The Representations contained within an Adaptation Set shall not contain values outside the ranges documented for that Adaptation Set. \n\n + * Adaptation Sets may be further arranged into groups using the \c \@group attribute. The semantics of this grouping is that the media content within one Period is represented by: + *
    + *
  1. either one Representation from group 0, if present, + *
  2. or the combination of at most one Representation from each non-zero group. + *
+ * If the AdaptationSet\@group attribute is not present then all Representations in this Adaptation Set are assigned to a non-zero group specific to this Adaptation Set. + * @see dash::mpd::IDescriptor dash::mpd::IContentComponent dash::mpd::IBaseUrl dash::mpd::ISegmentBase dash::mpd::ISegmentList dash::mpd::ISegmentTemplate dash::mpd::IRepresentation + * dash::mpd::IRepresentationBase + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IADAPTATIONSET_H_ +#define IADAPTATIONSET_H_ + +#include "config.h" + +#include "IDescriptor.h" +#include "IContentComponent.h" +#include "IBaseUrl.h" +#include "ISegmentBase.h" +#include "ISegmentList.h" +#include "ISegmentTemplate.h" +#include "IRepresentation.h" +#include "IRepresentationBase.h" + +namespace dash +{ + namespace mpd + { + class IAdaptationSet : public virtual IRepresentationBase + { + public: + virtual ~IAdaptationSet(){} + + /** + * Returns a reference to a vector of pointers to dash::mpd::IDescriptor objects that specify information about accessibility scheme.\n + * For more details refer to sections 5.8.1 and 5.8.4.3. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IDescriptor objects + */ + virtual const std::vector& GetAccessibility () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IDescriptor objects that specify information on role annotation scheme. + * For more details refer to sections 5.8.1 and 5.8.4.2. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IDescriptor objects + */ + virtual const std::vector& GetRole () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IDescriptor objects that specify information on rating scheme.\n + * For more details refer to sections 5.8.1 and 5.8.4.4. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IDescriptor objects + */ + virtual const std::vector& GetRating () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IDescriptor objects that specify information on viewpoint annotation scheme.\n + * For more details refer to sections 5.8.1 and 5.8.4.5. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IDescriptor objects + */ + virtual const std::vector& GetViewpoint () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IContentComponent objects that specifies the properties + * of one media content component contained in this Adaptation Set.\n + * For more details refer to section 5.3.4. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IContentComponent objects + */ + virtual const std::vector& GetContentComponent () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IBaseUrl objects that specify base URLs that can be used for reference resolution and alternative URL selection.\n + * For more details refer to the description in section 5.6. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IBaseUrl objects + */ + virtual const std::vector& GetBaseURLs () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegmentBase object that specifies default Segment Base information.\n + * Information in this element is overridden by information in the Representation.SegmentBase, if present.\n + * For more details see section 5.3.9. of ISO/IEC 23009-1, Part 1, 2012. + * @return a pointer to a dash::mpd::ISegmentBase object + */ + virtual ISegmentBase* GetSegmentBase () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegmentList object that specifies default Segment List information.\n + * Information in this element is overridden by information in the Representation.SegmentList, if present.\n + * For more details see section 5.3.9. of ISO/IEC 23009-1, Part 1, 2012. + * @return a pointer to a dash::mpd::ISegmentList object + */ + virtual ISegmentList* GetSegmentList () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegmentTemplate object that specifies default Segment Template information.\n + * Information in this element is overridden by information in the Representation.SegmentTemplate, if present.\n + * For more details see section 5.3.9. of ISO/IEC 23009-1, Part 1, 2012. + * @return a pointer to a dash::mpd::ISegmentTemplate object + */ + virtual ISegmentTemplate* GetSegmentTemplate () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IRepresentation objects that specifies a Representation.\n + * At least one Representation element shall be present in each Adaptation Set. The actual element may however be part of a remote element.\n + * For more details refer to section 5.3.5. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IRepresentation objects + */ + virtual const std::vector& GetRepresentation () const = 0; + + /** + * Returns a reference to a string that specifies a reference to external AdaptationSet element. + * @return a reference to a string + */ + virtual const std::string& GetXlinkHref () const = 0; + + /** + * Returns a reference to a string that specifies the processing instructions, which can be either \c \"onLoad\" or \c \"onRequest\". + * @return a reference to a string + */ + virtual const std::string& GetXlinkActuate () const = 0; + + /** + * Returns an unsigned integer that specifies an unique identifier for this Adaptation Set in the scope of the Period. + * The attribute shall be unique in the scope of the containing Period. \n\n + * The attribute shall not be present in a remote element.\n\n + * If not present, no identifier for the Adaptation Set is specified. + * @return an unsigned integer + */ + virtual uint32_t GetId () const = 0; + + /** + * Returns an unsigned integer that specifies an identifier for the group that is unique in the scope of the containing Period.\n + * For details refer to section 5.3.3.1. of ISO/IEC 23009-1, Part 1, 2012. + * @return an unsigned integer + */ + virtual uint32_t GetGroup () const = 0; + + /** + * Returns a reference to a string that declares the language code for this Adaptation Set. The syntax and semantics according to IETF RFC 5646 shall be used.\n + * If not present, the language code may be defined for each media component or it may be unknown. + * @return a reference to a string + */ + virtual const std::string& GetLang () const = 0; + + /** + * Returns a reference to a string that specifies the media content component type for this Adaptation Set. + * A value of the top-level Content-type 'type' value as defined in RFC1521, Clause 4 shall be taken.\n + * If not present, the media content component type may be defined for each media component or it may be unknown. + * @return a reference to a string + */ + virtual const std::string& GetContentType () const = 0; + + /** + * Returns a reference to a string that specifies the picture aspect ratio of the video media component type, + * in the form of a string consisting of two integers separated by ':', e.g., \c \"16:9\". When this attribute is present, + * and the attributes \c \@width and \c \@height for the set of Representations are also present, the picture aspect ratio as specified by this attribute shall be the same + * as indicated by the values of \c \@width, \c \@height, and \c \@sar, i.e. it shall express the same ratio as (\c \@width * \em sarx): (\c \@height * \em sary), + * with \em sarx the first number in \c \@sar and \em sary the second number.\n + * If not present, the picture aspect ratio may be defined for each media component or it may be unknown. + * @return a reference to a string + */ + virtual const std::string& GetPar () const = 0; + + /** + * Returns an unsigned integer that specifies the minimum \c \@bandwidth value in all Representations in this Adaptation Set. + * This value has the same units as the \c \@bandwidth attribute. + * @return an unsigned integer + */ + virtual uint32_t GetMinBandwidth () const = 0; + + /** + * Returns an unsigned integer that specifies the maximum \c \@bandwidth value in all Representations in this Adaptation Set. + * This value has the same units as the \c \@bandwidth attribute. + * @return an unsigned integer + */ + virtual uint32_t GetMaxBandwidth () const = 0; + + /** + * Returns an unsigned integer that specifies the minimum \c \@width value in all Representations in this Adaptation Set. + * This value has the same units as the \c \@width attribute. + * @return an unsigned integer + */ + virtual uint32_t GetMinWidth () const = 0; + + /** + * Returns an unsigned integer that specifies the maximum \c \@width value in all Representations in this Adaptation Set. + * This value has the same units as the \c \@width attribute. + * @return an unsigned integer + */ + virtual uint32_t GetMaxWidth () const = 0; + + /** + * Returns an unsigned integer that specifies specifies the minimum \c \@height value in all Representations in this Adaptation Set. + * This value has the same units as the \c \@height attribute. + * @return an unsigned integer + */ + virtual uint32_t GetMinHeight () const = 0; + + /** + * Returns an unsigned integer that specifies specifies the maximum \c \@height value in all Representations in this Adaptation Set. + * This value has the same units as the \c \@height attribute. + * @return an unsigned integer + */ + virtual uint32_t GetMaxHeight () const = 0; + + /** + * Returns a reference to a string that specifies the minimum \c \@framerate value in all Representations in this Adaptation Set. + * This value is encoded in the same format as the \c \@frameRate attribute. + * @return a reference to a string + */ + virtual const std::string& GetMinFramerate () const = 0; + + /** + * Returns a reference to a string that specifies the maximum \c \@framerate value in all Representations in this Adaptation Set. + * This value is encoded in the same format as the \c \@frameRate attribute. + * @return a reference to a string + */ + virtual const std::string& GetMaxFramerate () const = 0; + + /** + * Because of the fact that the type of the attribute \em segmentAlignment is a union of \c xs:unsignedInt and \c xs:boolean this method is needed to determine + * whether its value is of type bool or integer.\n + * If and only if \c 'true' is returned, an invocation of HasSegmentAlignment() is neccessary to retrieve the bool value.\n + * If and only if \c 'false' is returned, an invocation of GetSegmentAlignment() is neccessary to retrieve the integer value. + * @return a bool value + */ + virtual bool SegmentAlignmentIsBoolValue () const = 0; + + /** + * If the return value of SegmentAlignmentIsBoolValue() equals \c 'true' the bool value returned by this method + * specifies whether Segment Alignment is used or not. This is only valid for Adaptation Sets containing Representations with multiple media content components. + * If \c 'true' is returned, this specifies that for any two Representations, + * X and Y, within the same Adaptation Set, the m-th Segment of X and the n-th Segment of Y + * are non-overlapping (as defined in section 4.5.2 of ISO/IEC 23009-1, Part 1, 2012) whenever \em m is not equal to \em n. + * @return a bool value + */ + virtual bool HasSegmentAlignment () const = 0; + + /** + * If the return value of SegmentAlignmentIsBoolValue() equals \c 'false' this specifies that for any two Representations, + * X and Y, within the same Adaptation Set, the m-th Segment of X and the n-th Segment of Y + * are non-overlapping (as defined in section 4.5.2 of ISO/IEC 23009-1, Part 1, 2012) whenever \em m is not equal to \em n.\n\n + * For Adaptation Sets containing Representations with a single media content component, when two AdaptationSet elements within a Period share the same + * integer value for this attribute - which is the return value of this method, then for any two Representations, X and Y, within the union of the two Adaptation Sets, + * the m-th Segment of X and the n-th Segment of Y are non-overlapping (as defined in section 4.5.2 of ISO/IEC 23009-1, Part 1, 2012) + * whenever \em m is not equal to \em n. + * @return an unsigned integer + */ + virtual uint32_t GetSegmentAligment () const = 0; + + /** + * Because of the fact that the type of the attribute \em subsegmentAlignment is a union of \c xs:unsignedInt and \c xs:boolean this method is needed to determine + * whether its value is of type bool or integer.\n + * If and only if \c 'true' is returned, an invocation of HasSubsegmentAlignment() is neccessary to retrieve the bool value.\n + * If and only if \c 'false' is returned, an invocation of GetSubsegmentAlignment() is neccessary to retrieve the integer value. + * @return a bool value + */ + virtual bool SubsegmentAlignmentIsBoolValue () const = 0; + + /** + * If and only if the return value of SubsegmentAlignmentIsBoolValue() equals \c 'true' the bool value returned by this method + * specifies whether Subsegment Alignment is used or not. + * If \c 'true' is returned, the following conditions shall be satisfied: + *
    + *
  • Each Media Segment shall be indexed (i.e. either it contains a Segment index or there is an Index Segment providing an index for the Media Segment) + *
  • For any two Representations, X and Y, within the same Adaptation Set, the m-th Subsegment of X and the n-th Subsegment of Y are + * non-overlapping (as defined in section 4.5.2 of ISO/IEC 23009-1, Part 1, 2012) whenever \em m is not equal to \em n. + *
+ * @return a bool value + */ + virtual bool HasSubsegmentAlignment () const = 0; + + /** + * If the return value of SubsegmentAlignmentIsBoolValue() equals \c 'false' the following conditions shall be satisfied: + *
    + *
  • Each Media Segment shall be indexed (i.e. either it contains a Segment index or there is an Index Segment providing an index for the Media Segment) + *
  • For any two Representations, X and Y, within the same Adaptation Set, the m-th Subsegment of X and the n-th Subsegment of Y are + * non-overlapping (as defined in section 4.5.2 of ISO/IEC 23009-1, Part 1, 2012) whenever \em m is not equal to \em n. + *
  • For Adaptation Sets containing Representations with a single media content component, when two AdaptationSet elements within a Period share + * the same integer value for this attribute - which is the return value of this method, then for any two Representations, X and Y, + * within the union of the two Adaptation Sets, the m-th Subsegment of X and the n-th Subsegment of Y are non-overlapping + * (as defined in section 4.5.2 of ISO/IEC 23009-1, Part 1, 2012) whenever m is not equal to n. + *
+ * @return an unsigned integer + */ + virtual uint32_t GetSubsegmentAlignment () const = 0; + + /** + * Returns a unsigned integer that when greater than 0, specifies that each Subsegment with \c SAP_type greater than 0 starts with a SAP of type + * less than or equal to the value of \c \@subsegmentStartsWithSAP. A Subsegment starts with SAP when the Subsegment contains a SAP, + * and for the first SAP, ISAU is the index of the first access unit that follows ISAP , and ISAP is contained in the Subsegment.\n + * The semantics of \c \@subsegmentStartsWithSAP equal to 0 are unspecified. + * @return an unsigned integer + */ + virtual uint8_t GetSubsegmentStartsWithSAP () const = 0; + + /** + * Returns a bool value that when true, the following applies: + *
    + *
  • All Representations in the Adaptation Set shall have the same number \em M of Media Segments; + *
  • Let \em R1 , \em R2 , ..., \em RN be all the Representations within the Adaptation Set. + *
  • Let + *
      + *
    • Si,j , for \em j > 0, be the \em jth Media Segment in the \em ith Representation (i.e., \em Ri ) + *
    • if present, let Si,0 be the Initialization Segment in the \em ith Representation, and + *
    • if present, let \em Bi be the Bitstream Switching Segment in the \em ith Representation. + *
    + *
  • The sequence of + *
      + *
    • any Initialization Segment, if present, in the Adaptation Set, with, + *
    • if Bitstream Switching Segments are present, \n + * Bi(1), Si(1),1, Bi(2), Si(2),2, ..., + * Bi(k), Si(k),k, ..., Bi(M), Si(M),M + *
    • else \n + * Si(1),1, Si(2),2, ..., Si(k),k, ..., Si(M),M, + *
    + * wherein any \em i(k) for all \em k values in the range of 1 to \em M, respectively, is an integer value in the range of 1 to \em N, + * results in a \"conforming Segment sequence\" as defined in section 4.5.3 of ISO/IEC 23009-1, Part 1, 2012 + * with the media format as specified in the \c \@mimeType attribute. + *
+ * More detailed rules may be defined for specific media formats. + * @return a bool value + */ + virtual bool GetBitstreamSwitching () const = 0; + }; + } +} + +#endif /* IADAPTATIONSET_H_ */ \ No newline at end of file diff --git a/libdash/include/IBaseUrl.h b/libdash/include/IBaseUrl.h new file mode 100644 index 00000000..206ad14b --- /dev/null +++ b/libdash/include/IBaseUrl.h @@ -0,0 +1,69 @@ +/** + * @class dash::mpd::IBaseUrl + * @brief This interface is needed for accessing the attributes and the content of BaseURL elements + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.6 + * @details The BaseURL element may be used to specify one or more common locations for Segments and other resources.\n + * A URL that can be used as Base URL. The content of this element is a URI string as described in ISO/IEC 23009-1, Part 1, 2012, section 5.6.4 + * @see dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IBASEURL_H_ +#define IBASEURL_H_ + +#include "config.h" + +#include "IMPDElement.h" +#include "ISegment.h" + +namespace dash +{ + namespace mpd + { + class IBaseUrl : public virtual IMPDElement + { + public: + virtual ~IBaseUrl(){} + + /** + * Returns the reference to a string representing a BaseURL + * @return a reference to a string + */ + virtual const std::string& GetUrl () const = 0; + + /** + * Returns the reference to a string that specifies a relationship between Base URLs such that \c BaseURL elements with the same + * \c \@serviceLocation value are likely to have their URLs resolve to services at a common network location, for example a common Content Delivery Network + * @return a reference to a string + */ + virtual const std::string& GetServiceLocation () const = 0; + + /** + * Returns the reference to a string that represents a byte range. \n + * If present specifies HTTP partial GET requests may alternatively be issued by adding the byte range into a + * regular HTTP-URL based on the value of this attribute and the construction rules in Annex E.2. of ISO/IEC 23009-1, Part 1, 2012.\n + * If not present, HTTP partial GET requests may not be converted into regular GET requests. \n + * \b NOTE: Such alternative requests are expected to not be used unless the DASH application requires this. For more details refer to Annex E. + * @return a reference to a string + */ + virtual const std::string& GetByteRange () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegment object which represents a media segment that can be downloaded. Should be used for single base urls inside + * a representation. + * @param baseurls a vector of pointers to dash::mpd::IBaseUrl objects that represent the path to the media segment + * @return a pointer to a dash::mpd::ISegment object + */ + virtual ISegment* ToMediaSegment (const std::vector& baseurls) const = 0; + }; + } +} + +#endif /* IBASEURL_H_ */ \ No newline at end of file diff --git a/libdash/include/IChunk.h b/libdash/include/IChunk.h new file mode 100644 index 00000000..76b3314b --- /dev/null +++ b/libdash/include/IChunk.h @@ -0,0 +1,88 @@ +/** + * @class dash::network::IChunk + * @brief This interface is needed for accessing the information belonging to a dash::network::IChunk object + * @details Provides specific information of a chunk, e.g., URI, Hostname, Port, Path and optional Range that + * that are needed to download this chunk. + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ICHUNK_H_ +#define ICHUNK_H_ + +#include "config.h" +#include "IHTTPTransaction.h" + +namespace dash +{ + namespace network + { + class IChunk + { + public: + virtual ~IChunk(){} + + /** + * Returns a reference to a string that specifies the absolute URI to this chunk + * @return a reference to a string + */ + virtual std::string& AbsoluteURI () = 0; + + /** + * Returns a reference to a string that specifies the host of this chunk + * @return a reference to a string + */ + virtual std::string& Host () = 0; + + /** + * Returns an unsigned integer representing the port belonging to this chunk + * @return an unsigned integer + */ + virtual size_t Port () = 0; + + /** + * Returns a reference to a string that specifies the path to this chunk + * @return a reference to a string + */ + virtual std::string& Path () = 0; + + /** + * Returns a reference to a string that specifies the byte range belonging to this chunk + * @return a reference to a string + */ + virtual std::string& Range () = 0; + + /** + * Returns an unsigned integer representing the start byte of the byte range that belongs to this chunk + * @return an unsigned integer + */ + virtual size_t StartByte () = 0; + + /** + * Returns an unsigned integer representing the end byte of the byte range that belongs to this chunk + * @return an unsigned integer + */ + virtual size_t EndByte () = 0; + + /** + * Returns a bool value that represents whether this chunk has a byte range or not + * @return a bool value + */ + virtual bool HasByteRange () = 0; + + /** + * Returns the type of a HTTP Request/Response Transaction in ISO/IEC 23009-1, Part 1, 2012, annex D.4.3, Table D.2 + * @return a dash::metrics::HTTPTransactionType + */ + virtual dash::metrics::HTTPTransactionType GetType () = 0; + }; + } +} + +#endif /* ICHUNK_H_ */ \ No newline at end of file diff --git a/libdash/include/IConnection.h b/libdash/include/IConnection.h new file mode 100644 index 00000000..4150d562 --- /dev/null +++ b/libdash/include/IConnection.h @@ -0,0 +1,69 @@ +/** + * @class dash::network::IConnection + * @brief The connection interface can be used to enable the download of segments through external connections. + * @details This interface enables the extension of libdash to download segment through, e.g., SPDY, CCN etc. + * @see dash::network::IChunk + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ICONNECTION_H_ +#define ICONNECTION_H_ + +#include "config.h" + +#include "IChunk.h" +#include "IDASHMetrics.h" + +namespace dash +{ + namespace network + { + class IConnection : public virtual dash::metrics::IDASHMetrics + { + public: + virtual ~IConnection(){} + + /** + * This function should read a block of bytes from the specified chunk. + * @param data pointer to a block of memory + * @param len size of the memory block that can be used by the method + * @param chunk the dash::network::IChunk object from which data should be read + * @return amount of data that has been read + */ + virtual int Read (uint8_t *data, size_t len, IChunk *chunk) = 0; + + /** + * This function should peek a block of bytes from the specified chunk. + * @param data pointer to a block of memory + * @param len size of the memory block that can be used by the method + * @param chunk the dash::network::IChunk object from which data should be peeked + * @return amount of data that has been peeked + */ + virtual int Peek (uint8_t *data, size_t len, IChunk *chunk) = 0; + + /** + * This function should get the average speed of download for the specified chunk. + * It should be called after the whole chunk has been downloaded. + * @return average speed of download in bps. + */ + virtual double GetAverageDownloadingSpeed () = 0; + + /** + * This function should get the time taken for the download of the specified chunk. + * It should be called after the whole chunk has been downloaded. + * @return average time of download in seconds. + */ + virtual double GetDownloadingTime () = 0; + + }; + } +} + +#endif /* ICONNECTION_H_ */ diff --git a/libdash/include/IContentComponent.h b/libdash/include/IContentComponent.h new file mode 100644 index 00000000..9f3be97f --- /dev/null +++ b/libdash/include/IContentComponent.h @@ -0,0 +1,83 @@ +/** + * @class dash::mpd::IContentComponent + * @brief This interface is needed for accessing the attributes and elements of the ContentComponent element + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.4.2, table 6 + * @details Each Adaptation Set contains one or more media content components. The properties of each media content + * component are described by a ContentComponent element or may be described directly on the AdaptationSet element + * if only one media content component is present in the Adaptation Set. ContentComponent elements are contained in an AdaptationSet element. + * @see dash::mpd::IMPDElement dash::mpd::IDescriptor dash::mpd::IAdaptationSet + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ICONTENTCOMPONENT_H_ +#define ICONTENTCOMPONENT_H_ + +#include "config.h" + +#include "IMPDElement.h" +#include "IDescriptor.h" + +namespace dash +{ + namespace mpd + { + class IContentComponent : public virtual IMPDElement + { + public: + virtual ~IContentComponent(){} + + + /** + * @copydoc dash::mpd::IAdaptationSet::GetAccessibility() + */ + virtual const std::vector& GetAccessibility () const = 0; + + /** + * @copydoc dash::mpd::IAdaptationSet::GetRole() + */ + virtual const std::vector& GetRole () const = 0; + + /** + * @copydoc dash::mpd::IAdaptationSet::GetRating() + */ + virtual const std::vector& GetRating () const = 0; + + /** + * @copydoc dash::mpd::IAdaptationSet::GetViewpoint() + */ + virtual const std::vector& GetViewpoint () const = 0; + + /** + * Returns an unsigned integer that specifies an identifier for this media component. + * The attribute shall be unique in the scope of the containing Adaptation Set. + * @return an unsigned integer + */ + virtual uint32_t GetId () const = 0; + + /** + * @copydoc dash::mpd::IAdaptationSet::GetLang() + */ + virtual const std::string& GetLang () const = 0; + + /** + * @copydoc dash::mpd::IAdaptationSet::GetContentType() + */ + virtual const std::string& GetContentType () const = 0; + + /** + * @copydoc dash::mpd::IAdaptationSet::GetPar() + */ + virtual const std::string& GetPar () const = 0; + + }; + } +} + +#endif /* ICONTENTCOMPONENT_H_ */ \ No newline at end of file diff --git a/libdash/include/IDASHManager.h b/libdash/include/IDASHManager.h new file mode 100644 index 00000000..5e4688fb --- /dev/null +++ b/libdash/include/IDASHManager.h @@ -0,0 +1,45 @@ +/** + * @class dash::IDASHManager + * @brief This interface is needed for generating an IMPD object from the information found in a MPD file + * @details By invoking the method Open(char *path) all the information found in the MPD file specified by \em path is mapped to corresponding IMPD objects. + * @see dash::mpd::IMPD + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IDASHMANAGER_H_ +#define IDASHMANAGER_H_ + +#include "config.h" + +#include "IMPD.h" +#include "IConnection.h" + +namespace dash +{ + class IDASHManager + { + public: + virtual ~IDASHManager(){} + + /** + * Returns a pointer to dash::mpd::IMPD object representing the the information found in the MPD file specified by \em path + * @param path A URI to a MPD file + * @return a pointer to an dash::mpd::IMPD object + */ + virtual mpd::IMPD* Open (char *path, std::string mURL = "") = 0; + + /** + * Frees allocated memory and deletes the DashManager + */ + virtual void Delete () = 0; + }; +} + +#endif /* IDASHMANAGER_H_ */ diff --git a/libdash/include/IDASHMetrics.h b/libdash/include/IDASHMetrics.h new file mode 100644 index 00000000..748af7a5 --- /dev/null +++ b/libdash/include/IDASHMetrics.h @@ -0,0 +1,48 @@ +/** + * @class dash::metrics::IDASHMetrics + * @brief The connection interface can be used to retrieve DASH Metrics. + * @details The TCP Connection list and HTTP Request/Response Transactions can be retrieved. \n + * This basically corresponds to the information neeed for Observation point 1 as defined in ISO/IEC 23009-1, Part 1, 2012, annex D.3.2. + * @see dash::metrics::ITCPConnection dash::metrics::IHTTPConnection + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IDASHMETRICS_H_ +#define IDASHMETRICS_H_ + +#include "config.h" +#include "IHTTPTransaction.h" +#include "ITCPConnection.h" + +namespace dash +{ + namespace metrics + { + class IDASHMetrics + { + public: + virtual ~IDASHMetrics(){} + + /** + * This function returns a list of DASH Metrics as defined in ISO/IEC 23009-1, Part 1, 2012, annex D.4.2. + * @return a list of dash::metrics::ITCPConnection Metrics Objects + */ + virtual const std::vector& GetTCPConnectionList () const = 0; + + /** + * This function returns a list of DASH Metrics as defined in ISO/IEC 23009-1, Part 1, 2012, annex D.4.3. + * @return a list of dash::metrics::IHTTPConnection Metrics Objets + */ + virtual const std::vector& GetHTTPTransactionList () const = 0; + }; + } +} + +#endif /* IDASHMETRICS_H_ */ diff --git a/libdash/include/IDescriptor.h b/libdash/include/IDescriptor.h new file mode 100644 index 00000000..3983e41e --- /dev/null +++ b/libdash/include/IDescriptor.h @@ -0,0 +1,68 @@ +/** + * @class dash::mpd::IDescriptor + * @brief This interface is needed for accessing the attributes of the Descriptor type as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.8.2, table 21 + * @details The MPD may contain descriptors that make use of a common syntax as defined in this subclause. + * The elements of type \c DescriptorType provide a flexible mechanism for DASH content authors to annotate + * and extend the \c MPD, \c Period, \c AdaptationSet and \c Representation elements.\n + * The descriptor elements are all structured in the same way, namely they contain a \c \@schemeIdUri attribute that provides a URI to identify the scheme and + * an optional attribute \c \@value. The semantics of the element are specific to the scheme employed. The URI identifying the scheme may be a URN or a URL.\n + * In ISO/IEC 23009-1, Part 1, 2012, specific elements for descriptors are defined in section 5.8.4.\n + * The MPD does not provide any specific information on how to use these elements. It is up to the application that employs DASH formats + * to instantiate the description elements with appropriate scheme information. However, this part of ISO/IEC 23009 defines some specific schemes in 5.8.5.\n + * DASH applications that use one of these elements must first define a Scheme Identifier in the form of a URI and must then define the value space + * for the element when that Scheme Identifier is used. The Scheme Identifier appears in the \c \@schemeIdUri attribute. + * In the case that a simple set of enumerated values are required, a text string may be defined for each value and this string must be included + * in the \c \@value attribute. If structured data is required then any extension element or attribute may be defined in a separate namespace.\n + * Two elements of type DescriptorType are equivalent, if the element name, the value of the \c \@schemeIdUri and the value of the \c \@value attribute are equivalent. + * If the \c \@schemeIdUri is a URN, then equivalence shall refer to lexical equivalence as defined in clause 5 of RFC 2141. If the \c \@schemeIdUri is a URL, + * then equivalence shall refer to equality on a character-for-character basis as defined in clause 6.2.1 of RFC3986. + * If the \c \@value attribute is not present, equivalence is determined by the equivalence for \c \@schemeIdUri only. + * Attributes and element in extension namespaces are not used for determining equivalence. + * @see dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IDESCRIPTOR_H_ +#define IDESCRIPTOR_H_ + +#include "config.h" + +#include "IMPDElement.h" + +namespace dash +{ + namespace mpd + { + class IDescriptor : public virtual IMPDElement + { + public: + virtual ~IDescriptor(){} + + /** + * Returns a reference to a string that specifies a URI to identify the scheme. \n + * The semantics of this element are specific to the scheme specified by this attribute. + * The \c \@schemeIdUri may be a URN or URL. When a URL is used, it should also contain a month-date in the form + * mmyyyy; the assignment of the URL must have been authorized by the owner of the domain name in that URL on + * or very close to that date, to avoid problems when domain names change ownership. + * @return a reference to a string + */ + virtual const std::string& GetSchemeIdUri () const = 0; + + /** + * Returns a reference to a string that specifies the value for the descriptor element. \n + * The value space and semantics must be defined by the owners of the scheme identified in the \c \@schemeIdUri attribute. + * @return a reference to a string + */ + virtual const std::string& GetValue () const = 0; + }; + } +} + +#endif /* IDESCRIPTOR_H_ */ \ No newline at end of file diff --git a/libdash/include/IDownloadObserver.h b/libdash/include/IDownloadObserver.h new file mode 100644 index 00000000..b78198d4 --- /dev/null +++ b/libdash/include/IDownloadObserver.h @@ -0,0 +1,58 @@ +/** + * @class dash::network::IDownloadObserver + * @brief This interface is needed for informing the application of the state of the download process. + * @details Informs the application about the download rate and the state of the download. + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IDOWNLOADOBSERVER_H_ +#define IDOWNLOADOBSERVER_H_ + +#include "config.h" + +namespace dash +{ + namespace network + { + enum DownloadState + { + NOT_STARTED = 0, + IN_PROGRESS = 1, + REQUEST_ABORT = 2, + ABORTED = 3, + COMPLETED = 4 + }; + class IDownloadObserver + { + public: + virtual ~IDownloadObserver(){} + + /** + * Informs the dash::network::IDownloadObserver object that the download rate has changed. + * @param bytesDownloaded the number of downloaded bytes + */ + virtual void OnDownloadRateChanged (uint64_t bytesDownloaded) = 0; + + /** + * Informs the dash::network::IDownloadObserver object that the downloading time has changed. + * @param downloadingTime the time taken for the downloading of this segment in seconds + */ + virtual void OnDownloadTimeChanged (double downloadingTime) = 0; + + /** + * Informs the dash::network::IDownloadObserver object that the download state has changed. + * @param state the download state + */ + virtual void OnDownloadStateChanged (DownloadState state) = 0; + }; + } +} + +#endif /* IDOWNLOADOBSERVER_H_ */ diff --git a/libdash/include/IDownloadObserver.h.save b/libdash/include/IDownloadObserver.h.save new file mode 100644 index 00000000..0d67c852 --- /dev/null +++ b/libdash/include/IDownloadObserver.h.save @@ -0,0 +1,58 @@ +/** + * @class dash::network::IDownloadObserver + * @brief This interface is needed for informing the application of the state of the download process. + * @details Informs the application about the download rate and the state of the download. + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IDOWNLOADOBSERVER_H_ +#define IDOWNLOADOBSERVER_H_ + +#include "config.h" + +namespace dash +{ + namespace network + { + enum DownloadState + { + NOT_STARTED = 0, + IN_PROGRESS = 1, + REQUEST_ABORT = 2, + ABORTED = 3, + COMPLETED = 4 + }; + class IDownloadObserver + { + public: + virtual ~IDownloadObserver(){} + + /** + * Informs the dash::network::IDownloadObserver object that the download rate has changed. + * @param bytesDownloaded the number of downloaded bytes + */ + virtual void OnDownloadRateChanged (uint64_t bytesDownloaded) = 0; + + /** + * Informs the dash::network::IDownloadObserver object that the download rate has changed. + * @param bytesDownloaded the number of downloaded bytes + */ + virtual void OnDownloadTimeChanged (double ) = 0; + + /** + * Informs the dash::network::IDownloadObserver object that the download state has changed. + * @param state the download state + */ + virtual void OnDownloadStateChanged (DownloadState state) = 0; + }; + } +} + +#endif /* IDOWNLOADOBSERVER_H_ */ diff --git a/libdash/include/IDownloadableChunk.h b/libdash/include/IDownloadableChunk.h new file mode 100644 index 00000000..d7189cd2 --- /dev/null +++ b/libdash/include/IDownloadableChunk.h @@ -0,0 +1,93 @@ +/** + * @class dash::network::IDownloadableChunk + * @brief This interface is needed for starting and abortinng downloads, reading, peeking and attaching dash::network::IDownloadObservers to this Chunk + * @details Enables the download of media segments with the internal libcurl connection or with external connections that can be passed to this interface + * @see dash::network::IDownloadObserver dash::network::IConnection dash::network::IChunk + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IDOWNLOADABLECHUNK_H_ +#define IDOWNLOADABLECHUNK_H_ + +#include "config.h" + +#include "IDownloadObserver.h" +#include "IConnection.h" +#include "IChunk.h" +#include "IDASHMetrics.h" + +namespace dash +{ + namespace network + { + class IDownloadableChunk : public IChunk, public dash::metrics::IDASHMetrics + { + public: + virtual ~IDownloadableChunk(){} + + /** + * Starts the download of this chunk and returns a bool value whether the starting of the download was possible or not + * @return a bool value + */ + virtual bool StartDownload () = 0; + + /** + * Starts the download of this chunk and returns a bool value whether the starting of the download was possible or not + * @param connection the dash::network::IConnection that shall be used for downloading + * @return a bool value + */ + virtual bool StartDownload (IConnection *connection) = 0; + + /** + * Aborts the download of a chunk + */ + virtual void AbortDownload () = 0; + + /** + * Reads + * @param data pointer to a block of memory + * @param len size of the memory block that can be used by the method + * @return amount of data that has been read + */ + virtual int Read (uint8_t *data, size_t len) = 0; + + /** + * Reads + * @param data pointer to a block of memory + * @param len size of the memory block that can be used by the method + * @return amount of data that has been peeked + */ + virtual int Peek (uint8_t *data, size_t len) = 0; + + /** + * Reads + * @param data pointer to a block of memory + * @param len size of the memory block that can be used by the method + * @param offset the offset to start with + * @return amount of data that has been peeked + */ + virtual int Peek (uint8_t *data, size_t len, size_t offset) = 0; + + /** + * Attaches a dash::network::IDownloadObserver to this Chunk + * @param observer a dash::network::IDownloadObserver + */ + virtual void AttachDownloadObserver (IDownloadObserver *observer) = 0; + + /** + * Detaches a dash::network::IDownloadObserver from this Chunk + * @param observer a dash::network::IDownloadObserver + */ + virtual void DetachDownloadObserver (IDownloadObserver *observer) = 0; + }; + } +} + +#endif /* IDOWNLOADABLECHUNK_H_ */ \ No newline at end of file diff --git a/libdash/include/IHTTPTransaction.h b/libdash/include/IHTTPTransaction.h new file mode 100644 index 00000000..756a1550 --- /dev/null +++ b/libdash/include/IHTTPTransaction.h @@ -0,0 +1,56 @@ +/** + * @class dash::metrics::IHTTPTransaction + * @brief This interface is needed for accessing the attributes and the content of a HTTP Request/Response Transaction + * as specified in ISO/IEC 23009-1, Part 1, 2012, annex D.4.3 + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IHTTPTRANSACTION_H_ +#define IHTTPTRANSACTION_H_ + +#include "config.h" +#include "IThroughputMeasurement.h" + +namespace dash +{ + namespace metrics + { + enum HTTPTransactionType + { + MPD, + XLinkEpansion, + InitializationSegment, + IndexSegment, + MediaSegment, + BitstreamSwitchingSegment, + Other + }; + + class IHTTPTransaction + { + public: + virtual ~IHTTPTransaction (){} + + virtual uint32_t TCPId () const = 0; + virtual HTTPTransactionType Type () const = 0; + virtual const std::string& OriginalUrl () const = 0; + virtual const std::string& ActualUrl () const = 0; + virtual const std::string& Range () const = 0; + virtual const std::string& RequestSentTime () const = 0; + virtual const std::string& ResponseReceivedTime () const = 0; + virtual uint16_t ResponseCode () const = 0; + virtual uint64_t Interval () const = 0; + virtual const std::vector& ThroughputTrace () const = 0; + virtual const std::string& HTTPHeader () const = 0; + }; + } +} + +#endif /* IHTTPTRANSACTION_H_ */ diff --git a/libdash/include/IMPD.h b/libdash/include/IMPD.h new file mode 100644 index 00000000..7e69eecf --- /dev/null +++ b/libdash/include/IMPD.h @@ -0,0 +1,207 @@ +/** + * @class dash::mpd::IMPD + * @brief This interface is needed for accessing the attributes and elements of the MPD element + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.1.2, table 3 + * @details A Media Presentation as described in the MPD consists of (all sections refer to ISO/IEC 23009-1, Part 1, 2012) + *
    + *
  • A sequence of one or more Periods as described in section 5.3.2. + *
  • Each Period contains one or more Adaptation Sets as described in 5.3.3. + * In case an Adaptation Set contains multiple media content components, then each media content component is described individually + * as defined in 5.3.4. + *
  • Each Adaptation Set contains one or more Representations as described in 5.3.5. + *
  • A Representation may contain one or more Sub-Representations as described in 5.3.6. + *
  • Adaptation Sets, Representations and Sub-Representations share common attributes and elements that are described in 5.3.7. + *
  • Each Period may contain one or more Subsets that restrict combination of Adaptation Sets for presentation. Subsets are described in 5.3.8. + *
  • Each Representation consists of one or more Segments described in 6. Segment Information is introduced in 5.3.9. + * Segments contain media data and/or metadata to access, decode and present the included media content. + * Representations may also include Sub-Representations as defined in 5.3.6 to describe and extract partial information from a Representation. + *
  • Each Segment consists of one or more Subsegments. Subsegments are described in 6.2.3.2. + *
+ * @see dash::mpd::IMPDElement dash::mpd::IProgramInformation dash::mpd::IBaseUrl dash::mpd::IPeriod dash::mpd::IMetrics + * dash::mpd::IRepresentationBase + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IMPD_H_ +#define IMPD_H_ + +#include "config.h" + +#include "IMPDElement.h" +#include "IProgramInformation.h" +#include "IBaseUrl.h" +#include "IPeriod.h" +#include "IMetrics.h" +#include "IDASHMetrics.h" + +namespace dash +{ + namespace mpd + { + class IMPD : public virtual IMPDElement, public dash::metrics::IDASHMetrics + { + public: + virtual ~IMPD(){} + + /** + * Returns a reference to a vector of pointers to dash::mpd::IProgramInformation objects that specify descriptive information about the program.\n + * For more details refer to the description in section 5.7. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IProgramInformation objects + */ + virtual const std::vector& GetProgramInformations () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IBaseUrl objects that specify Base URLs that can be used for reference resolution + * and alternative URL selection. \n + * For more details refer to the description in section 5.6. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IBaseUrl objects + */ + virtual const std::vector& GetBaseUrls () const = 0; + + /** + * Returns a reference to a vector of strings that specify locations at which the MPD is available. + * @return a reference to a vector of strings + */ + virtual const std::vector& GetLocations () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IPeriod objects that specify the information of a Period.\n + * For more details refer to the description in section 5.3.2. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IPeriod objects + */ + virtual const std::vector& GetPeriods () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IMetrics objects that specify the DASH Metrics.\n + * For more details see section 5.9. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IPeriod objects + */ + virtual const std::vector& GetMetrics () const = 0; + + /** + * Returns a reference to a string that specifies an identifier for the Media Presentation. It is recommended to use an identifier that is unique within + * the scope in which the Media Presentation is published. \n + * If not specified, no MPD-internal identifier is provided. However, for example the URL to the MPD may be used as an identifier for the Media Presentation. + * @return a reference to a string + */ + virtual const std::string& GetId () const = 0; + + /** + * Returns a reference to a vector of strings that specifies a list of Media Presentation profiles as described in section 8 of ISO/IEC 23009-1, Part 1, 2012.\n + * The contents of this attribute shall conform to either the \c pro-simple or \c pro-fancy productions of RFC6381, Section 4.5, without the enclosing \c DQUOTE characters, + * i.e. including only the \c unencodedv or \c encodedv elements respectively. + * As profile identifier the URI defined for the conforming Media Presentation profiles as described in section 8 shall be used. + * @return a reference to a vector of pointers to dash::mpd::IProgramInformation objects + */ + virtual const std::vector& GetProfiles () const = 0; + + /** + * Returns a reference to a string that specifies whether the Media Presentation Description may be updated (\@type=\"dynamic\") or not (\@type=\"static\").\n + * \b NOTE: Static MPDs are typically used for On-Demand services, whereas dynamic MPDs are used for live services. + * @return a reference to a string + */ + virtual const std::string& GetType () const = 0; + + /** + * Returns a reference to a string that specifies + *
    + *
  • the anchor for the computation of the earliest availability time (in UTC) for any Segment in the Media Presentation if \@type=\"dynamic\". + *
  • the Segment availability start time for all Segments referred to in this MPD if \@type=\"static\". + *
+ * If not present, all Segments described in the MPD shall become available at the time the MPD becomes available.\n + * For \@type=\"dynamic\" this attribute shall be present. + * @return a reference to a string + */ + virtual const std::string& GetAvailabilityStarttime () const = 0; + + /** + * Returns a reference to a string that specifies the latest Segment availability end time for any Segment in the Media Presentation. When not present, the value is unknown. + * @return a reference to a string + */ + virtual const std::string& GetAvailabilityEndtime () const = 0; + + /** + * Returns a reference to a string that specifies the duration of the entire Media Presentation. If the attribute is not present, the duration of the Media Presentation is unknown. + * In this case the attribute MPD\@minimumUpdatePeriod shall be present.\n + * This attribute shall be present when the attribute MPD\@minimumUpdatePeriod is not present. + * @return a reference to a string + */ + virtual const std::string& GetMediaPresentationDuration () const = 0; + + /** + * Returns a reference to a string that specifies the smallest period between potential changes to the MPD. + * This can be useful to control the frequency at which a client checks for updates. \n + * If this attribute is not present it indicates that the MPD does not change. + * If MPD\@type is \c \"static\", \c \@minimumUpdatePeriod shall not be present.\n + * Details on the use of the value of this attribute are specified in section 5.4. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a string + */ + virtual const std::string& GetMinimumUpdatePeriod () const = 0; + + /** + * Returns a reference to a string that specifies a common duration used in the definition of the Representation data rate + * (see \c \@bandwidth attribute in section 5.3.5.2 of ISO/IEC 23009-1, Part 1, 2012). + * @return a reference to a string + */ + virtual const std::string& GetMinBufferTime () const = 0; + + + /** + * Returns a reference to a string that specifies the duration of the time shifting buffer that is guaranteed to be available for a Media Presentation + * with type \c \"dynamic\". When not present, the value is infinite. This value of the attribute is undefined if the type attribute is equal to \c \"static\". + * @return a reference to a string + */ + virtual const std::string& GetTimeShiftBufferDepth () const = 0; + + /** + * Returns a reference to a string that specifies + *
    + *
  • when \c \@type is \c \"dynamic\", a fixed delay offset in time from the presentation time of each access unit that is suggested to be used for presentation of each access unit.\n + For more details refer to 7.2.1. \n + When not specified, then no value is provided and the client is expected to choose a suitable value. +
  • when \c \@type is \c \"static\" the value of the attribute is undefined and may be ignored. +
+ * @return a reference to a string + */ + virtual const std::string& GetSuggestedPresentationDelay () const = 0; + + /** + * Returns a reference to a string that specifies the maximum duration of any Segment in any Representation in the Media Presentation, + * i.e. documented in this MPD and any future update of the MPD. If not present, then the maximum Segment duration shall be the maximum duration of any Segment documented in this MPD. + * @return a reference to a string + */ + virtual const std::string& GetMaxSegmentDuration () const = 0; + + /** + * Returns a reference to a string that specifies the maximum duration of any Media Subsegment in any Representation in the Media Presentation. + * If not present, the same value as for the maximum Segment duration is implied. + * @return a reference to a string + */ + virtual const std::string& GetMaxSubsegmentDuration () const = 0; + + /** + * Returns a pointer to a dash::mpd::IBaseUrl that specifies the absolute path to the MPD file. \n + * This absolute path is needed if there is no BaseURL specified and all other BaseURLs are relative. + * @return a pointer to a dash::mpd::IBaseUrl + */ + virtual IBaseUrl* GetMPDPathBaseUrl () const = 0; + + /** + * Returns the UTC time in seconds when the MPD was fetched.\n + * It is up to the client to check that this value has been set accordingly. \n + * See SetFetchTime() for further details. + * @return an unsigned integer + */ + virtual uint32_t GetFetchTime () const = 0; + }; + } +} + +#endif /* IMPD_H_ */ \ No newline at end of file diff --git a/libdash/include/IMPDElement.h b/libdash/include/IMPDElement.h new file mode 100644 index 00000000..616b5448 --- /dev/null +++ b/libdash/include/IMPDElement.h @@ -0,0 +1,77 @@ +/** + * @class dash::mpd::IMPDElement + * @brief This interface is needed for accessing additional nested XML Elements and XML Attributes of some MPD Classes. + * @details Due to the fact that some MPD classes may contain additional XML Elements, which are not specified in ISO/IEC 23009-1, Part 1, 2012 + * but are attached to them, this interface is needed for retrieving these XML Elements. \n\n + * See example below for clarification (inspired by the example from section G.7 of ISO/IEC 23009-1, Part 1, 2012).\n + * \code{.xml} + * + * http://MoviesSP.example.com/protect?license=kljklsdfiowek + * http://MoviesSP.example.com/protect?content=oyfYvpo8yFyvyo8f + * + * \endcode + * \em ContentProtection is of type DescriptorType which is defined in section 5.8.3 of ISO/IEC 23009-1, Part 1, 2012 as follows: + * \code{.xml} + * + * + * + * + * + * + * + * + * + * \endcode + * So ContentProtection can contain additional XML Elements - here License and Content - + * which are of type dash::xml::INode and can be retrieved by calling GetAdditionalSubNodes(). \n + * Similarly additional XML Attributes that are not specified in ISO/IEC 23009-1, Part 1, 2012 + * can be retrieved by calling GetRawAttributes(), but please mind that all attributes + * of the Element are returned, not just the additional ones. So in the example above both + * attributes \c schemeIDUri (which is specified) and \c additionalAttribute (which is not) are returned. + * @see dash::xml::INode + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IMPDELEMENT_H_ +#define IMPDELEMENT_H_ + +#include "config.h" + +#include "INode.h" + +namespace dash +{ + namespace mpd + { + class IMPDElement + { + public: + virtual ~IMPDElement (){} + + /** + * This method returns a vector of pointers to dash::xml::INode objects which correspond to additional XML Elements of certain + * MPD elements. These XML Elements are not specified in ISO/IEC 23009-1, Part 1, 2012. \n + * See the example in the class description for details. + * @return a vector of pointers to dash::xml::INode objects + */ + virtual const std::vector GetAdditionalSubNodes () const = 0; + + /** + * This method returns a map with key values and mapped values of type std::string of all XML Attributes of certain MPD elements. \n + * Some of these XML Attributes are not specified in ISO/IEC 23009-1, Part 1, 2012. \n + * See the example in the class description for details. + * @return a map with key values and mapped values, both of type std::string + */ + virtual const std::map GetRawAttributes () const = 0; + }; + } +} + +#endif /* IMPDELEMENT_H_ */ diff --git a/libdash/include/IMetrics.h b/libdash/include/IMetrics.h new file mode 100644 index 00000000..c1498727 --- /dev/null +++ b/libdash/include/IMetrics.h @@ -0,0 +1,62 @@ +/** + * @class dash::mpd::IMetrics + * @brief This interface is needed for accessing the attributes and elements of the Metrics element as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.9.2, table 23 + * @details This part of ISO/IEC 23009 does not define mechanisms for reporting metrics, however it does define a set of metrics and a mechanism + * that may be used by the service provider to trigger metric collection and reporting at the clients, should a reporting mechanism be available. + * The trigger mechanism is based on the Metrics element in the MPD. The element contains the list of DASH Metrics for which the measurements are desired, + * the time interval and the granularity for the measurements, as well as the scheme according to which the metric reporting is desired. + * @see dash::mpd::IDescriptor dash::mpd::IRange dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IMETRICS_H_ +#define IMETRICS_H_ + +#include "config.h" + +#include "IMPDElement.h" +#include "IDescriptor.h" +#include "IRange.h" + +namespace dash +{ + namespace mpd + { + class IMetrics : public virtual IMPDElement + { + public: + virtual ~IMetrics(){} + + /** + * Returns a refernce to a vector of pointers to dash::mpd::IDescriptor objects that specify information about the requested reporting method and formats.\n + * For more details refer to section 5.9.4. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IDescriptor objects + */ + virtual const std::vector& GetReportings () const = 0; + + /** + * Returns a refernce to a vector of pointers to dash::mpd::IRange objects that specify the time period during which DASH Metrics collection is requested. + * When not present, DASH Metrics reporting is requested for the whole duration of the content. + * @return a reference to a vector of pointers to dash::mpd::IRange objects + */ + virtual const std::vector& GetRanges () const = 0; + + /** + * Returns a reference to a string that specifies all DASH Metrics (as a list of DASH Metric keys as defined in Annex D of ISO/IEC 23009-1, Part 1, 2012, separated by a comma) + * that the client is desired to report. + * @return a reference to a string + */ + virtual const std::string& GetMetrics () const = 0; + + }; + } +} + +#endif /* IMETRICS_H_ */ \ No newline at end of file diff --git a/libdash/include/IMultipleSegmentBase.h b/libdash/include/IMultipleSegmentBase.h new file mode 100644 index 00000000..06d0dff3 --- /dev/null +++ b/libdash/include/IMultipleSegmentBase.h @@ -0,0 +1,66 @@ +/** + * @class dash::mpd::IMultipleSegmentBase + * @brief This interface is needed for accessing the common elements and attributes for the MultipleBaseInformation type + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.9.2.2, table 12 + * @details It specifies multiple Segment base information + * @see dash::mpd::ISegmentTimeline dash::mpd::ISegmentBase dash::mpd::IURLType + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IMULTIPLESEGMENTBASE_H_ +#define IMULTIPLESEGMENTBASE_H_ + +#include "config.h" + +#include "ISegmentTimeline.h" +#include "ISegmentBase.h" +#include "IURLType.h" + +namespace dash +{ + namespace mpd + { + class IMultipleSegmentBase : public virtual ISegmentBase + { + public: + virtual ~IMultipleSegmentBase(){} + + /** + * Return a pointer to a dash::mpd::ISegmentTimeline object + * @return a pointer to a dash::mpd::ISegmentTimeline object + */ + virtual const ISegmentTimeline* GetSegmentTimeline () const = 0; + + /** + * Returns a pointer to a dash::mpd::IURLType object that specifies the URL including a possible byte range for the Bitstream Switching Segment. + * @return a pointer to a dash::mpd::IURLType object + */ + virtual const IURLType* GetBitstreamSwitching () const = 0; + + /** + * Returns a integer specifying the constant approximate Segment duration. \n + * All Segments within this Representation element have the same duration unless it is the last Segment within the Period, which could be significantly shorter.\n + * The value of the duration in seconds is the division of the value of this attribute and the value of the \c \@timescale attribute associated to the containing Representation.\n + * For more details refer to section 5.3.9.5.3. of ISO/IEC 23009-1, Part 1, 2012. + * @return an unsigned integer + */ + virtual uint32_t GetDuration () const = 0; + + /** + * Returns a integer specifying the number of the first Media Segment in this Representation in the Period.\n + * For more details refer to 5.3.9.5.3. of ISO/IEC 23009-1, Part 1, 2012. + * @return an unsigned integer + */ + virtual uint32_t GetStartNumber () const = 0; + }; + } +} + +#endif /* IMULTIPLESEGMENTBASE_H_ */ \ No newline at end of file diff --git a/libdash/include/INode.h b/libdash/include/INode.h new file mode 100644 index 00000000..96bc6e88 --- /dev/null +++ b/libdash/include/INode.h @@ -0,0 +1,97 @@ +/** + * @class dash::xml::INode + * @brief This interface defines the access to class members of XML Elements. + * @details Due to the fact that some MPD elements may contain additional XML Elements, which are not specified in ISO/IEC 23009-1, Part 1, 2012 + * but are attached to them, this interface is needed to access such an XML Element. \n\n + * For clarification see the example in dash::mpd::IMPDElement + * @see dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef INODE_H_ +#define INODE_H_ + +#include "config.h" + +namespace dash +{ + namespace xml + { + class INode + { + public: + virtual ~INode (){} + + /** + * Returns a reference to a vector of pointers to nested dash::xml::INode objects. + * @return a reference to a vector containing pointers to dash::xml::INode objects. + */ + virtual const std::vector& GetNodes () const = 0; + + /** + * Returns a vector of attribute names belonging to this XML Element + * @return a vector of strings + */ + virtual std::vector GetAttributeKeys () const = 0; + + /** + * Returns the name of this XML Element + * @return a reference to string + */ + virtual const std::string& GetName () const = 0; + + /** + * Returns the text contained in this XML Element + * @return a string + */ + virtual std::string GetText () const = 0; + + /** + * Returns a std::map of key value / mapped value pairs corresponding to the XML Attributes and their values of this XML Element + * @return a reference to a map with key values and mapped values, both of type std::string + */ + virtual const std::map& GetAttributes () const = 0; + + /** + * Returns the type of this XML Element represented by an integer + * \code + * Start = 1 + * End = 15 + * WhiteSpace = 14 + * Text = 3 + * \endcode + * @return an integer + */ + virtual int GetType () const = 0; + + /** + * Returns the value belonging to the XML Attribute specified by key + * @param key the name of the desired XML Attribute + * @return a reference to a string + */ + virtual const std::string& GetAttributeValue (std::string key) const = 0; + + /** + * Returns a bool value determininig whether the XML Attribute name is contained in this XML Element or not. + * @param name the name of the desired XML Attribute + * @return a bool value + */ + virtual bool HasAttribute (const std::string& name) const = 0; + + /** + * Returns a bool value determining whether the XML Element has text or not. + * @return a bool value + */ + virtual bool HasText () const = 0; + }; + } +} + +#endif /* INODE_H_ */ diff --git a/libdash/include/IPeriod.h b/libdash/include/IPeriod.h new file mode 100644 index 00000000..10ec9244 --- /dev/null +++ b/libdash/include/IPeriod.h @@ -0,0 +1,156 @@ +/** + * @class dash::mpd::IPeriod + * @brief This interface is needed for accessing the attributes and elements of the Period element + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.2.2, table 4 + * @details A Media Presentation consists of one or more Periods. A Period is defined by a Period element in the MPD element. \n + * The type of the Period, either a regular Period or an Early Available Period, as well as the \em PeriodStart time of a regular Period is determined as follows: + *
    + *
  • If the attribute \c \@start is present in the Period, then the Period is a regular Period and the \em PeriodStart is equal to the value of this attribute. + *
  • If the \c \@start attribute is absent, but the previous Period element contains a \c \@duration attribute then then this new Period is also a regular Period. + * The start time of the new Period \em PeriodStart is the sum of the start time of the previous Period \em PeriodStart and + * the value of the attribute \c \@duration of the previous Period. + *
  • If \em (i) \c \@start attribute is absent, and \em (ii) the Period element is the first in the MPD, + * and \em (iii) the MPD\@type is \c 'static', then the \em PeriodStart time shall be set to zero. + *
  • If \em (i) \c \@start attribute is absent, and \em (ii) the previous Period element does not contain a \c \@duration attribute or + * the Period element is the first in the MPD, and (iii) the MPD\@type is \c 'dynamic', + * then this Period is an Early Available Period (see below for details). + *
+ * For any regular Period the following holds: \em PeriodStart reflects the actual time that should elapse after playing the media of all prior Periods in this Media Presentation + * relative to the \em PeriodStart time of the first Period in the Media Presentation. The Period extends until the \em PeriodStart of the next Period, or until the end of the + * Media Presentation in the case of the last Period. More specifically, the difference between the \em PeriodStart time of a Period and + * either the \em PeriodStart time of the following Period, if this is not the last Period, or the value of the MPD\@mediaPresentationDuration if this is the last one, + * is the presentation duration in Media Presentation time of the media content represented by the Representations in this Period. \n\n + * Early Available Periods may be used to advertise initialization of other non-media data before the media data itself is available. + * Period elements documenting early available Periods shall not occur before any Period element documenting a regular Period. For Early Available Periods, + * any resources that are announced in such a Period element shall be available. Such a Period element shall not contain URLs to Media Segments. + * The data contained in such a Period element does not represent a Period in the Media Presentation. Only when the \em PeriodStart time becomes known + * through an update of the MPD, such a Period element represents a regular Period. However, an update of the MPD may even remove a Period element + * representing an Early Available Period in later updates of the MPD as long as no \em PeriodStart time is associated with the Period. \n\n + * To avoid dereferencing of a remote element containing a Period element solely to determine the Period timeline, e.g. in case of seeking, + * Period\@start or previous Period's Period\@duration should be present in the MPD. + * @see dash::mpd::IMPDElement dash::mpd::BaseUrl dash::mpd::IAdaptationSet dash::mpd::ISegmentBase dash::mpd::ISegmentList dash::mpd::ISegmentTemplate dash::mpd::ISubset + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IPERIOD_H_ +#define IPERIOD_H_ + +#include "config.h" + +#include "IMPDElement.h" +#include "IBaseUrl.h" +#include "ISegmentBase.h" +#include "ISegmentList.h" +#include "ISegmentTemplate.h" +#include "IAdaptationSet.h" +#include "ISubset.h" + +namespace dash +{ + namespace mpd + { + class IPeriod : public virtual IMPDElement + { + public: + virtual ~IPeriod(){} + + /** + * Returns a reference to a vector of pointers to dash::mpd::IBaseUrl objects that specify base URLs that can be used for reference resolution and alternative URL selection.\n + * For more details refer to the description in section 5.6. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IBaseUrl objects + */ + virtual const std::vector& GetBaseURLs () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegmentBase object that specifies default Segment Base information.\n + * Information in this element is overridden by information in AdapationSet.SegmentBase and Representation.SegmentBase, if present.\n + * For more details see section 5.3.9. of ISO/IEC 23009-1, Part 1, 2012. + * @return a pointer to a dash::mpd::ISegmentBase object + */ + virtual ISegmentBase* GetSegmentBase () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegmentList object that specifies default Segment List information.\n + * Information in this element is overridden by information in AdapationSet.SegmentList and Representation.SegmentList, if present.\n + * For more details see section 5.3.9. of ISO/IEC 23009-1, Part 1, 2012. + * @return a pointer to a dash::mpd::ISegmentList object + */ + virtual ISegmentList* GetSegmentList () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegmentTemplate object that specifies default Segment Template information.\n + * Information in this element is overridden by information in AdapationSet.SegmentTemplate and Representation.SegmentTemplate, if present. + * For more details see section 5.3.9. of ISO/IEC 23009-1, Part 1, 2012. + * @return a pointer to a dash::mpd::ISegmentTemplate object + */ + virtual ISegmentTemplate* GetSegmentTemplate () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IAdaptationSet objects that specify Adapatation Sets.\n + * At least one Adaptation Set shall be present in each Period. However, the actual element may be present only in a remote element if xlink is in use.\n + * For more details see section 5.3.3. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IAdaptationSet objects + */ + virtual const std::vector& GetAdaptationSets () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::ISubset objects that specify Subsets.\n + * For more details see section 5.3.8. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::ISubset objects + */ + virtual const std::vector& GetSubsets () const = 0; + + /** + * Returns a reference to a string that specifies a reference to an external Period element. + * @return a reference to a string + */ + virtual const std::string& GetXlinkHref () const = 0; + + /** + * Returns a reference to a string that specifies the processing instructions, which can be either \c \"onLoad\" or \c \"onRequest\".\n + * This attribute shall not be present if the \c \@xlink:href attribute is not present. + * @return a reference to a string + */ + virtual const std::string& GetXlinkActuate () const = 0; + + /** + * Returns an reference to a string that specifies an identifier for this Period. + * The attribute shall be unique in the scope of the Media Presentation. + * @return a reference to a string + */ + virtual const std::string& GetId () const = 0; + + /** + * Returns a reference to a string that specifies the \em PeriodStart time of the Period.The \em PeriodStart time is used as an anchor to determine the MPD start + * time of each Media Segment as well as to determine the presentation time of each each access unit in the Media Presentation timeline.\n + * If not present, refer to the details in section 5.3.2.1. of ISO/IEC 23009-1, Part 1, 2012 + * @return a reference to a string + */ + virtual const std::string& GetStart () const = 0; + + /** + * Returns a reference to a string that specifies the duration of the Period to determine the \em PeriodStart time of the next Period.\n + * If not present, refer to the details in section 5.3.2.1. of ISO/IEC 23009-1, Part 1, 2012 + * @return a reference to a string + */ + virtual const std::string& GetDuration () const = 0; + + /** + * When set to \c 'true', this is equivalent as if the AdaptationSet\@bitstreamSwitching for each Adaptation Set contained in this Period is set to \c 'true'. + * In this case, the AdaptationSet\@bitstreamSwitching attribute shall not be set to \c 'false' for any Adaptation Set in this Period. + * @return a bool value + */ + virtual bool GetBitstreamSwitching () const = 0; + + }; + } +} + +#endif /* IPERIOD_H_ */ \ No newline at end of file diff --git a/libdash/include/IProgramInformation.h b/libdash/include/IProgramInformation.h new file mode 100644 index 00000000..58c03750 --- /dev/null +++ b/libdash/include/IProgramInformation.h @@ -0,0 +1,69 @@ +/** + * @class dash::mpd::IProgramInformation + * @brief This interface is needed for accessing the attributes and elements of the Program Information element as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.7.2, table 20 + * @details Descriptive information on the program may be provided for a Media Presentation within the ProgramInformation element. + * When multiple ProgramInformation elements are present, the \c \@lang attribute shall be present and + * each element shall describe the Media Presentation sufficiently in the language defined by the value of the \c \@lang attribute. + * For each language, the program information may specify title, source of the program, copyright information, and a URL to more information. + * @see dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IPROGRAMINFORMATION_H_ +#define IPROGRAMINFORMATION_H_ + +#include "config.h" + +#include "IMPDElement.h" + +namespace dash +{ + namespace mpd + { + class IProgramInformation : public virtual IMPDElement + { + public: + virtual ~IProgramInformation(){} + + /** + * Returns a reference to a string that specifies the title for the Media Presentation. + * @return a reference to a string + */ + virtual const std::string& GetTitle () const = 0; + + /** + * Returns a reference to a string that specifies information about the original source (for example content provider) of the Media Presentation. + * @return a reference to a string + */ + virtual const std::string& GetSource () const = 0; + + /** + * Returns a reference to a string that specifies a copyright statement for the Media Presentation, usually starting with the copyright symbol, unicode \c U+00A9. + * @return a reference to a string + */ + virtual const std::string& GetCopyright () const = 0; + + /** + * Returns a reference to a string that declares the language code(s) for this Program Information. The syntax and semantics according to IETF RFC 5646 shall be applied. + * @return a reference to a string + */ + virtual const std::string& GetLang () const = 0; + + /** + * Returns a reference to a string that specifies an absolute URL which provides more information about the Media Presentation. + * @return a reference to a string + */ + virtual const std::string& GetMoreInformationURL () const = 0; + + }; + } +} + +#endif /* IPROGRAMINFORMATION_H_ */ \ No newline at end of file diff --git a/libdash/include/IRange.h b/libdash/include/IRange.h new file mode 100644 index 00000000..0f85f98d --- /dev/null +++ b/libdash/include/IRange.h @@ -0,0 +1,53 @@ +/** + * @class dash::mpd::IRange + * @brief This interface is needed for accessing the attributes and elements of the Metrics Range element as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.9.2, table 23 + * @details It specifies the time period during which DASH Metrics collection is requested. + * @see dash::mpd::IMetrics + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IRANGE_H_ +#define IRANGE_H_ + +#include "config.h" + +namespace dash +{ + namespace mpd + { + class IRange + { + public: + virtual ~IRange(){} + + /** + * Returns a reference to a string that specifies the start time of the DASH Metrics collection operation. + * When not present, DASH Metrics collection is requested from the beginning of content consumption. + * For services with MPD\@type='dynamic', the start time is indicated in wallclock time by adding the value of this + * attribute to the value of the MPD\@availabilityStartTime attribute. + * For services with MPD\@type='static', the start time is indicated in Media Presentation time and is relative to the + * PeriodStart time of the first Period in this MPD. + * \b NOTE: For example, if MPD\@availabilityStartTime is 14:30 and the metrics collection is intended to start at 14:45, then \c \@starttime is 0:15. + * @return a reference to a string + */ + virtual const std::string& GetStarttime () const = 0; + + /** + * Returns a reference to a string that specifies the duration of the DASH metrics collection interval. The value of the attribute expresses in Media Presentation time. + * If not present, the value is identical to the Media Presentation duration. + * @return a reference to string + */ + virtual const std::string& GetDuration () const = 0; + + }; + } +} + +#endif /* IRANGE_H_ */ \ No newline at end of file diff --git a/libdash/include/IRepresentation.h b/libdash/include/IRepresentation.h new file mode 100644 index 00000000..0762d870 --- /dev/null +++ b/libdash/include/IRepresentation.h @@ -0,0 +1,159 @@ +/** + * @class dash::mpd::IRepresentation + * @brief This interface is needed for accessing the attributes and elements of the Representation element + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.5.2, table 7 + * @details Representations are described by the Representation element. Representation elements are contained in an AdaptationSet element.\n + * A Representation is one of the alternative choices of the complete set or subset of media content components comprising the media content during the defined Period.\n + * A Representation starts at the start of the Period \em PeriodStart and continues to the end of the Period, i.e. the start of the next Period or the end of the Media Presentation.\n + * Each Representation includes one or more media streams, where each media stream is an encoded version of one media content component.\n + * A Representation consists of one or more Segments.\n + * Each Representation either shall contain an Initialization Segment or each Media Segment in the Representation shall be self-initializing, + * i.e. the Media Segment itself conforms to the media type as specified in the \c \@mimeType attribute for this Representation. \n + * When a Representation is not a dependent Representation, i.e. the \c \@dependencyId attribute is absent, then concatenation of the Initialization Segment, + * if present, and all consecutive Media Segments in one Representation shall represent a conforming Segment sequence as defined in section 4.5.3 of ISO/IEC 23009-1, Part 1, 2012 + * conforming to the media type as specified in the \c \@mimeType attribute for this Representation.\n + * Dependent Representations are described by a Representation element that contains a \c \@dependencyId attribute. + * Dependent Representations are regular Representations except that they depend on a set of complementary Representations for decoding and/or presentation. + * The \c \@dependencyId contains the values of the \c \@id attribute of all the complementary Representations, i.e. Representations that are necessary to + * present and/or decode the media content components contained in this dependent Representation.\n + * For any dependent Representation X that depends on complementary Representation Y, the m-th Subsegment of X and the n-th Subsegment of Y + * shall be non-overlapping (as defined in 4.5.3) whenever \em m is not equal to \em n. For dependent Representations the concatenation of the Initialization Segment with the + * sequence of Subsegments of the dependent Representations, each being preceded by the corresponding Subsegment of each of the complementary Representations + * in order as provided in the \c \@dependencyId attribute shall represent a conforming Subsegment sequence as defined in 4.5.3 conforming to the media + * format as specified in the \c \@mimeType attribute for this dependent Representation.\n\n + * \b NOTE: When decoding of a dependent Representation is started from a SAP in the (Sub)Segment with number \em i, the decoding process does not need to access + * data from the complementary Representation(s) from any earlier (sub)segments than (sub)Segment with number i of the complementary Representation(s). + * @see dash::mpd::IRepresentationBase dash::mpd::ISegmentBase dash::mpd::ISegmentList dash::mpd::ISegmentTemplate dash::mpd::IBaseUrl dash::mpd::ISubRepresentation + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IREPRESENTATION_H_ +#define IREPRESENTATION_H_ + +#include "config.h" + +#include "IBaseUrl.h" +#include "ISubRepresentation.h" +#include "ISegmentBase.h" +#include "ISegmentList.h" +#include "ISegmentTemplate.h" +#include "IRepresentationBase.h" + +namespace dash +{ + namespace mpd + { + class IRepresentation : public virtual IRepresentationBase + { + public: + virtual ~IRepresentation(){} + + /** + * Returns a reference to a vector of pointers to dash::mpd::IBaseUrl objects that specifies Base URLs that can be used for reference resolution and alternative URL selection.\n + * For more details refer to the description in section 5.6. of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IBaseUrl objects + */ + virtual const std::vector& GetBaseURLs () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::ISubRepresentation objects that specifies information about Sub-Representations + * that are embedded in the containing Representation. + * For more detail see section 5.3.6 of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::ISubRepresentation objects + */ + virtual const std::vector& GetSubRepresentations () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegmentBase object that specifies default Segment Base information. + * For more detail see section 5.3.9 of ISO/IEC 23009-1, Part 1, 2012. + * @return a pointer to a dash::mpd::ISegmentBase object + */ + virtual ISegmentBase* GetSegmentBase () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegmentList object that specifies the Segment List information. + * For more detail see section 5.3.9 of ISO/IEC 23009-1, Part 1, 2012. + * @return a pointer to a dash::mpd::ISegmentList object + */ + virtual ISegmentList* GetSegmentList () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegmentTemplate object that specifies the Segment Template information. + * For more detail see section 5.3.9 of ISO/IEC 23009-1, Part 1, 2012. + * @return a pointer to a dash::mpd::ISegmentTemplate object + */ + virtual ISegmentTemplate* GetSegmentTemplate () const = 0; + + /** + * Returns a reference to a string that specifies an identifier for this Representation. The identifier shall be unique within a Period + * unless the Representation is functionally identically to another Representation in the same Period. \n + * The identifier shall not contain whitespace characters. \n + * If used in the template-based URL construction as defined in 5.3.9.4.4 of ISO/IEC 23009-1, Part 1, 2012, + * the string shall only contain characters that are permitted within an HTTP-URL according to RFC 1738. + * @return a reference to a string + */ + virtual const std::string& GetId () const = 0; + + /** + * Returns an integer that specifies a bandwidth in bits per seconds (bps).\n + * If the Representation is continuously delivered at this bitrate, starting at any SAP that is indicated either by \c \@startWithSAP + * or by any Segment Index box, a client can be assured of having enough data for continuous playout providing playout begins after + * \c \@minBufferTime * \c \@bandwidth bits have been received (i.e. at time \c \@minBufferTime after the first bit is received).\n\n + * For dependent Representations this value shall specify the minimum bandwidth as defined above of this Representation and all complementary Representations. + * @return an unsigned integer + */ + virtual uint32_t GetBandwidth () const = 0; + + /** + * Returns an integer that specifies a quality ranking of the Representation relative to other Representations in the same Adaptation Set. + * Lower values represent higher quality content. + * @return an unsigned integer + */ + virtual uint32_t GetQualityRanking () const = 0; + + /** + * Returns a reference to a vector of strings that specifies all complementary Representations the Representation depends on in the decoding and/or + * presentation process as values of \c \@id attributes.\n + * If not present, the Representation can be decoded and presented independently of any other Representation. \n + * This attribute shall not be present where there are no dependencies. + * @return a reference to a vector of strings + */ + virtual const std::vector& GetDependencyId () const = 0; + + /** + * Returns a reference to a vector of strings that specifies media stream structure identifier values.\n + * The attribute may be present for Representations containing video and its semantics are unspecified for any other type of Representations.\n + * If present, the attribute \c \@mediaStreamStructureId specifies a whitespace-separated list of media stream structure identifier values. + * If media streams share the same media stream structure identifier value, the media streams shall have the following characteristics: + *
    + *
  • The media streams have the same number of Stream Access Points of type 1 to 3. + *
  • The values of TSAP, TDEC, TEPT, and TPTF of the i-th SAP of type 1 to 3 in one media stream are identical + * to the values of TSAP, TDEC, TEPT, and TPTF, respectively, of the i-th SAP of type 1 to 3 + * in the other media streams for any value of \em i from 1 to the number of SAPs of type 1 to 3 in any of the media streams. + *
  • A media stream formed by concatenating the media stream of a first Representation until ISAU (exclusive) of the i-th SAP of type 1 to 3 and the + * media stream of a second Representation (having the same media stream structure identifier value as for the first Representation) starting from the ISAU + * (inclusive) of the i-th SAP of type 1 to 3 conforms to the specification in which the media stream format is specified for any value of \em i from 1 to the number + * of SAPs of type 1 to 3 in either media stream. Furthermore, the decoded pictures have an acceptable quality regardless of type of the Stream Access Point access unit used. + *
+ * All media stream structure identifier values for one Adaptation Set shall differ from those of another Adaptation Set.\n + * If not present, then for this Representation no similarities to other Representations are known. + * \b NOTE Indicating multiple media stream structure identifier values for a Representation can be useful in cases where switching between Representations A and B + * as well as between Representations B and C is allowed at non-IDR intra pictures, but switching between Representations A and C would cause too + * severe a degradation in the quality of the leading pictures and is hence not allowed. To indicate these permissions and restrictions, + * Representation A would contain \c \@mediaStreamStructureId equal to \"1"\", Representation B would contain \c \@mediaStreamStructureId equal to \"1 2\", + * and Representation C would contain \c \@mediaStreamStructureId equal to \"2\". + * @return a reference to a vector of strings + */ + virtual const std::vector& GetMediaStreamStructureId () const = 0; + }; + } +} + +#endif /* IREPRESENTATION_H_ */ diff --git a/libdash/include/IRepresentationBase.h b/libdash/include/IRepresentationBase.h new file mode 100644 index 00000000..e7b7e688 --- /dev/null +++ b/libdash/include/IRepresentationBase.h @@ -0,0 +1,181 @@ +/** + * @class dash::mpd::IRepresentationBase + * @brief This interface is needed for accessing the common attributes and elements of the certain MPD element as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.7 + * @details The elements \c AdaptationSet, \c Representation and \c SubRepresentation have assigned common attributes and elements that are specified in + * ISO/IEC 23009-1, Part 1, 2012, section 5.3.7.2, table 9 + * @see dash::mpd::IDescriptor dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IREPRESENTATIONBASE_H_ +#define IREPRESENTATIONBASE_H_ + +#include "config.h" + +#include "IMPDElement.h" +#include "IDescriptor.h" + +namespace dash +{ + namespace mpd + { + class IRepresentationBase : public virtual IMPDElement + { + public: + virtual ~IRepresentationBase(){} + + /** + * Returns a reference to a vector of pointers to dash::mpd::IDescriptor objects that specifies frame-packing arrangement information of the video media component type.\n + * When no FramePacking element is provided for a video component, frame-packing shall not used for the video media component.\n + * For further details see sections 5.8.1 and 5.8.4.6 of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IDescriptor objects + */ + virtual const std::vector& GetFramePacking () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IDescriptor objects that specifies the audio channel configuration of the audio media component type.\n + * For further details see sections 5.8.1 and 5.8.4.7 of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IDescriptor objects + */ + virtual const std::vector& GetAudioChannelConfiguration () const = 0; + + /** + * Returns a reference to a vector of pointers to dash::mpd::IDescriptor objects that specifies information about content protection schemes used for the associated Representations.\n + * For further details see sections 5.8.1 and 5.8.4.1 of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a vector of pointers to dash::mpd::IDescriptor objects + */ + virtual const std::vector& GetContentProtection () const = 0; + + /** + * Returns a reference to a vector of strings that specifies the profiles which the associated Representation(s) + * conform to of the list of Media Presentation profiles as described in section 8 of ISO/IEC 23009-1, Part 1, 2012. + * The value shall be a subset of the respective value in any higher level of the document hierarchy (Representation, Adaptation Set, MPD).\n + * If not present, the value is inferred to be the same as in the next higher level of the document hierarchy. + * For example, if the value is not present for a Representation, then \c \@profiles at the Adaptation Set level is valid for the Representation.\n + * The same syntax as defined in 5.3.1.2 shall be used. + * @return a reference to a vector of strings + */ + virtual const std::vector& GetProfiles () const = 0; + + /** + * Returns an integer that specifies the horizontal visual presentation size of the video + * media type on a grid determined by the \c \@sar attribute. \n\n + * In the absence of \c \@sar width and height are specified as if the value of \c \@sar were \"1:1\".\n + * \b NOTE: The visual presentation size of the video is equal to the number of horizontal and vertical samples used for presentation + * after encoded samples are cropped in response to encoded cropping parameters, “overscan” signaling, or “pan/scan” display parameters, e.g. SEI messages. + * @return an unsigned integer + */ + virtual uint32_t GetWidth () const = 0; + + /** + * Returns an integer that specifies the vertical visual presentation size of the video + * media type, on a grid determined by the \c \@sar attribute. + * @return an unsigned integer + */ + virtual uint32_t GetHeight () const = 0; + + /** + * Returns a string that specifies the sample aspect ratio of the video media component type, + * in the form of a string consisting of two integers separated by ':', e.g., \"10:11\". + * The first number specifies the horizontal size of the encoded video pixels (samples) in arbitrary units. + * The second number specifies the vertical size of the encoded video pixels (samples) in same units as the horizontal size. + * @return a string + */ + virtual std::string GetSar () const = 0; + + /** + * Returns a string that specifies the output frame rate (or in the case of interlaced, half the output field rate) + * of the video media type in the Representation. If the frame or field rate is varying, the value is the average frame + * or half the average field rate field rate over the entire duration of the Representation.\n + * The value is coded as a string, either containing two integers separated by a \"/\", (\"F/D\"), or a single integer \"F\". + * The frame rate is the division F/D, or F, respectively, per second (i.e. the default value of D is \"1\"). + * @return a string + */ + virtual std::string GetFrameRate () const = 0; + + /** + * Returns a string that represents an audio sampling rate. \n + * Either a single decimal integer value specifying the sampling rate or a whitespace separated pair of decimal integer + * values specifying the minimum and maximum sampling rate of the audio media component type. The values are in samples per second. + * @return a string + */ + virtual std::string GetAudioSamplingRate () const = 0; + + /** + * Returns a string that specifies the MIME type of the concatenation of the Initialization Segment, if present, + * and all consecutive Media Segments in the Representation. + * @return a string + */ + virtual std::string GetMimeType () const = 0; + + /** + * Returns a reference to a vector of strings that specifies the profiles of Segments that are essential to process the Representation. + * The detailed semantics depend on the value of the \c \@mimeType attribute. The contents of this attribute shall conform to either the + * \c pro-simple or \c pro-fancy productions of RFC6381, Section 4.5, without the enclosing \c DQUOTE characters, + * i.e. including only the \c unencodedv or \c encodedv elements respectively. + * As profile identifier the brand identifier for the Segment as defined in section 6 of ISO/IEC 23009-1, Part 1, 2012 shall be used.\n + * If not present on any level, the value may be deducted from the value of the\c \@profiles attribute. + * @return a reference to a vector of strings + */ + virtual const std::vector& GetSegmentProfiles () const = 0; + + /** + * Returns a reference to a vector of strings that specifies the codecs present within the Representation. + * The codec parameters shall also include the profile and level information where applicable. \n + * The contents of this attribute shall conform to either the \c simp-list or \c fancy-list productions of RFC6381, Section 3.2, + * without the enclosing \c DQUOTE characters. The codec identifier for the Representation's media format, + * mapped into the name space for codecs as specified in RFC6381, Section 3.3, shall be used. + * @return a reference to a vector of strings + */ + virtual const std::vector& GetCodecs () const = 0; + + /** + * Returns a double value specifying the maximum SAP interval in seconds of all contained media streams, + * where the SAP interval is the maximum time interval between the TSAP of any two successive SAPs of types 1 to 3 + * inclusive of one media stream in the associated Representations. + * @return a double + */ + virtual double GetMaximumSAPPeriod () const = 0; + + /** + * Returns an unsigned integer that (when greater than 0) specifies that in the associated Representations, + * each Media Segment starts with a SAP of type less than or equal to the value of this attribute value in each media stream. + * @return an unsigned integer + */ + virtual uint8_t GetStartWithSAP () const = 0; + + /** + * Returns a double that specifies the maximum playout rate as a multiple of the regular playout rate, + * which is supported with the same decoder profile and level requirements as the normal playout rate. + * @return a double + */ + virtual double GetMaxPlayoutRate () const = 0; + + /** + * Returns a bool value that informs about coding dependency.\n + * If \c 'true', for all contained media streams, specifies that there is at least one access unit that depends on one or more other access units for decoding. + * If \c 'false', for any contained media stream, there is no access unit that depends on any other access unit for decoding (e.g. for video all the pictures are intra coded). \n + * If not specified on any level, there may or may not be coding dependency between access units. + * @return a bool value + */ + virtual bool HasCodingDependency () const = 0; + + /** + * Returns a string that specifies the scan type of the source material of the video media component type. + * The value may be equal to one of \c \"progressive\", \c \"interlaced\" and \c \"unknown\". + * @return a string + */ + virtual std::string GetScanType () const = 0; + + }; + } +} + +#endif /* IREPRESENTATIONBASE_H_ */ \ No newline at end of file diff --git a/libdash/include/ISegment.h b/libdash/include/ISegment.h new file mode 100644 index 00000000..32355ef5 --- /dev/null +++ b/libdash/include/ISegment.h @@ -0,0 +1,86 @@ +/** + * @class dash::mpd::ISegment + * @brief This interface is needed for setting values of a Segment + * @details Several methods for setting the values of member variables are provided. \n\n + * These are only needed in case you do not want to use the values provided by the MPD. + * @see dash::network::IDownloadableChunk dash::network::IChunk + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ISEGMENT_H_ +#define ISEGMENT_H_ + +#include "config.h" + +#include "IDownloadableChunk.h" + +namespace dash +{ + namespace mpd + { + class ISegment : public virtual network::IDownloadableChunk + { + public: + virtual ~ISegment(){} + + /** + * This method allows you to specify an absolute URI for this Segment + * @param uri a string representing an URI + */ + virtual void AbsoluteURI (std::string uri) = 0; + + /** + * This method allows you to specify a host for this Segment + * @param host a string representing an host + */ + virtual void Host (std::string host) = 0; + + /** + * This method allows you to specify a port for this Segment + * @param port an integer representing a portnumber + */ + virtual void Port (size_t port) = 0; + + /** + * This method allows you to specify a path for this Segment + * @param path a string representing a path + */ + virtual void Path (std::string path) = 0; + + /** + * This method allows you to specify a byte range for this Segment + * @param range a string representing a byte range as definend in ISO/IEC 23009-1, Part 1, 2012, section 5.3.9.2.2, table 13:\n + The byte range shall be expressed and formatted as a \c byte-range-spec as defined in RFC 2616, Clause 14.35.1. + It is restricted to a single expression identifying a contiguous range of bytes. + */ + virtual void Range (std::string range) = 0; + + /** + * This method allows you to specify the start byte for this Segment + * @param startByte an integer representing the start byte + */ + virtual void StartByte (size_t startByte) = 0; + + /** + * This method allows you to specify the end byte for this Segment + * @param endByte an integer representing the end byte + */ + virtual void EndByte (size_t endByte) = 0; + + /** + * This method allows you to specify whether this Segment has a byte range or not + * @param hasByteRange a bool value, \c true to specify that this Segment has a byte range, \c false otherwise + */ + virtual void HasByteRange (bool hasByteRange) = 0; + }; + } +} + +#endif /* ISEGMENT_H_ */ \ No newline at end of file diff --git a/libdash/include/ISegmentBase.h b/libdash/include/ISegmentBase.h new file mode 100644 index 00000000..a29a4eab --- /dev/null +++ b/libdash/include/ISegmentBase.h @@ -0,0 +1,86 @@ +/** + * @class dash::mpd::ISegmentBase + * @brief This interface is needed for accessing the common elements and attributes of SegmentBase + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.9.2.2, table 11 + * @details It specifies Semgent base elements as well as the type for the Segment base information. \n + * The SegmentBase is sufficient if only a single Media Segment is provided per Representation and the Media Segment URL is included in the BaseURL element.\n + * In case multiple Media Segments are present, either a SegmentList or a SegmentTemplate is used + * that share the multiple Segment base information as provided in 5.3.9.2.3, Table 12. \n + * If the Representation contains more than one Media Segment, then either the attribute \c \@duration or the element SegmentTimeline shall be present. + * The attribute \c \@duration and the element SegmentTimeline shall not be present at the same time. + * Segments described by the Segment base information are referenced by an HTTP-URL conforming to the type URLType as defined in Table 13. + * @see dash::mpd::IURLType dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ISEGMENTBASE_H_ +#define ISEGMENTBASE_H_ + +#include "config.h" + +#include "IMPDElement.h" +#include "IURLType.h" + +namespace dash +{ + namespace mpd + { + class ISegmentBase : public virtual IMPDElement + { + public: + virtual ~ISegmentBase(){} + + /** + * Returns a pointer to a dash::mpd::IURLType object that specifies the URL including a possible byte range for the Initialization Segment. + * @return a pointer to dash::mpd::IURLType object + */ + virtual const IURLType* GetInitialization () const = 0; + + /** + * Returns a pointer to a dash::mpd::IURLType object that specifies the URL including a possible byte range for the Representation Index Segment. + * @return a pointer to dash::mpd::IURLType object + */ + virtual const IURLType* GetRepresentationIndex () const = 0; + + /** + * Returns an integer representing a timescale that specifies the timescale in units per seconds + * to be used for the derivation of different real-time duration values in the Segment Information.\n + * \b NOTE: This may be any frequency but typically is the media clock frequency of one of the media streams (or a positive integer multiple thereof). + * @return an unsigned integer + */ + virtual uint32_t GetTimescale () const = 0; + + /** + * Returns an integer that specifies the presentation time offset of the Representation relative to the start of the Period.\n + * The value of the presentation time offset in seconds is the division of the value of this attribute and the value of the \c \@timescale attribute.\n + * If not present on any level, the value of the presentation time offset is 0. + * @return an unsigned integer + */ + virtual uint32_t GetPresentationTimeOffset () const = 0; + + /** + * Returns a string that specifies the byte range that contains the Segment Index in all Media Segments of the Representation.\n + * The byte range shall be expressed and formatted as a \c byte-range-spec as defined in RFC 2616, Clause 14.35.1. + * It is restricted to a single expression identifying a contiguous range of bytes. + * @return a reference to a string + */ + virtual const std::string& GetIndexRange () const = 0; + + /** + * When set to \c 'true' specifies that for all Segments in the Representation, the data outside the prefix defined by \c \@indexRange contains the data + * needed to access all access units of all media streams syntactically and semantically. + * @return a bool value + */ + virtual bool HasIndexRangeExact () const = 0; + }; + } +} + +#endif /* ISEGMENTBASE_H_ */ \ No newline at end of file diff --git a/libdash/include/ISegmentList.h b/libdash/include/ISegmentList.h new file mode 100644 index 00000000..803df66d --- /dev/null +++ b/libdash/include/ISegmentList.h @@ -0,0 +1,58 @@ +/** + * @class dash::mpd::ISegmentList + * @brief This interface is needed for accessing the attributes and elements of the SegmentList element + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.9.3.2, table 14 + * @details The Segment list is defined by one or more SegmentList elements. Each SegmentList element itself contains a list of SegmentURL elements + * for a consecutive list of Segment URLs. Each Segment URL may contain the Media Segment URL and possibly a byte range. + * The Segment URL element may also contain an Index Segment. + * @see dash::mpd::ISegmentURL dash::mpd::IMultipleSegmentBase + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ISEGMENTLIST_H_ +#define ISEGMENTLIST_H_ + +#include "config.h" + +#include "ISegmentURL.h" +#include "IMultipleSegmentBase.h" + +namespace dash +{ + namespace mpd + { + class ISegmentList : public virtual IMultipleSegmentBase + { + public: + virtual ~ISegmentList(){} + + /** + * Returns a reference to a vector of pointers to dash::mpd::ISegmentURL objects that are contained in this SegmentList. + * @return a reference to a vector of pointers to dash::mpd::ISegmentURL objects + */ + virtual const std::vector& GetSegmentURLs () const = 0; + + /** + * Returns a reference to a string that specifies a reference to an external SegmentList element. + * @return a reference to a string + */ + virtual const std::string& GetXlinkHref () const = 0; + + /** + * Returns a reference to a string that specifies the processing set - can be either \c \"onLoad\" or \c \"onRequest\". + * @return a reference to a string + */ + virtual const std::string& GetXlinkActuate () const = 0; + + }; + } +} + +#endif /* ISEGMENTLIST_H_ */ \ No newline at end of file diff --git a/libdash/include/ISegmentTemplate.h b/libdash/include/ISegmentTemplate.h new file mode 100644 index 00000000..d7072956 --- /dev/null +++ b/libdash/include/ISegmentTemplate.h @@ -0,0 +1,148 @@ +/** + * @class dash::mpd::ISegmentTemplate + * @brief This interface is needed for accessing the attributes and elements of the SegmentTemplate element + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.9.4.2, table 15 + * @details The Segment template is defined by the SegmentTemplate element. In this case, specific identifiers that are substituted by dynamic values assigned to Segments, + * to create a list of Segments. The substitution rules are provided in section 5.3.9.4.4 of ISO/IEC 23009-1, Part 1, 2012. + * @see dash::mpd::ISegment dash::mpd::IMultipleSegmentBase + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + + +#ifndef ISEGMENTTEMPLATE_H_ +#define ISEGMENTTEMPLATE_H_ + +#include "config.h" + +#include "IMultipleSegmentBase.h" + +namespace dash +{ + namespace mpd + { + class ISegmentTemplate : public virtual IMultipleSegmentBase + { + public: + virtual ~ISegmentTemplate(){} + + /** + * Returns a reference to a string that specifies the template to create the Media Segment List.\n + * For more details on template-based segment URL construction refer to section 5.3.9.4.4 of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a string + */ + virtual const std::string& Getmedia () const = 0; + + /** + * Returns a reference to a string that specifies the template to create the Index Segment List. If neither the \em \$Number\$ nor the \em \$Time\$ identifier + * is included, this provides the URL to a Representation Index. \n + * For more details on template-based segment URL construction refer to section 5.3.9.4.4 of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a string + */ + virtual const std::string& Getindex () const = 0; + + /** + * Returns a reference to a string that specifies the template to create the Initialization Segment. Neither \em \$Number\$ nor the \em \$Time\$ identifier + * shall be included. \n + * For more details on template-based segment URL construction refer to section 5.3.9.4.4 of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a string + */ + virtual const std::string& Getinitialization () const = 0; + + /** + * Returns a reference to a string that specifies the template to create the Bitstream Switching Segment. Neither \em \$Number\$ nor the \em \$Time\$ identifier shall be included.\n + * For more details on template-based segment URL construction refer to section 5.3.9.4.4 of ISO/IEC 23009-1, Part 1, 2012. + * @return a reference to a string + */ + virtual const std::string& GetbitstreamSwitching () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegment object that represents an initialization segment and can be downloaded. + * @param baseurls a vector of pointers to dash::mpd::IBaseUrl objects that represent the path to the Initialization Segment (template). + * @param representationID a string containing the representation ID that will replace the identifier \em \$RepresentationID\$ in the Initialization template. \n + * \b NOTE: If there is no identifier \em \$RepresentationID\$ in the template then this parameter will not be used and can be set to \"\". + * @param bandwidth an integer specifying the bandwidth that will replace the identifier \em \$Bandwidth\$ in the initialization template. + * This integer will be formated according to a possibly contained format tag in the \em \$Bandwidth\$ identifier. \n + * \b NOTE: If there is no identifier \em \$bandwidth\$ in the template then this parameter will not be used and can be set to 0. + * @return a pointer to a dash::mpd::ISegment object + */ + virtual ISegment* ToInitializationSegment (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth) const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegment object that represents a Bitstream Switching Segment and can be downloaded. + * @param baseurls a vector of pointers to dash::mpd::IBaseUrl objects that represent the path to the Bitstream Switching Segment (template). + * @param representationID a string containing the representation ID that will replace the identifier \em \$RepresentationID\$ in the Bitstream Switching template. \n + * \b NOTE: If there is no identifier \em \$RepresentationID\$ in the template then this parameter will not be used and can be set to \"\". + * @param bandwidth an integer specifying the bandwidth that will replace the identifier \em \$Bandwidth\$ in the Bitstream Switching template. + * This integer will be formated according to a possibly contained format tag in the \em \$Bandwidth\$ identifier.\n + * \b NOTE: If there is no identifier \em \$bandwidth\$ in the template then this parameter will not be used and can be set to 0. + * @return a pointer to a dash::mpd::ISegment object + */ + virtual ISegment* ToBitstreamSwitchingSegment (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth) const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegment object that represents a Media Segment and can be downloaded. + * @param baseurls a vector of pointers to dash::mpd::IBaseUrl objects that represent the path to the Media Segment (template). + * @param representationID a string containing the representation ID that will replace the identifier \em \$RepresentationID\$ in the Media template.\n + * \b NOTE: If there is no identifier \em \$RepresentationID\$ in the template then this parameter will not be used and can be set to \"\". + * @param bandwidth an integer specifying the bandwidth that will replace the identifier \em \$Bandwidth\$ in the Media template. + * This integer will be formated according to a possibly contained format tag in the \em \$Bandwidth\$ identifier. \n + * \b NOTE: If there is no identifier \em \$bandwidth\$ in the template then this parameter will not be used and can be set to 0. + * @param number an integer specifying the desired Segment number that will replace the identifier \em \$Number\$ in the Media template. + * This integer will be formated according to a possibly contained format tag in the \em \$Number\$ identifier. + * @return a pointer to a dash::mpd::ISegment object + */ + virtual ISegment* GetMediaSegmentFromNumber (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t number) const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegment object that represents a Index Segment and can be downloaded. + * @param baseurls a vector of pointers to dash::mpd::IBaseUrl objects that represent the path to the Index Segment (template). + * @param representationID a string containing the representation ID that will replace the identifier \em \$RepresentationID\$ in the Index template.\n + * \b NOTE: If there is no identifier \em \$RepresentationID\$ in the template then this parameter will not be used and can be set to \"\". + * @param bandwidth an integer specifying the bandwidth that will replace the identifier \em \$Bandwidth\$ in the Index template. + * This integer will be formated according to a possibly contained format tag in the \em \$Bandwidth\$ identifier.\n + * \b NOTE: If there is no identifier \em \$bandwidth\$ in the template then this parameter will not be used and can be set to 0. + * @param number an integer specifying the desired Segment number that will replace the identifier \em \$Number\$ in the Index template. + * This integer will be formated according to a possibly contained format tag in the \em \$Number\$ identifier. + * @return a pointer to a dash::mpd::ISegment object + */ + virtual ISegment* GetIndexSegmentFromNumber (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t number) const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegment object that represents a Media Segment and can be downloaded. + * @param baseurls a vector of pointers to dash::mpd::IBaseUrl objects that represent the path to the Media Segment (template). + * @param representationID a string containing the representation ID that will replace the identifier \em \$RepresentationID\$ in the Media template. + * \b NOTE: If there is no identifier \em \$RepresentationID\$ in the template then this parameter will not be used and can be set to \"\". + * @param bandwidth an integer specifying the bandwidth that will replace the identifier \em \$Bandwidth\$ in the Media template. + * This integer will be formated according to a possibly contained format tag in the \em \$Bandwidth\$ identifier.\n + * \b NOTE: If there is no identifier \em \$bandwidth\$ in the template then this parameter will not be used and can be set to 0. + * @param time an integer corresponding to the SegmentTimeline\@t attribute that will replace the identifier \em \$Time\$ in the Media template. + * This integer will be formated according to a possibly contained format tag in the \em \$Time\$ identifier. + * @return a pointer to a dash::mpd::ISegment object + */ + virtual ISegment* GetMediaSegmentFromTime (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t time) const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegment object that represents a Index Segment and can be downloaded. + * @param baseurls a vector of pointers to dash::mpd::IBaseUrl objects that represent the path to the Index Segment (template). + * @param representationID a string containing the representation ID that will replace the identifier \em \$RepresentationID\$ in the Index template.\n + * \b NOTE: If there is no identifier \em \$RepresentationID\$ in the template then this parameter will not be used and can be set to \"\". + * @param bandwidth an integer specifying the bandwidth that will replace the identifier \em \$Bandwidth\$ in the Index template. + * This integer will be formated according to a possibly contained format tag in the \em \$Bandwidth\$ identifier.\n + * \b NOTE: If there is no identifier \em \$bandwidth\$ in the template then this parameter will not be used and can be set to 0. + * @param time an integer corresponding to the SegmentTimeline\@t attribute that will replace the identifier \em \$Time\$ in the Index template. + * This integer will be formated according to a possibly contained format tag in the \em \$Time\$ identifier. + * @return a pointer to a dash::mpd::ISegment object + */ + virtual ISegment* GetIndexSegmentFromTime (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t time) const = 0; + }; + } +} + +#endif /* ISEGMENTTEMPLATE_H_ */ \ No newline at end of file diff --git a/libdash/include/ISegmentTimeline.h b/libdash/include/ISegmentTimeline.h new file mode 100644 index 00000000..809ee735 --- /dev/null +++ b/libdash/include/ISegmentTimeline.h @@ -0,0 +1,50 @@ +/** + * @class dash::mpd::ISegmentTimeline + * @brief This interface is needed for accessing the subelements of the SegmentTimeline element, as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.9.6 + * @details The SegmentTimeline element expresses the earliest presentation time and presentation duration (in units based on the \c \@timescale attribute) + * for each Segment in the Representation. The use is an alternative to providing the \c \@duration attribute and provides three additional features: + *
    + *
  • the specification of arbitrary Segment durations, + *
  • the specification of accurate Segment durations for one media stream where the duration expresses presentation duration of the Segment, and + *
  • the signalling of discontinuities of the Media Presentation timeline for which no Segment data are present in a specific Representation. + *
+ * For compactness the syntax of this element includes run-length compression to express a sequence of Segments having constant duration.\n\n + * The SegmentTimeline element shall contain a list of S elements each of which describes a sequence of contiguous segments of identical MPD duration. + * @see dash::mpd::ITimeline dash::mpd::IMultipleSegmentBase dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ISEGMENTTIMELINE_H_ +#define ISEGMENTTIMELINE_H_ + +#include "config.h" + +#include "ITimeline.h" +#include "IMPDElement.h" + +namespace dash +{ + namespace mpd + { + class ISegmentTimeline : public virtual IMPDElement + { + public: + virtual ~ISegmentTimeline(){} + + /** + * Returns a reference to a vector of pointers to dash::mpd::ITimeline objects, that correspond to the to S elements. + * @return a reference to vector of pointers to dash::mpd::ITimeline objects + */ + virtual std::vector& GetTimelines () const = 0; + }; + } +} + +#endif /* ISEGMENTTIMELINE_H_ */ \ No newline at end of file diff --git a/libdash/include/ISegmentURL.h b/libdash/include/ISegmentURL.h new file mode 100644 index 00000000..236dc923 --- /dev/null +++ b/libdash/include/ISegmentURL.h @@ -0,0 +1,94 @@ +/** + * @class dash::mpd::ISegmentURL + * @brief This interface is needed for accessing the attributes and elements of the SegmentURL element + * as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.9.3.2, table 14 + * @details Each Segment URL may contain the Media Segment URL and possibly a byte range. The Segment URL element may also contain an Index Segment. + * @see dash::mpd::ISegment dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ISEGMENTURL_H_ +#define ISEGMENTURL_H_ + +#include "config.h" + +#include "ISegment.h" +#include "IBaseUrl.h" +#include "IMPDElement.h" + +namespace dash +{ + namespace mpd + { + class ISegmentURL : public virtual IMPDElement + { + public: + virtual ~ISegmentURL(){} + + /** + * Returns a reference to a string that in combination with the \c \@mediaRange attribute specifies the HTTP-URL for the Media Segment.\n + * It shall be formated as an \c according to RFC 3986, Clause 4.3, with a fixed scheme of \"http\" or \"https\" or + * as a \c according to RFC 3986, Clause 4.2.\n + * If not present, then any BaseURL element is mapped to the \c \@media attribute and the range attribute shall be present. + * @return a reference to a string + */ + virtual const std::string& GetMediaURI () const = 0; + + /** + * Returns a reference to a string that specifies the byte range within the resource identified by the \c \@media corresponding to the Media Segment. + * The byte range shall be expressed and formatted as a \c byte-range-spec as defined in RFC 2616, Clause 14.35.1. + * It is restricted to a single expression identifying a contiguous range of bytes.\n + * If not present, the Media Segment is the entire resource referenced by the \c \@media attribute. + * @return a reference to a string + */ + virtual const std::string& GetMediaRange () const = 0; + + /** + * Returns a reference to a string that in combination with the \c \@indexRange attribute specifies the HTTP-URL for the Index Segment. + * It shall be formated as an \c according to RFC 3986, Clause 4.3, with a fixed scheme of \"http\" or \"https\" or + * as a \c according to RFC 3986, Clause 4.2. \n + * If not present and the \c \@indexRange not present either, then no Index Segment information is provided for this Media Segment.\n + * If not present and the \c \@indexRange present, then the \c \@media attribute is mapped to the \c \@index. If the \c \@media attribute is not present either, + * then any BaseURL element is mapped to the \c \@index attribute and the \c \@indexRange attribute shall be present. + * @return a reference to a string + */ + virtual const std::string& GetIndexURI () const = 0; + + /** + * Returns a reference to a string that specifies the byte range within the resource identified by the \c \@index corresponding to the Index Segment. + * If \c \@index is not present, it specifies the byte range of the Segment Index in Media Segment.\n + * The byte range shall be expressed and formatted as a \c byte-range-spec as defined in RFC 2616, Clause 14.35.1. It is restricted to a single + * expression identifying a contiguous range of bytes. \n + * If not present, the Index Segment is the entire resource referenced by the \c \@index attribute. + * @return a reference to a string + */ + virtual const std::string& GetIndexRange () const = 0; + + virtual uint64_t GetActualRate () = 0; + + /** + * Returns a pointer to a dash::mpd::ISegment object which represents a media segment that can be downloaded. + * @param baseurls a vector of pointers to dash::mpd::IBaseUrl objects that represent the path to the media segment + * @return a pointer to a dash::mpd::ISegment object + */ + virtual ISegment* ToMediaSegment (const std::vector& baseurls) const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegment object that represents an index segment and can be downloaded. + * @param baseurls a vector of pointers to dash::mpd::IBaseUrl objects that represent the path to the index segment + * @return a pointer to a dash::mpd::ISegment object + */ + virtual ISegment* ToIndexSegment (const std::vector& baseurls) const = 0; + + }; + } +} + +#endif /* ISEGMENTURL_H_ */ diff --git a/libdash/include/ISubRepresentation.h b/libdash/include/ISubRepresentation.h new file mode 100644 index 00000000..a93605c2 --- /dev/null +++ b/libdash/include/ISubRepresentation.h @@ -0,0 +1,70 @@ +/** + * @class dash::mpd::ISubRepresentation + * @brief This interface is needed for accessing the attributes and elements of the SubRepresentation element as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.6.2, table 8 + * @details Sub-Representations are embedded in regular Representations and are described by the SubRepresentation element. + * SubRepresentation elements are contained in a Representation element.\n + * The SubRepresentation element describes properties of one or several media content components that are embedded in the Representation. + * It may for example describe the exact properties of an embedded audio component (e.g., codec, sampling rate, etc.), + * an embedded sub-title (e.g., codec) or it may describe some embedded lower quality video layer (e.g. some lower frame rate, etc.). + * @see dash::mpd::IRepresentationBase + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ISUBREPRESENTATION_H_ +#define ISUBREPRESENTATION_H_ + +#include "config.h" + +#include "IRepresentationBase.h" + +namespace dash +{ + namespace mpd + { + class ISubRepresentation : public virtual IRepresentationBase + { + public: + virtual ~ISubRepresentation(){} + + /** + * Returns an integer that specifies the Sub-Representation level. If \c \@level attribute is present and for media formats used in this Part of ISO/IEC 23009, + * a Subsegment Index as defined in section 6.3.2.4 of ISO/IEC 23009-1, Part 1, 2012 shall be available for each Media Segment in the containing Representation. + * @return an unsigned integer + */ + virtual uint32_t GetLevel () const = 0; + + /** + * Returns a reference to a vector of unsigned integers that specifies the set of Sub-Representations within this Representation that this Sub-Representation depends on + * in the decoding and/or presentation process as a list of \c \@level values.\n + * If not present, the Sub-Representation can be decoded and presented independently of any other Representation. + * @return a reference to a vector of unsigned integers + */ + virtual const std::vector& GetDependencyLevel () const = 0; + + /** + * Returns an integer that is identical to the \c \@bandwidth definition in Representation, but applied to this Sub-Representation. + * This attribute shall be present if the \c \@level attribute is present. + * @return an unsigned integer + */ + virtual uint32_t GetBandWidth () const = 0; + + /** + * Returns a reference to a vector of strings that specifies the set of all media content components + * that are contained in this Sub-Representation as ContentComponent\@id values. \n + * If not present, the Sub-Representation is not assigned to a media content component. + * @return a reference to a vector of strings + */ + virtual const std::vector& GetContentComponent () const = 0; + + }; + } +} + +#endif /* ISUBREPRESENTATION_H_ */ \ No newline at end of file diff --git a/libdash/include/ISubset.h b/libdash/include/ISubset.h new file mode 100644 index 00000000..6d5c8566 --- /dev/null +++ b/libdash/include/ISubset.h @@ -0,0 +1,54 @@ +/** + * @class dash::mpd::ISubset + * @brief This interface is needed for accessing the attributes of the Subset element as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.8.2, table 10 + * @details Subsets are described by the Subset element contained in the Period element. \n + * Subsets provide a mechanism to restrict the combination of active Adaptation Sets where an active Adaptation Set is one + * for which the DASH client is presenting at least one of the contained Representations.\n + * A Subset defines a set of one or more Adaptation Sets. The presence of a Subset element within a Period element expresses + * the intention of the creator of the Media Presentation that a client should act as follows: + * At any time, the set of active Adaptation Sets shall be a subset of the Adaptation Sets of one of the specified Subsets. + * Any Adaptation Set not explicitly contained in any Subset element is implicitly contained in all specified Subsets.\n + * This implies that + *
    + *
  • Empty Subsets are not allowed. + *
  • No Subset should contain all the Adaptation Sets. + *
+ * Each Adaptation Set for which the value of the \c \@id is provided in the \c \@contains attribute is contained in this Subset. + * @see dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ISUBSET_H_ +#define ISUBSET_H_ + +#include "config.h" + +#include "IMPDElement.h" + +namespace dash +{ + namespace mpd + { + class ISubset : public virtual IMPDElement + { + public: + virtual ~ISubset(){} + + /** + * Returns a reference to a vector of unsigned integers specifying the Adaptation Sets contained in a Subset by providing + * the \c \@id values of the contained Adaptation Sets. + * @return a reference to a vector of unsigned integers + */ + virtual const std::vector& Contains () const = 0; + }; + } +} + +#endif /* ISUBSET_H_ */ \ No newline at end of file diff --git a/libdash/include/ITCPConnection.h b/libdash/include/ITCPConnection.h new file mode 100644 index 00000000..3d4f4867 --- /dev/null +++ b/libdash/include/ITCPConnection.h @@ -0,0 +1,40 @@ +/** + * @class dash::metrics::ITCPConnection + * @brief This interface is needed for accessing the attributes and the content of a TCP Connection + * as specified in ISO/IEC 23009-1, Part 1, 2012, annex D.4.2 + * @see + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ITCTPCONNECTION_H_ +#define ITCTPCONNECTION_H_ + +#include "config.h" + +namespace dash +{ + namespace metrics + { + class ITCPConnection + { + public: + virtual ~ITCPConnection (){} + + virtual uint32_t TCPId () const = 0; + virtual const std::string& DestinationAddress () const = 0; + virtual const std::string& ConnectionOpenedTime () const = 0; + virtual const std::string& ConnectionClosedTime () const = 0; + virtual uint64_t ConnectionTime () const = 0; + + }; + } +} + +#endif /* ITCTPCONNECTION_H_ */ diff --git a/libdash/include/IThroughputMeasurement.h b/libdash/include/IThroughputMeasurement.h new file mode 100644 index 00000000..c35cb9ae --- /dev/null +++ b/libdash/include/IThroughputMeasurement.h @@ -0,0 +1,38 @@ +/** + * @class dash::metrics::IThroughputMeasurement + * @brief This interface is needed for accessing the attributes and the content of a single Throughput Measurment entry + * as specified in ISO/IEC 23009-1, Part 1, 2012, annex D.4.3 (part of the HTTP Request/Response Transaction) + * @see dash::metrics::IHTTPTransaction + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ITHROUGHPUTMEASUREMENT_H_ +#define ITHROUGHPUTMEASUREMENT_H_ + +#include "config.h" + +namespace dash +{ + namespace metrics + { + class IThroughputMeasurement + { + public: + virtual ~IThroughputMeasurement (){} + + virtual const std::string& StartOfPeriod () const = 0; + virtual uint64_t DurationOfPeriod () const = 0; + virtual const std::vector& ReceivedBytesPerTrace () const = 0; + + }; + } +} + +#endif /* ITHROUGHPUTMEASUREMENT_H_ */ diff --git a/libdash/include/ITimeline.h b/libdash/include/ITimeline.h new file mode 100644 index 00000000..e1f8e953 --- /dev/null +++ b/libdash/include/ITimeline.h @@ -0,0 +1,66 @@ +/** + * @class dash::mpd::ITimeline + * @brief This interface is needed for accessing the attributes of S elements, as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.9.6.1 + * @details dash::mpd::ITimeline objects correspond to S elements of the SegmentTimeline \n \n + * The S element contains a mandatory \c \@d attribute specifying the MPD duration, + * an optional \c \@r repeat count attribute specifying the number of contiguous Segments with identical MPD duration minus one + * and an optional \c \@t time attribute specifying the MPD start time of the first Segment in the series. + * @see dash::mpd::ISegmentTimeline dash::mpd::IMultipleSegmentBase dash::mpd::IMPDElement + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef ITIMELINE_H_ +#define ITIMELINE_H_ + +#include "config.h" + +#include "IMPDElement.h" + +namespace dash +{ + namespace mpd + { + class ITimeline : public virtual IMPDElement + { + public: + virtual ~ITimeline(){} + + /** + * Returns an integer that specifies the MPD start time, in \c \@timescale units, the first Segment in the series starts relative to the beginning of the Period.\n + * The value of this attribute must be equal to or greater than the sum of the previous S element earliest presentation time and + * the sum of the contiguous Segment durations. \n + * If the value of the attribute is greater than what is expressed by the previous S element, it expresses discontinuities in the timeline.\n + * If not present then the value shall be assumed to be zero for the first S element and for the subsequent S elements, + * the value shall be assumed to be the sum of the previous S element's earliest presentation time and contiguous duration + * (i.e. previous S\@t + \c \@d * (\c \@r + 1)).\n\n + * \em StartTime corresponds to the \c \@t attribute. + * @return an unsigned integer + */ + virtual uint32_t GetStartTime () const = 0; + + /** + * Returns the integer that specifies the Segment duration, in units of the value of the \c \@timescale. \n\n + * \em Duration corresponds to the \c \@d attribute. + * @return an unsigned integer + */ + virtual uint32_t GetDuration () const = 0; + + /** + * Returns an integer that specifies the repeat count of the number of following contiguous Segments with the same duration expressed by the value of \c \@d. + * This value is zero-based (e.g. a value of three means four Segments in the contiguous series). \n\n + * \em RepeatCount corresponds to the \c \@r attribute. + * @return an unsigned integer + */ + virtual uint32_t GetRepeatCount () const = 0; + }; + } +} + +#endif /* ITIMELINE_H_ */ \ No newline at end of file diff --git a/libdash/include/IURLType.h b/libdash/include/IURLType.h new file mode 100644 index 00000000..67f893b5 --- /dev/null +++ b/libdash/include/IURLType.h @@ -0,0 +1,61 @@ +/** + * @class dash::mpd::IURLType + * @brief This interface is needed for accessing the attributes of URLType elements, as specified in ISO/IEC 23009-1, Part 1, 2012, section 5.3.9.2.2, table 13 + * @details This object defines an HTTP-URL\n + * @see dash::mpd::IBaseUrl dash::mpd::IMPDElement dash::mpd::ISegment + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef IURLTYPE_H_ +#define IURLTYPE_H_ + +#include "config.h" + +#include "IMPDElement.h" +#include "ISegment.h" +#include "IBaseUrl.h" + +namespace dash +{ + namespace mpd + { + class IURLType : public virtual IMPDElement + { + public: + virtual ~IURLType(){} + + /** + * Returns a reference to a string that pecifies the source URL part and shall be formated either as an \c according to RFC 3986, Clause 4.3, + * with a fixed scheme of \"http\" or \"https\" or as a \c according to RFC 3986, Clause 4.2.\n + * If not present, then any BaseURL element is mapped to the \c \@sourceURL attribute and the range attribute shall be present. + * @return a reference to a string + */ + virtual const std::string& GetSourceURL () const = 0; + + /** + * Returns a reference to a string that specifies the byte range restricting the above HTTP-URL.\n + * The byte range shall be expressed and formatted as a byte-range-spec as defined in RFC 2616, Clause 14.35.1. It is restricted to a single expression identifying a + * contiguous range of bytes.\n + * If not present, the element refers to the entire resource referenced in the \c \@sourceURL attribute. + * @return a reference to a string + */ + virtual const std::string& GetRange () const = 0; + + /** + * Returns a pointer to a dash::mpd::ISegment object, that can be downloaded + * @param baseurls a reference to a vector of pointers to dash::mpd::IBaseUrl objects representing the path to \c \@sourceURL + * @return a pointer to a dash::mpd::ISegment object + */ + virtual ISegment* ToSegment (const std::vector& baseurls) const = 0; + }; + } +} + +#endif /* IURLTYPE_H_ */ \ No newline at end of file diff --git a/libdash/include/config.h b/libdash/include/config.h new file mode 100644 index 00000000..951c7e12 --- /dev/null +++ b/libdash/include/config.h @@ -0,0 +1,37 @@ +/** + * config.h + * + * @brief Standard includes that are used by the library + * @details ... + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef CONFIG_H_ +#define CONFIG_H_ + +/******************************** + * Pragmas + ********************************/ +#pragma warning( disable : 4250 ) // virtual inheritance +/******************************** + * Standard includes + ********************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* CONFIG_H_ */ diff --git a/libdash/include/libdash.h b/libdash/include/libdash.h new file mode 100644 index 00000000..01b80bbd --- /dev/null +++ b/libdash/include/libdash.h @@ -0,0 +1,31 @@ +/** + * libdash.h + * + * @brief The main interface to the libary that should be used to create a IDASHManager + * @details ... + * + * @author bitmovin Softwareentwicklung OG \n + * Email: libdash-dev@vicky.bitmovin.net + * @version 2.1 + * @date 2013 + * @copyright bitmovin Softwareentwicklung OG, All Rights Reserved \n\n + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + */ + +#ifndef LIBDASH_H_ +#define LIBDASH_H_ + +#include "config.h" + +#if defined _WIN32 || defined _WIN64 +#else +#define __declspec(dllexport) +#define __cdecl +#endif + +#include "IDASHManager.h" + +__declspec(dllexport) dash::IDASHManager* __cdecl CreateDashManager(); + +#endif /* LIBDASH_H_ */ diff --git a/libdash/license.txt b/libdash/license.txt new file mode 100644 index 00000000..fee56851 --- /dev/null +++ b/libdash/license.txt @@ -0,0 +1,13 @@ +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \ No newline at end of file diff --git a/libdash/mainpage.dox b/libdash/mainpage.dox new file mode 100644 index 00000000..1a718a6e --- /dev/null +++ b/libdash/mainpage.dox @@ -0,0 +1,93 @@ +/** +\mainpage libdash 2.1 + +libdash is a library that provides an object orient (OO) interface to the MPEG-DASH standard. + +## Features + +- Cross platform build system based on cmake that includes Windows, Linux and Mac. +- Open source available and licensed under the LGPL. +- Implements the full MPEG-DASH standard according to ISO/IEC 23009-1, Information Technology Dynamic Adaptive Streaming over HTTP (DASH) Part 1: Media Presentation Description and Segment Formats +- Handles the download and xml parsing of the MPD. Based on that it provides an OO based interface to the MPD. +- Media elements, e.g., SegmentURL, SegmentTemplate, etc., are downloadable in that OO based structure and can be downloaded through libdash, which internally uses libcurl. +- Therefore basically all protocols that libcurl supports, e.g., HTTP, FTP, etc. are supported by libdash. +- However it also provides a configurable download interface, which enables the use of external connections that can be implemented by the user of the library for the download of media segments. +- The use of such external connections will be shown in the libdash_networkpart_test project which is part of libdash solution and also part of the cross platform cmake system and therefore usable on Windows, Linux and Mac. +- The project contains a sample multimedia player that is based on ffmpeg which uses libdash for the playback of one of our dataset MPDs. +- The development is based on Windows, therefore the code contains a VS10 solution with additional tests and the sample multimedia player. + +## Architecture +

The general architecture of MPEG-DASH is depicted in the figure below where the orange parts are standardized, i.e., the MPD and segment formats. The delivery of the MPD, the control heuristics and the media player itself, are depicted in blue in the figure. These parts are not standardized and allow the differentiation of industry solutions due to the performance or different features that can be integrated at that level. libdash is also depicted in blue and encapsulates the MPD parsing and HTTP part, which will be handled by the library. Therefore the library provides interfaces for the DASH Streaming Control and the Media Player to access MPDs and downloadable media segments. The download order of such media segments will not be handled by the library this is left to the DASH Streaming Control, which is an own component in this architecture but it could also be included in the Media Player. +

+

+In a typical deployment, a DASH server provides segments in several bitrates and resolutions. The client initially receives the MPD through libdash which provides a convenient object orient interface to that MPD. The MPD contains the temporal relationships for the various qualities and segments. Based on that information the client can download individual media segments through libdash at any point in time. Therefore varying bandwidth conditions can be handled by switching to the corresponding quality level at segment boundaries in order to provide a smooth streaming experience. This adaptation is not part of libdash and the MPEG-DASH standard and will be left to the application which is using libdash. +

+ + +![libdash architecture](http://www.bitmovin.net/wp-content/uploads/2013/01/libdash_arch-1024x483.png "libdash arch") + +## Mailinglist + +We offer a public accessible mailing list for discussions, questions, announcements, bug-reports, etc. around libdash. Everybody is invited to join, you can find the registration at: + +[libdash-dev] (http://vicky.bitmovin.net/mailman/listinfo/libdash-dev) + +There are a lot of things to do! So everybody is invited to contribute, to get involved in and exited about DASH! + +## Sources and Binaries + +You can find the latest sources and binaries in our [download section] (http://www.bitmovin.net/?page_id=851) and on github. + +## How to use + +### Windows +1. Download the tarball or clone the repository from gitlab (git://github.com/bitmovin/libdash.git) +2. Open the libdash.sln with Visual Studio 2010 +3. Build the solution +4. After that all files will be provided in the bin folder +5. You can test the library with the sampleplayer.exe. This application simply downloads the lowest representation of one of our dataset MPDs. + +### Ubuntu 12.04 +1. sudo apt-get install git-core build-essential cmake libxml2-dev libcurl4-openssl-dev +2. git clone git://github.com/bitmovin/libdash.git +3. cd libdash/libdash +4. mkdir build +5. cd build +6. cmake ../ +7. make +8. cd bin +9. The library and a simple test of the network part of the library should be available now. You can test the network part of the library with +10. ./libdash_networpart_test + +## License + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +## Professional Services + +In addition to the public available open source resources and the mailing list support, we provide professional development and integration services, consulting, high-quality streaming componentes/logics, etc. based on your individual needs. Feel free to contact us via sales@bitmovin.net so we can discuss your requirements and provide you an offer. + +## Acknowledgements + +We specially want to thank our passionate developers at [bitmovin](http://www.bitmovin.net/) as well as the researchers at the [Institute of Information Technology](http://www-itec.aau.at/dash/) (ITEC) from the Alpen Adria Universitaet Klagenfurt (AAU)! + +Furthermore we want to thank the [Netidee](http://www.netidee.at) initiative from the [Internet Foundation Austria](http://www.nic.at/ipa) for partially funding the open source development of libdash. + +## Citation of libdash +We kindly ask you to refer the following paper in any publication mentioning libdash: + +Christopher Mueller and Christian Timmerer. 2011. A VLC media player plugin enabling dynamic adaptive streaming over HTTP. In Proceedings of the 19th ACM international conference on Multimedia (MM '11). ACM, New York, NY, USA, 723-726. DOI=10.1145/2072298.2072429 [[pdf]](http://doi.acm.org/10.1145/2072298.2072429) + +*/ \ No newline at end of file diff --git a/libdash/source/defaults.mk b/libdash/source/defaults.mk new file mode 100644 index 00000000..d446c620 --- /dev/null +++ b/libdash/source/defaults.mk @@ -0,0 +1,7 @@ +LIBNAME=libdash +CC=g++ +AR=ar +LD=g++ +ARFLAGS=-cr +CFLAGS=-fPIC -c -Wall #-DDEBUG +LDFLAGS=-shared -Wl,-soname,$(LIBNAME).so diff --git a/libdash/source/dllmain.cpp b/libdash/source/dllmain.cpp new file mode 100644 index 00000000..b624228b --- /dev/null +++ b/libdash/source/dllmain.cpp @@ -0,0 +1,33 @@ +/* + * dllmain.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#if defined _WIN32 || defined _WIN64 + +#include "targetver.h" +#define WIN32_LEAN_AND_MEAN +#include + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +#endif diff --git a/libdash/source/helpers/Block.h b/libdash/source/helpers/Block.h new file mode 100644 index 00000000..c71462e8 --- /dev/null +++ b/libdash/source/helpers/Block.h @@ -0,0 +1,59 @@ +/* + * Block.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef __BLOCK_H__ +#define __BLOCK_H__ + +#include "config.h" + +namespace dash +{ + namespace helpers + { + struct block_t + { + uint8_t *data; + size_t len; + float millisec; + size_t offset; + }; + + static inline block_t* AllocBlock (size_t len) + { + block_t *block = (block_t *)malloc(sizeof(block_t)); + block->data = new uint8_t[len]; + block->len = len; + block->millisec = 0; + block->offset = 0; + return block; + } + static inline void DeleteBlock (block_t *block) + { + if(block) + { + delete [] block->data; + free(block); + block = NULL; + } + } + static inline block_t* DuplicateBlock (block_t *block) + { + block_t *ret = AllocBlock(block->len); + ret->offset = block->offset; + + memcpy(ret->data, block->data, ret->len); + + return block; + } + } +} + +#endif diff --git a/libdash/source/helpers/BlockStream.cpp b/libdash/source/helpers/BlockStream.cpp new file mode 100644 index 00000000..fd34f88e --- /dev/null +++ b/libdash/source/helpers/BlockStream.cpp @@ -0,0 +1,290 @@ +/* + * BlockStream.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "BlockStream.h" +#include + +using namespace dash::helpers; + +BlockStream::BlockStream () : + length (0) +{ +} +BlockStream::~BlockStream () +{ + this->Clear(); +} + +void BlockStream::PopAndDeleteFront () +{ + if(this->blockqueue.empty()) + return; + + this->length -= this->blockqueue.front()->len; + DeleteBlock(this->blockqueue.front()); + this->blockqueue.pop_front(); +} +void BlockStream::PushBack (block_t *block) +{ + this->length += block->len; + this->blockqueue.push_back(block); +} +void BlockStream::PushFront (block_t *block) +{ + this->length += block->len; + this->blockqueue.push_front(block); +} +const block_t* BlockStream::GetBytes (uint32_t len) +{ + /* Performance Intensive */ + if(this->length < len) + return NULL; + + block_t *block = AllocBlock(len); + this->BlockQueueGetBytes(block->data, block->len); + + this->length -= len; + + return block; +} +size_t BlockStream::GetBytes (uint8_t *data, size_t len) +{ + /* Performance Intensive */ + if(len > this->length) + len = (size_t) this->length; + + this->BlockQueueGetBytes(data, len); + + this->length -= len; + + return len; +} +size_t BlockStream::PeekBytes (uint8_t *data, size_t len) +{ + /* Performance Intensive */ + if(len > this->length) + len = (size_t) this->length; + + this->BlockQueuePeekBytes(data, len, 0); + + return len; +} +size_t BlockStream::PeekBytes (uint8_t *data, size_t len, size_t offset) +{ + /* Performance Intensive */ + if(len > this->length) + len = (size_t) this->length; + + if (offset + len > this->length) + len = (size_t) (this->length - offset); + + this->BlockQueuePeekBytes(data, len, offset); + + return len; +} +uint64_t BlockStream::Length () const +{ + return this->length; +} +const block_t* BlockStream::GetFront () +{ + if(this->blockqueue.empty()) + return NULL; + + const block_t* ret = this->blockqueue.front(); + this->length -= ret->len; + this->blockqueue.pop_front(); + + return ret; +} +const block_t* BlockStream::Front () const +{ + if(this->blockqueue.empty()) + return NULL; + + return this->blockqueue.front(); +} +bool BlockStream::BlockQueueGetBytes (uint8_t *data, uint32_t len) +{ + uint32_t pos = 0; + + block_t *block = NULL; + + while(pos < len) + { + block = this->blockqueue.front(); + if((len - pos) < (block->len)) + { + memcpy(data + pos, block->data, len - pos); + + this->blockqueue.pop_front(); + + block_t* newfront = AllocBlock(block->len - (len - pos)); + memcpy(newfront->data, block->data + (len - pos), newfront->len); + + DeleteBlock(block); + this->blockqueue.push_front(newfront); + + return true; + } + else + { + memcpy(data + pos, block->data, block->len); + pos += block->len; + + DeleteBlock(block); + this->blockqueue.pop_front(); + } + } + + return false; +} +bool BlockStream::BlockQueuePeekBytes (uint8_t *data, uint32_t len, size_t offset) +{ + uint32_t pos = 0; + int cnt = 0; + + const block_t *block = NULL; + + while(pos < len) + { + block = this->blockqueue.at(cnt); + if((offset + len - pos) < (block->len)) + { + memcpy(data + pos, block->data + offset, len - pos - offset); + return true; + } + else + { + memcpy(data + pos, block->data + offset, block->len - offset); + pos += block->len; + } + + cnt++; + } + + return false; +} +uint8_t BlockStream::ByteAt (uint64_t position) const +{ + if(position > this->length) + return -1; + + uint64_t pos = 0; + + for(size_t i = 0; i < this->blockqueue.size(); i++) + { + const block_t *block = this->blockqueue.at(i); + + if(pos + block->len > position) + return block->data[position - pos]; + else + pos += block->len; + } + + return -1; +} +const block_t* BlockStream::ToBlock () +{ + if(this->length > std::numeric_limits::max()) + return NULL; + + return BlockStream::GetBytes((size_t)this->length); +} +void BlockStream::Clear () +{ + while(!this->blockqueue.empty()) + { + DeleteBlock(this->blockqueue.front()); + this->blockqueue.pop_front(); + } + + this->length = 0; +} +void BlockStream::EraseFront (uint64_t len) +{ + if(len > this->length) + len = this->length; + + uint64_t actLen = 0; + + while(actLen < len) + { + if(this->blockqueue.size() == 0) + return; + + block_t *front = this->blockqueue.front(); + + if((actLen + front->len) <= len) + { + this->length -= front->len; + actLen += front->len; + + DeleteBlock(front); + this->blockqueue.pop_front(); + } + else + { + uint32_t diff = (uint32_t) (len - actLen); + this->length -= diff; + actLen += diff; + block_t* newfront = AllocBlock(front->len - diff); + + memcpy(newfront->data, front->data + diff, newfront->len); + + DeleteBlock(front); + this->blockqueue.pop_front(); + this->blockqueue.push_front(newfront); + } + } +} +BlockStream* BlockStream::GetBlocks (uint64_t len) +{ + if(len > this->length) + return NULL; + + BlockStream *blocks = new BlockStream(); + + uint64_t actLen = 0; + + while(actLen < len) + { + block_t *front = this->blockqueue.front(); + + if((actLen + front->len) <= len) + { + this->length -= front->len; + actLen += front->len; + + blocks->PushBack(front); + this->blockqueue.pop_front(); + } + else + { + uint32_t diff = (uint32_t) (len - actLen); + this->length -= diff; + actLen += diff; + block_t *block = AllocBlock(diff); + block_t* newfront = AllocBlock(front->len - diff); + + memcpy(block->data, front->data, diff); + blocks->PushBack(block); + + memcpy(newfront->data, front->data+ diff, newfront->len); + + DeleteBlock(front); + this->blockqueue.pop_front(); + this->blockqueue.push_front(newfront); + } + } + + return blocks; +} diff --git a/libdash/source/helpers/BlockStream.h b/libdash/source/helpers/BlockStream.h new file mode 100644 index 00000000..4b8b3fd5 --- /dev/null +++ b/libdash/source/helpers/BlockStream.h @@ -0,0 +1,55 @@ +/* + * BlockStream.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef __BLOCKSTREAM_H__ +#define __BLOCKSTREAM_H__ + +#include "config.h" + +#include "Block.h" + +namespace dash +{ + namespace helpers + { + class BlockStream + { + public: + BlockStream (); + virtual ~BlockStream (); + + virtual void PushBack (block_t *block); + virtual void PushFront (block_t *block); + virtual const block_t* GetBytes (uint32_t len); + virtual size_t GetBytes (uint8_t *data, size_t len); + virtual size_t PeekBytes (uint8_t *data, size_t len); + virtual size_t PeekBytes (uint8_t *data, size_t len, size_t offset); + virtual const block_t* GetFront (); + virtual const block_t* Front () const; + virtual uint64_t Length () const; + virtual uint8_t ByteAt (uint64_t position) const; + virtual const block_t* ToBlock (); + virtual void Clear (); + virtual void EraseFront (uint64_t len); + virtual BlockStream* GetBlocks (uint64_t len); + virtual void PopAndDeleteFront (); + + protected: + uint64_t length; + std::deque blockqueue; + + virtual bool BlockQueueGetBytes (uint8_t *data, uint32_t len); + virtual bool BlockQueuePeekBytes (uint8_t *data, uint32_t len, size_t offset); + }; + } +} + +#endif // __BLOCKSTREAM_H__ \ No newline at end of file diff --git a/libdash/source/helpers/Path.cpp b/libdash/source/helpers/Path.cpp new file mode 100644 index 00000000..301d9fa0 --- /dev/null +++ b/libdash/source/helpers/Path.cpp @@ -0,0 +1,118 @@ +/* + * Path.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "Path.h" + +using namespace dash::helpers; + +std::string Path::CombinePaths (const std::string &path1, const std::string &path2) +{ + if(path1 == "") + return path2; + if(path2 == "") + return path1; + + char path1Last = path1.at(path1.size() - 1); + char path2First = path2.at(0); + + if(path1Last == '/' && path2First == '/') + return path1 + path2.substr(1, path2.size()); + + if(path1Last != '/' && path2First != '/') + return path1 + "/" + path2; + + return path1 + path2; +} +std::string Path::GetDirectoryPath (const std::string &path) +{ + int pos = path.find_last_of('/'); + + return path.substr(0, pos); +} +std::vector Path::Split (const std::string &s, char delim) +{ + std::stringstream ss(s); + std::string item; + std::vector ret; + + while(std::getline(ss, item, delim)) + ret.push_back(item); + + return ret; +} +bool Path::GetHostPortAndPath (const std::string &url, std::string &host, size_t &port, std::string& path) +{ + std::string hostPort = ""; + size_t found = 0; + size_t pathBegin = 0; + + if (url.substr(0,7) == "http://" || url.substr(0,8) == "https://") + { + found = url.find("//"); + pathBegin = url.find('/', found+2); + path = url.substr(pathBegin, std::string::npos); + + hostPort = url.substr(found+2, pathBegin - (found+2)); + found = hostPort.find(':'); + if (found != std::string::npos) + { + port = strtoul(hostPort.substr(found+1, std::string::npos).c_str(), NULL, 10); + } + host = hostPort.substr(0, found); + return (host.size() > 0) && (path.size() > 0); + } + else if(url.substr(0,5) == "ndn:/") + { + found = url.find("/"); + pathBegin = url.find('/', found+1); + path = url.substr(pathBegin, std::string::npos); + + hostPort = url.substr(found+1, pathBegin - (found+1)); + found = hostPort.find(':'); + if (found != std::string::npos) + { + port = strtoul(hostPort.substr(found+1, std::string::npos).c_str(), NULL, 10); + } + host = hostPort.substr(0, found); + return (host.size() > 0) && (path.size() > 0); + } + else if(url.substr(0,6) == "ccnx:/") + { + found = url.find("/"); + pathBegin = url.find('/', found+1); + path = url.substr(pathBegin, std::string::npos); + + hostPort = url.substr(found+1, pathBegin - (found+1)); + found = hostPort.find(':'); + if (found != std::string::npos) + { + port = strtoul(hostPort.substr(found+1, std::string::npos).c_str(), NULL, 10); + } + host = hostPort.substr(0, found); + return (host.size() > 0) && (path.size() > 0); + } + + return false; +} +bool Path::GetStartAndEndBytes (const std::string &byteRange, size_t &startByte, size_t &endByte) +{ + size_t found = 0; + + found = byteRange.find('-'); + if (found != std::string::npos && found < byteRange.size()-1 ) + { + startByte = strtoul(byteRange.substr(0, found).c_str(), NULL, 10); + endByte = strtoul(byteRange.substr(found+1, std::string::npos).c_str(), NULL, 10); + return (startByte <= endByte); + } + + return false; +} diff --git a/libdash/source/helpers/Path.h b/libdash/source/helpers/Path.h new file mode 100644 index 00000000..1c791baa --- /dev/null +++ b/libdash/source/helpers/Path.h @@ -0,0 +1,33 @@ +/* + * Path.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef PATH_H_ +#define PATH_H_ + +#include "config.h" + +namespace dash +{ + namespace helpers + { + class Path + { + public: + static std::string CombinePaths (const std::string &path1, const std::string &path2); + static std::string GetDirectoryPath (const std::string &path); + static std::vector Split (const std::string &s, char delim); + static bool GetHostPortAndPath (const std::string &url, std::string &host, size_t &port, std::string& path); + static bool GetStartAndEndBytes (const std::string &byteRange, size_t &startByte, size_t &endByte); + }; + } +} + +#endif /* PATH_H_ */ \ No newline at end of file diff --git a/libdash/source/helpers/String.cpp b/libdash/source/helpers/String.cpp new file mode 100644 index 00000000..53da5592 --- /dev/null +++ b/libdash/source/helpers/String.cpp @@ -0,0 +1,53 @@ +/* + * String.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "String.h" + +using namespace dash::helpers; + +void String::Split (const std::string &s, char delim, std::vector& vector) +{ + std::stringstream ss(s); + std::string item; + + while(std::getline(ss, item, delim)) + vector.push_back(item); +} +void String::Split (const std::string &s, char delim, std::vector& vector) +{ + size_t lengthOfString = s.length(); + size_t pos = 0; + size_t i = 0; + uint32_t level = 0; + + while (pos != std::string::npos) + { + pos = s.find(delim, i); + + if (i < lengthOfString) + { + level = strtoul(s.substr(i, pos-i).c_str(), NULL, 10); + vector.push_back(level); + i = pos + 1; + } + } +} +bool String::ToBool (const std::string &s) +{ + if (s == "true" || s == "True" || s == "TRUE") + { + return true; + } + else + { + return false; + } +} \ No newline at end of file diff --git a/libdash/source/helpers/String.h b/libdash/source/helpers/String.h new file mode 100644 index 00000000..73ac6db8 --- /dev/null +++ b/libdash/source/helpers/String.h @@ -0,0 +1,31 @@ +/* + * String.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef STRING_H_ +#define STRING_H_ + +#include "config.h" + +namespace dash +{ + namespace helpers + { + class String + { + public: + static void Split (const std::string &s, char delim, std::vector& vector); + static void Split (const std::string &s, char delim, std::vector& vector); + static bool ToBool (const std::string &s); + }; + } +} + +#endif /* STRING_H_ */ \ No newline at end of file diff --git a/libdash/source/helpers/SyncedBlockStream.cpp b/libdash/source/helpers/SyncedBlockStream.cpp new file mode 100644 index 00000000..84fa63cf --- /dev/null +++ b/libdash/source/helpers/SyncedBlockStream.cpp @@ -0,0 +1,250 @@ +/* + * SyncedBlockStream.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "SyncedBlockStream.h" + +using namespace dash::helpers; + +SyncedBlockStream::SyncedBlockStream () : + eos (false) +{ + InitializeConditionVariable (&this->full); + InitializeCriticalSection (&this->monitorMutex); +} +SyncedBlockStream::~SyncedBlockStream () +{ + DeleteConditionVariable(&this->full); + DeleteCriticalSection(&this->monitorMutex); +} + +void SyncedBlockStream::PopAndDeleteFront () +{ + EnterCriticalSection(&this->monitorMutex); + + BlockStream::PopAndDeleteFront(); + + LeaveCriticalSection(&this->monitorMutex); +} +void SyncedBlockStream::PushBack (block_t *block) +{ + EnterCriticalSection(&this->monitorMutex); + + BlockStream::PushBack(block); + + WakeAllConditionVariable(&this->full); + LeaveCriticalSection(&this->monitorMutex); +} +void SyncedBlockStream::PushFront (block_t *block) +{ + EnterCriticalSection(&this->monitorMutex); + + BlockStream::PushFront(block); + + WakeAllConditionVariable(&this->full); + LeaveCriticalSection(&this->monitorMutex); +} +const block_t* SyncedBlockStream::GetBytes (uint32_t len) +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->length == 0 && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->length == 0) + { + LeaveCriticalSection(&this->monitorMutex); + return NULL; + } + + const block_t* block = BlockStream::GetBytes(len); + LeaveCriticalSection(&this->monitorMutex); + + return block; +} +size_t SyncedBlockStream::GetBytes (uint8_t *data, size_t len) +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->length == 0 && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->length == 0) + { + LeaveCriticalSection(&this->monitorMutex); + return 0; + } + + size_t ret = BlockStream::GetBytes(data, len); + LeaveCriticalSection(&this->monitorMutex); + + return ret; +} +size_t SyncedBlockStream::PeekBytes (uint8_t *data, size_t len) +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->length == 0 && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->length == 0) + { + LeaveCriticalSection(&this->monitorMutex); + return 0; + } + + size_t ret = BlockStream::PeekBytes(data, len); + LeaveCriticalSection(&this->monitorMutex); + + return ret; +} +size_t SyncedBlockStream::PeekBytes (uint8_t *data, size_t len, size_t offset) +{ + EnterCriticalSection(&this->monitorMutex); + + while((this->length == 0 || offset >= this->length) && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->length == 0 || offset >= this->length) + { + LeaveCriticalSection(&this->monitorMutex); + return 0; + } + + size_t ret = BlockStream::PeekBytes(data, len, offset); + LeaveCriticalSection(&this->monitorMutex); + + return ret; +} +uint64_t SyncedBlockStream::Length () const +{ + EnterCriticalSection(&this->monitorMutex); + + uint64_t len = BlockStream::Length(); + + LeaveCriticalSection(&this->monitorMutex); + + return len; +} +const block_t* SyncedBlockStream::GetFront () +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->length == 0 && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->length == 0) + { + LeaveCriticalSection(&this->monitorMutex); + return NULL; + } + + const block_t* block = BlockStream::GetFront(); + LeaveCriticalSection(&this->monitorMutex); + + return block; +} +const block_t* SyncedBlockStream::Front () const +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->length == 0 && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->length == 0) + { + LeaveCriticalSection(&this->monitorMutex); + return NULL; + } + + const block_t* block = BlockStream::Front(); + LeaveCriticalSection(&this->monitorMutex); + + return block; +} +uint8_t SyncedBlockStream::ByteAt (uint64_t position) const +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->length < position && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->length < position) + { + LeaveCriticalSection(&this->monitorMutex); + return 0; + } + + uint8_t ret = BlockStream::ByteAt(position); + LeaveCriticalSection(&this->monitorMutex); + + return ret; +} +const block_t* SyncedBlockStream::ToBlock () +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->length == 0 && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->length == 0) + { + LeaveCriticalSection(&this->monitorMutex); + return NULL; + } + + const block_t* block = BlockStream::ToBlock(); + LeaveCriticalSection(&this->monitorMutex); + + return block; +} +void SyncedBlockStream::Clear () +{ + EnterCriticalSection(&this->monitorMutex); + + BlockStream::Clear(); + + LeaveCriticalSection(&this->monitorMutex); +} +void SyncedBlockStream::EraseFront (uint64_t len) +{ + EnterCriticalSection(&this->monitorMutex); + + BlockStream::EraseFront(len); + + LeaveCriticalSection(&this->monitorMutex); +} +BlockStream* SyncedBlockStream::GetBlocks (uint64_t len) +{ + EnterCriticalSection(&this->monitorMutex); + + while(this->length == 0 && !this->eos) + SleepConditionVariableCS(&this->full, &this->monitorMutex, INFINITE); + + if(this->length == 0) + { + LeaveCriticalSection(&this->monitorMutex); + return NULL; + } + + BlockStream *stream = BlockStream::GetBlocks(len); + LeaveCriticalSection(&this->monitorMutex); + + return stream; +} +void SyncedBlockStream::SetEOS (bool value) +{ + EnterCriticalSection(&this->monitorMutex); + + this->eos = value; + + WakeAllConditionVariable(&this->full); + LeaveCriticalSection(&this->monitorMutex); +} diff --git a/libdash/source/helpers/SyncedBlockStream.h b/libdash/source/helpers/SyncedBlockStream.h new file mode 100644 index 00000000..9e266c60 --- /dev/null +++ b/libdash/source/helpers/SyncedBlockStream.h @@ -0,0 +1,57 @@ +/* + * SyncedBlockStream.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef __SYNCEDBLOCKSTREAM_H__ +#define __SYNCEDBLOCKSTREAM_H__ + +#include "config.h" + +#include "BlockStream.h" +#include "../portable/MultiThreading.h" + +namespace dash +{ + namespace helpers + { + class SyncedBlockStream : public BlockStream + { + public: + SyncedBlockStream (); + virtual ~SyncedBlockStream (); + + virtual void PushBack (block_t *block); + virtual void PushFront (block_t *block); + virtual const block_t* GetBytes (uint32_t len); + virtual size_t GetBytes (uint8_t *data, size_t len); + virtual size_t PeekBytes (uint8_t *data, size_t len); + virtual size_t PeekBytes (uint8_t *data, size_t len, size_t offset); + virtual const block_t* GetFront (); + virtual const block_t* Front () const; + virtual uint64_t Length () const; + virtual uint8_t ByteAt (uint64_t position) const; + virtual const block_t* ToBlock (); + virtual void Clear (); + virtual void EraseFront (uint64_t len); + virtual BlockStream* GetBlocks (uint64_t len); + virtual void PopAndDeleteFront (); + virtual void SetEOS (bool value); + + private: + bool eos; + + mutable CRITICAL_SECTION monitorMutex; + mutable CONDITION_VARIABLE full; + + }; + } +} + +#endif // __SYNCEDBLOCKSTREAM_H__ \ No newline at end of file diff --git a/libdash/source/helpers/Time.cpp b/libdash/source/helpers/Time.cpp new file mode 100644 index 00000000..e6ca2b81 --- /dev/null +++ b/libdash/source/helpers/Time.cpp @@ -0,0 +1,33 @@ +/* + * Time.cpp + ***************************************************************************** + * Copyright (C) 2013, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "Time.h" + +using namespace dash::helpers; + +uint32_t Time::GetCurrentUTCTimeInSec () +{ + return mktime(Time::GetCurrentUTCTime()); +} +std::string Time::GetCurrentUTCTimeStr () +{ + char timeString[30]; + strftime(timeString, 30, "%Y-%m-%dT%H:%M:%SZ", Time::GetCurrentUTCTime()); + + return std::string(timeString); +} +struct tm* Time::GetCurrentUTCTime () +{ + time_t rawTime; + + time(&rawTime); + return gmtime(&rawTime); +} diff --git a/libdash/source/helpers/Time.h b/libdash/source/helpers/Time.h new file mode 100644 index 00000000..e7de0cd9 --- /dev/null +++ b/libdash/source/helpers/Time.h @@ -0,0 +1,35 @@ +/* + * Time.h + ***************************************************************************** + * Copyright (C) 2013, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef DASH_HELPERS_TIME_H_ +#define DASH_HELPERS_TIME_H_ + +#include +#include "config.h" + +namespace dash +{ + namespace helpers + { + class Time + { + public: + static uint32_t GetCurrentUTCTimeInSec (); + static std::string GetCurrentUTCTimeStr (); + + private: + static struct tm* GetCurrentUTCTime (); + + }; + } +} + +#endif /* DASH_HELPERS_TIME_H_ */ diff --git a/libdash/source/libdash.cpp b/libdash/source/libdash.cpp new file mode 100644 index 00000000..f26c8224 --- /dev/null +++ b/libdash/source/libdash.cpp @@ -0,0 +1,20 @@ +/* + * libdash.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "../include/libdash.h" +#include "manager/DASHManager.h" + +using namespace dash; + +__declspec(dllexport) IDASHManager* __cdecl CreateDashManager() +{ + return new DASHManager(); +} \ No newline at end of file diff --git a/libdash/source/manager/DASHManager.cpp b/libdash/source/manager/DASHManager.cpp new file mode 100644 index 00000000..7650111e --- /dev/null +++ b/libdash/source/manager/DASHManager.cpp @@ -0,0 +1,45 @@ +/* + * DASHManager.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "DASHManager.h" + +using namespace dash; +using namespace dash::xml; +using namespace dash::mpd; +using namespace dash::network; +using namespace dash::helpers; + +DASHManager::DASHManager () +{ +} +DASHManager::~DASHManager () +{ +} +IMPD* DASHManager::Open (char *path, std::string mUrl) +{ + DOMParser parser(path); + + uint32_t fetchTime = Time::GetCurrentUTCTimeInSec(); + + if (!parser.Parse(mUrl)) + return NULL; + + MPD* mpd = parser.GetRootNode()->ToMPD(); + + if (mpd) + mpd->SetFetchTime(fetchTime); + + return mpd; +} +void DASHManager::Delete () +{ + delete this; +} diff --git a/libdash/source/manager/DASHManager.h b/libdash/source/manager/DASHManager.h new file mode 100644 index 00000000..99ef188b --- /dev/null +++ b/libdash/source/manager/DASHManager.h @@ -0,0 +1,35 @@ +/* + * DASHManager.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef DASHMANAGER_H_ +#define DASHMANAGER_H_ + +#include "config.h" + +#include "../xml/Node.h" +#include "../xml/DOMParser.h" +#include "IDASHManager.h" +#include "../helpers/Time.h" + +namespace dash +{ + class DASHManager : public IDASHManager + { + public: + DASHManager (); + virtual ~DASHManager (); + + mpd::IMPD* Open (char *path, std::string mUrl = ""); + void Delete (); + }; +} + +#endif /* DASHMANAGER_H_ */ diff --git a/libdash/source/metrics/HTTPTransaction.cpp b/libdash/source/metrics/HTTPTransaction.cpp new file mode 100644 index 00000000..2767b514 --- /dev/null +++ b/libdash/source/metrics/HTTPTransaction.cpp @@ -0,0 +1,122 @@ +/* + * HTTPTransaction.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "HTTPTransaction.h" + +using namespace dash::metrics; + +HTTPTransaction::HTTPTransaction () : + tcpId (0), + type (dash::metrics::Other), + responseCode (0), + interval (0), + url (""), + actualUrl (""), + range (""), + tRequest (""), + tResponse (""), + httpHeader ("") +{ +} +HTTPTransaction::~HTTPTransaction() +{ + for (size_t i = 0; i < trace.size(); i++) + delete trace.at(i); +} + +uint32_t HTTPTransaction::TCPId () const +{ + return this->tcpId; +} +void HTTPTransaction::SetTCPId (uint32_t tcpId) +{ + this->tcpId = tcpId; +} +HTTPTransactionType HTTPTransaction::Type () const +{ + return this->type; +} +void HTTPTransaction::SetType (HTTPTransactionType type) +{ + this->type = type; +} +const std::string& HTTPTransaction::OriginalUrl () const +{ + return this->url; +} +void HTTPTransaction::SetOriginalUrl (const std::string& origUrl) +{ + this->url = origUrl; +} +const std::string& HTTPTransaction::ActualUrl () const +{ + return this->actualUrl; +} +void HTTPTransaction::SetActualUrl (const std::string& actUrl) +{ + this->actualUrl = actUrl; +} +const std::string& HTTPTransaction::Range () const +{ + return this->range; +} +void HTTPTransaction::SetRange (const std::string& range) +{ + this->range = range; +} +const std::string& HTTPTransaction::RequestSentTime () const +{ + return this->tRequest; +} +void HTTPTransaction::SetRequestSentTime (std::string tRequest) +{ + this->tRequest = tRequest; +} +const std::string& HTTPTransaction::ResponseReceivedTime () const +{ + return this->tResponse; +} +void HTTPTransaction::SetResponseReceivedTime (std::string tResponse) +{ + this->tResponse = tResponse; +} +uint16_t HTTPTransaction::ResponseCode () const +{ + return this->responseCode; +} +void HTTPTransaction::SetResponseCode (uint16_t respCode) +{ + this->responseCode = respCode; +} +uint64_t HTTPTransaction::Interval () const +{ + return this->interval; +} +void HTTPTransaction::SetInterval (uint64_t interval) +{ + this->interval = interval; +} +const std::vector& HTTPTransaction::ThroughputTrace () const +{ + return (std::vector &) this->trace; +} +void HTTPTransaction::AddThroughputMeasurement (ThroughputMeasurement *throuputEntry) +{ + this->trace.push_back(throuputEntry); +} +const std::string& HTTPTransaction::HTTPHeader () const +{ + return this->httpHeader; +} +void HTTPTransaction::AddHTTPHeaderLine (std::string headerLine) +{ + this->httpHeader.append(headerLine); +} diff --git a/libdash/source/metrics/HTTPTransaction.h b/libdash/source/metrics/HTTPTransaction.h new file mode 100644 index 00000000..77c69234 --- /dev/null +++ b/libdash/source/metrics/HTTPTransaction.h @@ -0,0 +1,68 @@ +/* + * HTTPTransaction.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef HTTPTRANSACTION_H_ +#define HTTPTRANSACTION_H_ + +#include "IHTTPTransaction.h" +#include "ThroughputMeasurement.h" + +namespace dash +{ + namespace metrics + { + class HTTPTransaction : public IHTTPTransaction + { + public: + HTTPTransaction (); + virtual ~HTTPTransaction (); + + uint32_t TCPId () const; + HTTPTransactionType Type () const; + const std::string& OriginalUrl () const; + const std::string& ActualUrl () const; + const std::string& Range () const; + const std::string& RequestSentTime () const; + const std::string& ResponseReceivedTime () const; + uint16_t ResponseCode () const; + uint64_t Interval () const; + const std::vector& ThroughputTrace () const; + const std::string& HTTPHeader () const; + + void SetTCPId (uint32_t tcpId); + void SetType (HTTPTransactionType type); + void SetOriginalUrl (const std::string& origUrl); + void SetActualUrl (const std::string& actUrl); + void SetRange (const std::string& range); + void SetRequestSentTime (std::string tRequest); + void SetResponseReceivedTime (std::string tResponse); + void SetResponseCode (uint16_t respCode); + void SetInterval (uint64_t interval); + void AddThroughputMeasurement (ThroughputMeasurement *throuputEntry); + void AddHTTPHeaderLine (std::string headerLine); + + private: + uint32_t tcpId; + HTTPTransactionType type; + std::string url; + std::string actualUrl; + std::string range; + std::string tRequest; + std::string tResponse; + uint16_t responseCode; + uint64_t interval; + std::vector trace; + std::string httpHeader; + }; + } +} + +#endif /* HTTPTRANSACTION_H_ */ diff --git a/libdash/source/metrics/TCPConnection.cpp b/libdash/source/metrics/TCPConnection.cpp new file mode 100644 index 00000000..581b0539 --- /dev/null +++ b/libdash/source/metrics/TCPConnection.cpp @@ -0,0 +1,62 @@ +/* + * TCPConnection.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "TCPConnection.h" + +using namespace dash::metrics; + +TCPConnection::TCPConnection () +{ +} +TCPConnection::~TCPConnection() +{ +} + +uint32_t TCPConnection::TCPId () const +{ + return this->tcpId; +} +void TCPConnection::SetTCPId (uint32_t tcpId) +{ + this->tcpId = tcpId; +} +const std::string& TCPConnection::DestinationAddress () const +{ + return this->dest; +} +void TCPConnection::SetDestinationAddress (const std::string& destAddress) +{ + this->dest = destAddress; +} +const std::string& TCPConnection::ConnectionOpenedTime () const +{ + return this->tOpen; +} +void TCPConnection::SetConnectionOpenedTime (std::string tOpen) +{ + this->tOpen = tOpen; +} +const std::string& TCPConnection::ConnectionClosedTime () const +{ + return this->tClose; +} +void TCPConnection::SetConnectionClosedTime (std::string tClose) +{ + this->tClose = tClose; +} +uint64_t TCPConnection::ConnectionTime () const +{ + return this->tConnect; +} +void TCPConnection::SetConnectionTime (uint64_t tConnect) +{ + this->tConnect = tConnect; +} diff --git a/libdash/source/metrics/TCPConnection.h b/libdash/source/metrics/TCPConnection.h new file mode 100644 index 00000000..d4618d7f --- /dev/null +++ b/libdash/source/metrics/TCPConnection.h @@ -0,0 +1,49 @@ +/* + * TCPConnection.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef TCPCONNECTION_H_ +#define TCPCONNECTION_H_ + +#include "ITCPConnection.h" + +namespace dash +{ + namespace metrics + { + class TCPConnection : public ITCPConnection + { + public: + TCPConnection (); + virtual ~TCPConnection (); + + uint32_t TCPId () const; + const std::string& DestinationAddress () const; + const std::string& ConnectionOpenedTime () const; + const std::string& ConnectionClosedTime () const; + uint64_t ConnectionTime () const; + + void SetTCPId (uint32_t tcpId); + void SetDestinationAddress (const std::string& destAddress); + void SetConnectionOpenedTime (std::string tOpen); + void SetConnectionClosedTime (std::string tClose); + void SetConnectionTime (uint64_t tConnect); + + private: + uint32_t tcpId; + std::string dest; + std::string tOpen; + std::string tClose; + uint64_t tConnect; + }; + } +} + +#endif /* TCPCONNECTION_H_ */ diff --git a/libdash/source/metrics/ThroughputMeasurement.cpp b/libdash/source/metrics/ThroughputMeasurement.cpp new file mode 100644 index 00000000..8d2c485e --- /dev/null +++ b/libdash/source/metrics/ThroughputMeasurement.cpp @@ -0,0 +1,46 @@ +/* + * ThroughputMeasurement.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "ThroughputMeasurement.h" + +using namespace dash::metrics; + +ThroughputMeasurement::ThroughputMeasurement () +{ +} +ThroughputMeasurement::~ThroughputMeasurement() +{ +} + +const std::string& ThroughputMeasurement::StartOfPeriod () const +{ + return this->startOfPeriod; +} +void ThroughputMeasurement::SetStartOfPeriod (std::string start) +{ + this->startOfPeriod = start; +} +uint64_t ThroughputMeasurement::DurationOfPeriod () const +{ + return this->durationOfPeriod; +} +void ThroughputMeasurement::SetDurationOfPeriod (uint64_t duration) +{ + this->durationOfPeriod = duration; +} +const std::vector& ThroughputMeasurement::ReceivedBytesPerTrace () const +{ + return this->receivedBytesPerTrace; +} +void ThroughputMeasurement::AddReceivedBytes (uint32_t numberOfBytes) +{ + this->receivedBytesPerTrace.push_back(numberOfBytes); +} diff --git a/libdash/source/metrics/ThroughputMeasurement.h b/libdash/source/metrics/ThroughputMeasurement.h new file mode 100644 index 00000000..817e30d9 --- /dev/null +++ b/libdash/source/metrics/ThroughputMeasurement.h @@ -0,0 +1,43 @@ +/* + * ThroughputMeasurement.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef THROUGHPUTMEASUREMENT_H_ +#define THROUGHPUTMEASUREMENT_H_ + +#include "IThroughputMeasurement.h" + +namespace dash +{ + namespace metrics + { + class ThroughputMeasurement : public IThroughputMeasurement + { + public: + ThroughputMeasurement (); + virtual ~ThroughputMeasurement (); + + const std::string& StartOfPeriod () const; + uint64_t DurationOfPeriod () const; + const std::vector& ReceivedBytesPerTrace () const; + + void SetStartOfPeriod (std::string startOfPeriod); + void SetDurationOfPeriod (uint64_t duration); + void AddReceivedBytes (uint32_t numberOfBytes); + + private: + std::string startOfPeriod; + uint64_t durationOfPeriod; + std::vector receivedBytesPerTrace; + }; + } +} + +#endif /* THROUGHPUTMEASUREMENT_H_ */ diff --git a/libdash/source/mpd/AbstractMPDElement.cpp b/libdash/source/mpd/AbstractMPDElement.cpp new file mode 100644 index 00000000..146d1776 --- /dev/null +++ b/libdash/source/mpd/AbstractMPDElement.cpp @@ -0,0 +1,41 @@ +/* + * AbstractMPDElement.cpp + ***************************************************************************** + * Copyright (C) 2013, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AbstractMPDElement.h" + +using namespace dash::mpd; +using namespace dash::xml; + +AbstractMPDElement::AbstractMPDElement () +{ +} +AbstractMPDElement::~AbstractMPDElement () +{ + for(size_t i = 0; i < this->additionalSubNodes.size(); i++) + delete(this->additionalSubNodes.at(i)); +} + +const std::vector AbstractMPDElement::GetAdditionalSubNodes () const +{ + return this->additionalSubNodes; +} +const std::map AbstractMPDElement::GetRawAttributes () const +{ + return this->rawAttributes; +} +void AbstractMPDElement::AddAdditionalSubNode (INode *node) +{ + this->additionalSubNodes.push_back(node); +} +void AbstractMPDElement::AddRawAttributes (std::map attributes) +{ + this->rawAttributes = attributes; +} \ No newline at end of file diff --git a/libdash/source/mpd/AbstractMPDElement.h b/libdash/source/mpd/AbstractMPDElement.h new file mode 100644 index 00000000..b4235087 --- /dev/null +++ b/libdash/source/mpd/AbstractMPDElement.h @@ -0,0 +1,41 @@ +/* + * AbstractMPDElement.h + ***************************************************************************** + * Copyright (C) 2013, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef ABSTRACTMPDELEMENT_H_ +#define ABSTRACTMPDELEMENT_H_ + +#include "config.h" + +#include "IMPDElement.h" + +namespace dash +{ + namespace mpd + { + class AbstractMPDElement : public virtual IMPDElement + { + public: + AbstractMPDElement (); + virtual ~AbstractMPDElement (); + + virtual const std::vector GetAdditionalSubNodes () const; + virtual const std::map GetRawAttributes () const; + virtual void AddAdditionalSubNode (xml::INode * node); + virtual void AddRawAttributes (std::map attributes); + + private: + std::vector additionalSubNodes; + std::map rawAttributes; + }; + } +} + +#endif /* ABSTRACTMPDELEMENT_H_ */ diff --git a/libdash/source/mpd/AdaptationSet.cpp b/libdash/source/mpd/AdaptationSet.cpp new file mode 100644 index 00000000..b1a0331f --- /dev/null +++ b/libdash/source/mpd/AdaptationSet.cpp @@ -0,0 +1,343 @@ +/* + * AdaptationSet.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AdaptationSet.h" +#include + +using namespace dash::mpd; + +AdaptationSet::AdaptationSet () : + segmentBase(NULL), + segmentList(NULL), + segmentTemplate(NULL), + xlinkHref(""), + xlinkActuate("onRequest"), + id(0), + lang(""), + contentType(""), + par(""), + minBandwidth(0), + maxBandwidth(0), + minWidth(0), + maxWidth(0), + minHeight(0), + maxHeight(0), + minFramerate(""), + maxFramerate(""), + segmentAlignmentIsBool(true), + subsegmentAlignmentIsBool(true), + usesSegmentAlignment(false), + usesSubsegmentAlignment(false), + segmentAlignment(0), + subsegmentAlignment(0), + isBitstreamSwitching(false) +{ +} +AdaptationSet::~AdaptationSet () +{ + for(size_t i = 0; i < this->accessibility.size(); i++) + delete(this->accessibility.at(i)); + for(size_t i = 0; i < this->role.size(); i++) + delete(this->role.at(i)); + for(size_t i = 0; i < this->rating.size(); i++) + delete(this->rating.at(i)); + for(size_t i = 0; i < this->viewpoint.size(); i++) + delete(this->viewpoint.at(i)); + for(size_t i = 0; i < this->contentComponent.size(); i++) + delete(this->contentComponent.at(i)); + for(size_t i = 0; i < this->baseURLs.size(); i++) + delete(this->baseURLs.at(i)); + for(size_t i = 0; i < this->representation.size(); i++) + delete(this->representation.at(i)); + + delete(segmentBase); + delete(segmentList); + delete(segmentTemplate); +} + +const std::vector& AdaptationSet::GetAccessibility () const +{ + return (std::vector &) this->accessibility; +} +void AdaptationSet::AddAccessibity (Descriptor *accessibility) +{ + this->accessibility.push_back(accessibility); +} +const std::vector& AdaptationSet::GetRole () const +{ + return (std::vector &) this->role; +} +void AdaptationSet::AddRole (Descriptor *role) +{ + this->role.push_back(role); +} +const std::vector& AdaptationSet::GetRating () const +{ + return (std::vector &) this->rating; +} +void AdaptationSet::AddRating (Descriptor *rating) +{ + this->rating.push_back(rating); +} +const std::vector& AdaptationSet::GetViewpoint () const +{ + return (std::vector &) this->viewpoint; +} +void AdaptationSet::AddViewpoint (Descriptor *viewpoint) +{ + this->viewpoint.push_back(viewpoint); +} +const std::vector& AdaptationSet::GetContentComponent () const +{ + return (std::vector &) this->contentComponent; +} +void AdaptationSet::AddContentComponent (ContentComponent *contentComponent) +{ + this->contentComponent.push_back(contentComponent); +} +const std::vector& AdaptationSet::GetBaseURLs () const +{ + return (std::vector &) this->baseURLs; +} +void AdaptationSet::AddBaseURL (BaseUrl *baseUrl) +{ + this->baseURLs.push_back(baseUrl); +} +ISegmentBase* AdaptationSet::GetSegmentBase () const +{ + return this->segmentBase; +} +void AdaptationSet::SetSegmentBase (SegmentBase *segmentBase) +{ + this->segmentBase = segmentBase; +} +ISegmentList* AdaptationSet::GetSegmentList () const +{ + return this->segmentList; +} +void AdaptationSet::SetSegmentList (SegmentList *segmentList) +{ + this->segmentList = segmentList; +} +ISegmentTemplate* AdaptationSet::GetSegmentTemplate () const +{ + return this->segmentTemplate; +} +void AdaptationSet::SetSegmentTemplate (SegmentTemplate *segmentTemplate) +{ + this->segmentTemplate = segmentTemplate; +} +const std::vector& AdaptationSet::GetRepresentation () const +{ + return (std::vector &) this->representation; +} +void AdaptationSet::AddRepresentation (Representation *representation) +{ + this->representation.push_back(representation); +} +const std::string& AdaptationSet::GetXlinkHref () const +{ + return this->xlinkHref; +} +void AdaptationSet::SetXlinkHref (const std::string& xlinkHref) +{ + this->xlinkHref = xlinkHref; +} +const std::string& AdaptationSet::GetXlinkActuate () const +{ + return this->xlinkActuate; +} +void AdaptationSet::SetXlinkActuate (const std::string& xlinkActuate) +{ + this->xlinkActuate = xlinkActuate; +} +uint32_t AdaptationSet::GetId () const +{ + return this->id; +} +void AdaptationSet::SetId (uint32_t id) +{ + this->id = id; +} +uint32_t AdaptationSet::GetGroup () const +{ + return this->group; +} +void AdaptationSet::SetGroup (uint32_t group) +{ + this->group = group; +} +const std::string& AdaptationSet::GetLang () const +{ + return this->lang; +} +void AdaptationSet::SetLang (const std::string& lang) +{ + this->lang = lang; +} +const std::string& AdaptationSet::GetContentType () const +{ + return this->contentType; +} +void AdaptationSet::SetContentType (const std::string& contentType) +{ + this->contentType = contentType; +} +const std::string& AdaptationSet::GetPar () const +{ + return this->par; +} +void AdaptationSet::SetPar (const std::string& par) +{ + this->par = par; +} +uint32_t AdaptationSet::GetMinBandwidth () const +{ + return this->minBandwidth; +} +void AdaptationSet::SetMinBandwidth (uint32_t minBandwidth) +{ + this->minBandwidth = minBandwidth; +} +uint32_t AdaptationSet::GetMaxBandwidth () const +{ + return this->maxBandwidth; +} +void AdaptationSet::SetMaxBandwidth (uint32_t maxBandwidth) +{ + this->maxBandwidth = maxBandwidth; +} +uint32_t AdaptationSet::GetMinWidth () const +{ + return this->minWidth; +} +void AdaptationSet::SetMinWidth (uint32_t minWidth) +{ + this->minWidth = minWidth; +} +uint32_t AdaptationSet::GetMaxWidth () const +{ + return this->maxWidth; +} +void AdaptationSet::SetMaxWidth (uint32_t maxWidth) +{ + this->maxWidth = maxWidth; +} +uint32_t AdaptationSet::GetMinHeight () const +{ + return this->minHeight; +} +void AdaptationSet::SetMinHeight (uint32_t minHeight) +{ + this->minHeight = minHeight; +} +uint32_t AdaptationSet::GetMaxHeight () const +{ + return this->maxHeight; +} +void AdaptationSet::SetMaxHeight (uint32_t maxHeight) +{ + this->maxHeight = maxHeight; +} +const std::string& AdaptationSet::GetMinFramerate () const +{ + return this->minFramerate; +} +void AdaptationSet::SetMinFramerate (const std::string& minFramerate) +{ + this->minFramerate = minFramerate; +} +const std::string& AdaptationSet::GetMaxFramerate () const +{ + return this->maxFramerate; +} +void AdaptationSet::SetMaxFramerate (const std::string& maxFramerate) +{ + this->maxFramerate = maxFramerate; +} +bool AdaptationSet::SegmentAlignmentIsBoolValue () const +{ + return this->segmentAlignmentIsBool; +} +bool AdaptationSet::SubsegmentAlignmentIsBoolValue () const +{ + return this->subsegmentAlignmentIsBool; +} +bool AdaptationSet::HasSegmentAlignment () const +{ + return this->usesSegmentAlignment; +} +bool AdaptationSet::HasSubsegmentAlignment () const +{ + return this->usesSubsegmentAlignment; +} +uint32_t AdaptationSet::GetSegmentAligment () const +{ + return this->segmentAlignment; +} +void AdaptationSet::SetSegmentAlignment (const std::string& segmentAlignment) +{ + if (segmentAlignment == "true" || segmentAlignment == "True" || segmentAlignment == "TRUE") + { + this->segmentAlignmentIsBool = true; + this->usesSegmentAlignment = true; + return; + } + + if (segmentAlignment == "false" || segmentAlignment == "False" || segmentAlignment == "FALSE") + { + this->segmentAlignmentIsBool = true; + this->usesSegmentAlignment = false; + return; + } + + this->segmentAlignmentIsBool = false; + this->segmentAlignment = strtoul(segmentAlignment.c_str(), NULL, 10); +} +void AdaptationSet::SetSubsegmentAlignment (const std::string& subsegmentAlignment) +{ + if (subsegmentAlignment == "true" || subsegmentAlignment == "True" || subsegmentAlignment == "TRUE") + { + this->subsegmentAlignmentIsBool = true; + this->usesSubsegmentAlignment = true; + return; + } + + if (subsegmentAlignment == "false" || subsegmentAlignment == "False" || subsegmentAlignment == "FALSE") + { + this->subsegmentAlignmentIsBool = true; + this->usesSubsegmentAlignment = false; + return; + } + + this->subsegmentAlignmentIsBool = false; + this->subsegmentAlignment = strtoul(subsegmentAlignment.c_str(), NULL, 10); +} +uint32_t AdaptationSet::GetSubsegmentAlignment () const +{ + return this->subsegmentAlignment; +} +uint8_t AdaptationSet::GetSubsegmentStartsWithSAP () const +{ + return this->subsegmentStartsWithSAP; +} +void AdaptationSet::SetSubsegmentStartsWithSAP (uint8_t subsegmentStartsWithSAP) +{ + this->subsegmentStartsWithSAP = subsegmentStartsWithSAP; +} +bool AdaptationSet::GetBitstreamSwitching () const +{ + return this->isBitstreamSwitching; +} +void AdaptationSet::SetBitstreamSwitching (bool value) +{ + this->isBitstreamSwitching = value; +} diff --git a/libdash/source/mpd/AdaptationSet.h b/libdash/source/mpd/AdaptationSet.h new file mode 100644 index 00000000..5a17cf4a --- /dev/null +++ b/libdash/source/mpd/AdaptationSet.h @@ -0,0 +1,138 @@ +/* + * AdaptationSet.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef ADAPTATIONSET_H_ +#define ADAPTATIONSET_H_ + +#include "config.h" + +#include "IAdaptationSet.h" +#include "RepresentationBase.h" +#include "BaseUrl.h" +#include "SegmentBase.h" +#include "SegmentList.h" +#include "SegmentTemplate.h" +#include "ContentComponent.h" +#include "Representation.h" + +namespace dash +{ + namespace mpd + { + class AdaptationSet : public IAdaptationSet, public RepresentationBase + { + public: + AdaptationSet (); + virtual ~AdaptationSet (); + + const std::vector& GetAccessibility () const; + const std::vector& GetRole () const; + const std::vector& GetRating () const; + const std::vector& GetViewpoint () const; + const std::vector& GetContentComponent () const; + const std::vector& GetBaseURLs () const; + ISegmentBase* GetSegmentBase () const; + ISegmentList* GetSegmentList () const; + ISegmentTemplate* GetSegmentTemplate () const; + const std::vector& GetRepresentation () const; + const std::string& GetXlinkHref () const; + const std::string& GetXlinkActuate () const; + uint32_t GetId () const; + uint32_t GetGroup () const; + const std::string& GetLang () const; + const std::string& GetContentType () const; + const std::string& GetPar () const; + uint32_t GetMinBandwidth () const; + uint32_t GetMaxBandwidth () const; + uint32_t GetMinWidth () const; + uint32_t GetMaxWidth () const; + uint32_t GetMinHeight () const; + uint32_t GetMaxHeight () const; + const std::string& GetMinFramerate () const; + const std::string& GetMaxFramerate () const; + bool SegmentAlignmentIsBoolValue () const; + bool HasSegmentAlignment () const; + uint32_t GetSegmentAligment () const; + bool SubsegmentAlignmentIsBoolValue () const; + bool HasSubsegmentAlignment () const; + uint32_t GetSubsegmentAlignment () const; + uint8_t GetSubsegmentStartsWithSAP () const; + bool GetBitstreamSwitching () const; + + void AddAccessibity (Descriptor *accessibility); + void AddRole (Descriptor *role); + void AddRating (Descriptor *rating); + void AddViewpoint (Descriptor *viewpoint); + void AddContentComponent (ContentComponent *contentComponent); + void AddBaseURL (BaseUrl *baseURL); + void SetSegmentBase (SegmentBase *segmentBase); + void SetSegmentList (SegmentList *segmentList); + void SetSegmentTemplate (SegmentTemplate *segmentTemplate); + void AddRepresentation (Representation* representation); + void SetXlinkHref (const std::string& xlinkHref); + void SetXlinkActuate (const std::string& xlinkActuate); + void SetId (uint32_t id); + void SetGroup (uint32_t group); + void SetLang (const std::string& lang); + void SetContentType (const std::string& contentType); + void SetPar (const std::string& par); + void SetMinBandwidth (uint32_t minBandwidth); + void SetMaxBandwidth (uint32_t maxBandwidth); + void SetMinWidth (uint32_t minWidth); + void SetMaxWidth (uint32_t maxWidth); + void SetMinHeight (uint32_t minHeight); + void SetMaxHeight (uint32_t maxHeight); + void SetMinFramerate (const std::string& minFramerate); + void SetMaxFramerate (const std::string& maxFramerate); + void SetSegmentAlignment (const std::string& segmentAlignment); + void SetSubsegmentAlignment (const std::string& subsegmentAlignment); + void SetSubsegmentStartsWithSAP (uint8_t subsegmentStartsWithSAP); + void SetBitstreamSwitching (bool value); + + private: + std::vector accessibility; + std::vector role; + std::vector rating; + std::vector viewpoint; + std::vector contentComponent; + std::vector baseURLs; + SegmentBase *segmentBase; + SegmentList *segmentList; + SegmentTemplate *segmentTemplate; + std::vector representation; + std::string xlinkHref; + std::string xlinkActuate; + uint32_t id; + uint32_t group; + std::string lang; + std::string contentType; + std::string par; + uint32_t minBandwidth; + uint32_t maxBandwidth; + uint32_t minWidth; + uint32_t maxWidth; + uint32_t minHeight; + uint32_t maxHeight; + std::string minFramerate; + std::string maxFramerate; + bool segmentAlignmentIsBool; + bool subsegmentAlignmentIsBool; + bool usesSegmentAlignment; + bool usesSubsegmentAlignment; + uint32_t segmentAlignment; + uint32_t subsegmentAlignment; + uint8_t subsegmentStartsWithSAP; + bool isBitstreamSwitching; + }; + } +} + +#endif /* ADAPTATIONSET_H_ */ diff --git a/libdash/source/mpd/BaseUrl.cpp b/libdash/source/mpd/BaseUrl.cpp new file mode 100644 index 00000000..7bafe794 --- /dev/null +++ b/libdash/source/mpd/BaseUrl.cpp @@ -0,0 +1,60 @@ +/* + * BaseUrl.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "BaseUrl.h" + +using namespace dash::mpd; + +BaseUrl::BaseUrl () : + url(""), + serviceLocation(""), + byteRange("") +{ +} +BaseUrl::~BaseUrl () +{ +} + +const std::string& BaseUrl::GetUrl () const +{ + return this->url; +} +void BaseUrl::SetUrl (const std::string& url) +{ + this->url = url; +} +const std::string& BaseUrl::GetServiceLocation () const +{ + return this->serviceLocation; +} +void BaseUrl::SetServiceLocation (const std::string& serviceLocation) +{ + this->serviceLocation = serviceLocation; +} +const std::string& BaseUrl::GetByteRange () const +{ + return this->byteRange; +} +void BaseUrl::SetByteRange (const std::string& byteRange) +{ + this->byteRange = byteRange; +} +ISegment* BaseUrl::ToMediaSegment (const std::vector& baseurls) const +{ + Segment *seg = new Segment(); + + if(seg->Init(baseurls, this->url, this->byteRange, dash::metrics::MediaSegment)) + return seg; + + delete(seg); + + return NULL; +} diff --git a/libdash/source/mpd/BaseUrl.h b/libdash/source/mpd/BaseUrl.h new file mode 100644 index 00000000..0147c1ed --- /dev/null +++ b/libdash/source/mpd/BaseUrl.h @@ -0,0 +1,49 @@ +/* + * BaseUrl.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef BASEURL_H_ +#define BASEURL_H_ + +#include "config.h" + +#include "Segment.h" +#include "IBaseUrl.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class BaseUrl : public IBaseUrl, public AbstractMPDElement + { + public: + BaseUrl (); + virtual ~BaseUrl(); + + const std::string& GetUrl () const; + const std::string& GetServiceLocation () const; + const std::string& GetByteRange () const; + + void SetUrl (const std::string& url); + void SetServiceLocation (const std::string& serviceLocation); + void SetByteRange (const std::string& byteRange); + + virtual ISegment* ToMediaSegment (const std::vector& baseurls) const; + + private: + std::string url; + std::string serviceLocation; + std::string byteRange; + }; + } +} + +#endif /* BASEURL_H_ */ diff --git a/libdash/source/mpd/ContentComponent.cpp b/libdash/source/mpd/ContentComponent.cpp new file mode 100644 index 00000000..ce3de857 --- /dev/null +++ b/libdash/source/mpd/ContentComponent.cpp @@ -0,0 +1,98 @@ +/* + * ContentComponent.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "ContentComponent.h" + +using namespace dash::mpd; + +ContentComponent::ContentComponent () : + id(0), + lang(""), + contentType(""), + par("") +{ +} +ContentComponent::~ContentComponent () +{ + for(size_t i = 0; i < this->accessibility.size(); i++) + delete(this->accessibility.at(i)); + for(size_t i = 0; i < this->role.size(); i++) + delete(this->role.at(i)); + for(size_t i = 0; i < this->rating.size(); i++) + delete(this->rating.at(i)); + for(size_t i = 0; i < this->viewpoint.size(); i++) + delete(this->viewpoint.at(i)); +} + +const std::vector& ContentComponent::GetAccessibility () const +{ + return (std::vector &)this->accessibility; +} +void ContentComponent::AddAccessibity (Descriptor *accessibility) +{ + this->accessibility.push_back(accessibility); +} +const std::vector& ContentComponent::GetRole () const +{ + return (std::vector &)this->role; +} +void ContentComponent::AddRole (Descriptor *role) +{ + this->role.push_back(role); +} +const std::vector& ContentComponent::GetRating () const +{ + return (std::vector &)this->rating; +} +void ContentComponent::AddRating (Descriptor *rating) +{ + this->rating.push_back(rating); +} +const std::vector& ContentComponent::GetViewpoint () const +{ + return (std::vector &)this->viewpoint; +} +void ContentComponent::AddViewpoint (Descriptor *viewpoint) +{ + this->viewpoint.push_back(viewpoint); +} +uint32_t ContentComponent::GetId () const +{ + return this->id; +} +void ContentComponent::SetId (uint32_t id) +{ + this->id = id; +} +const std::string& ContentComponent::GetLang () const +{ + return this->lang; +} +void ContentComponent::SetLang (const std::string& lang) +{ + this->lang = lang; +} +const std::string& ContentComponent::GetContentType () const +{ + return this->contentType; +} +void ContentComponent::SetContentType (const std::string& contentType) +{ + this->contentType = contentType; +} +const std::string& ContentComponent::GetPar () const +{ + return this->par; +} +void ContentComponent::SetPar (const std::string& par) +{ + this->par = par; +} diff --git a/libdash/source/mpd/ContentComponent.h b/libdash/source/mpd/ContentComponent.h new file mode 100644 index 00000000..b59ab6d8 --- /dev/null +++ b/libdash/source/mpd/ContentComponent.h @@ -0,0 +1,62 @@ +/* + * ContentComponent.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef CONTENTCOMPONENT_H_ +#define CONTENTCOMPONENT_H_ + +#include "config.h" + +#include "IContentComponent.h" +#include "Descriptor.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class ContentComponent : public IContentComponent, public AbstractMPDElement + { + public: + ContentComponent (); + virtual ~ContentComponent (); + + const std::vector& GetAccessibility () const; + const std::vector& GetRole () const; + const std::vector& GetRating () const; + const std::vector& GetViewpoint () const; + uint32_t GetId () const; + const std::string& GetLang () const; + const std::string& GetContentType () const; + const std::string& GetPar () const; + + void AddAccessibity (Descriptor *accessibility); + void AddRole (Descriptor *role); + void AddRating (Descriptor *rating); + void AddViewpoint (Descriptor *viewpoint); + void SetId (uint32_t id); + void SetLang (const std::string& lang); + void SetContentType (const std::string& contentType); + void SetPar (const std::string& par); + + private: + std::vector accessibility; + std::vector role; + std::vector rating; + std::vector viewpoint; + uint32_t id; + std::string lang; + std::string contentType; + std::string par; + }; + } +} + +#endif /* CONTENTCOMPONENT_H_ */ diff --git a/libdash/source/mpd/Descriptor.cpp b/libdash/source/mpd/Descriptor.cpp new file mode 100644 index 00000000..c6eb787b --- /dev/null +++ b/libdash/source/mpd/Descriptor.cpp @@ -0,0 +1,39 @@ +/* + * Descriptor.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "Descriptor.h" + +using namespace dash::mpd; + +Descriptor::Descriptor () : + schemeIdUri (""), + value ("") +{ +} +Descriptor::~Descriptor () +{ +} +const std::string& Descriptor::GetSchemeIdUri () const +{ + return this->schemeIdUri; +} +void Descriptor::SetSchemeIdUri (const std::string& schemeIdUri) +{ + this->schemeIdUri = schemeIdUri; +} +const std::string& Descriptor::GetValue () const +{ + return this->value; +} +void Descriptor::SetValue (const std::string& value) +{ + this->value = value; +} diff --git a/libdash/source/mpd/Descriptor.h b/libdash/source/mpd/Descriptor.h new file mode 100644 index 00000000..5cb66b94 --- /dev/null +++ b/libdash/source/mpd/Descriptor.h @@ -0,0 +1,43 @@ +/* + * Descriptor.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef DESCRIPTOR_H_ +#define DESCRIPTOR_H_ + +#include "config.h" + +#include "IDescriptor.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class Descriptor : public IDescriptor, public AbstractMPDElement + { + public: + Descriptor (); + virtual ~Descriptor (); + + const std::string& GetSchemeIdUri () const; + const std::string& GetValue () const; + + void SetValue (const std::string& value); + void SetSchemeIdUri (const std::string& schemeIdUri); + + private: + std::string schemeIdUri; + std::string value; + }; + } +} + +#endif /* DESCRIPTOR_H_ */ diff --git a/libdash/source/mpd/MPD.cpp b/libdash/source/mpd/MPD.cpp new file mode 100644 index 00000000..84e0e614 --- /dev/null +++ b/libdash/source/mpd/MPD.cpp @@ -0,0 +1,212 @@ +/* + * MPD.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "MPD.h" + +using namespace dash::mpd; +using namespace dash::metrics; + +MPD::MPD () : + id(""), + type("static"), + availabilityStarttime(""), + availabilityEndtime(""), + mediaPresentationDuration(""), + minimumUpdatePeriod(""), + minBufferTime(""), + timeShiftBufferDepth(""), + suggestedPresentationDelay(""), + maxSegmentDuration(""), + maxSubsegmentDuration("") +{ +} +MPD::~MPD () +{ + for(size_t i = 0; i < this->programInformations.size(); i++) + delete(this->programInformations.at(i)); + for(size_t i = 0; i < this->metrics.size(); i++) + delete(this->metrics.at(i)); + for(size_t i = 0; i < this->periods.size(); i++) + delete(this->periods.at(i)); + for(size_t i = 0; i < this->baseUrls.size(); i++) + delete(this->baseUrls.at(i)); +} + +const std::vector& MPD::GetProgramInformations () const +{ + return (std::vector &) this->programInformations; +} +void MPD::AddProgramInformation (ProgramInformation *programInformation) +{ + this->programInformations.push_back(programInformation); +} +const std::vector& MPD::GetBaseUrls () const +{ + return (std::vector &) this->baseUrls; +} +void MPD::AddBaseUrl (BaseUrl *url) +{ + this->baseUrls.push_back(url); +} +const std::vector& MPD::GetLocations () const +{ + return this->locations; +} +void MPD::AddLocation (const std::string& location) +{ + this->locations.push_back(location); +} +const std::vector& MPD::GetPeriods () const +{ + return (std::vector &) this->periods; +} +void MPD::AddPeriod (Period *period) +{ + this->periods.push_back(period); +} +const std::vector& MPD::GetMetrics () const +{ + return (std::vector &) this->metrics; +} +void MPD::AddMetrics (Metrics *metrics) +{ + this->metrics.push_back(metrics); +} +const std::string& MPD::GetId () const +{ + return this->id; +} +void MPD::SetId (const std::string& id) +{ + this->id = id; +} +const std::vector& MPD::GetProfiles () const +{ + return this->profiles; +} +void MPD::SetProfiles (const std::string& profiles) +{ + dash::helpers::String::Split(profiles, ',', this->profiles); +} +const std::string& MPD::GetType () const +{ + return this->type; +} +void MPD::SetType (const std::string& type) +{ + this->type = type; +} +const std::string& MPD::GetAvailabilityStarttime () const +{ + return this->availabilityStarttime; +} +void MPD::SetAvailabilityStarttime (const std::string& availabilityStarttime) +{ + this->availabilityStarttime = availabilityStarttime; +} +const std::string& MPD::GetAvailabilityEndtime () const +{ + return this->availabilityEndtime; +} +void MPD::SetAvailabilityEndtime (const std::string& availabilityEndtime) +{ + this->availabilityEndtime = availabilityEndtime; +} +const std::string& MPD::GetMediaPresentationDuration () const +{ + return this->mediaPresentationDuration; +} +void MPD::SetMediaPresentationDuration (const std::string& mediaPresentationDuration) +{ + this->mediaPresentationDuration = mediaPresentationDuration; +} +const std::string& MPD::GetMinimumUpdatePeriod () const +{ + return this->minimumUpdatePeriod; +} +void MPD::SetMinimumUpdatePeriod (const std::string& minimumUpdatePeriod) +{ + this->minimumUpdatePeriod = minimumUpdatePeriod; +} +const std::string& MPD::GetMinBufferTime () const +{ + return this->minBufferTime; +} +void MPD::SetMinBufferTime (const std::string& minBufferTime) +{ + this->minBufferTime = minBufferTime; +} +const std::string& MPD::GetTimeShiftBufferDepth () const +{ + return this->timeShiftBufferDepth; +} +void MPD::SetTimeShiftBufferDepth (const std::string& timeShiftBufferDepth) +{ + this->timeShiftBufferDepth = timeShiftBufferDepth; +} +const std::string& MPD::GetSuggestedPresentationDelay () const +{ + return this->suggestedPresentationDelay; +} +void MPD::SetSuggestedPresentationDelay (const std::string& suggestedPresentationDelay) +{ + this->suggestedPresentationDelay = suggestedPresentationDelay; +} +const std::string& MPD::GetMaxSegmentDuration () const +{ + return this->maxSegmentDuration; +} +void MPD::SetMaxSegmentDuration (const std::string& maxSegmentDuration) +{ + this->maxSegmentDuration = maxSegmentDuration; +} +const std::string& MPD::GetMaxSubsegmentDuration () const +{ + return this->maxSubsegmentDuration; +} +void MPD::SetMaxSubsegmentDuration (const std::string& maxSubsegmentDuration) +{ + this->maxSubsegmentDuration = maxSubsegmentDuration; +} +IBaseUrl* MPD::GetMPDPathBaseUrl () const +{ + return this->mpdPathBaseUrl; +} +void MPD::SetMPDPathBaseUrl (BaseUrl *mpdPath) +{ + this->mpdPathBaseUrl = mpdPath; +} +uint32_t MPD::GetFetchTime () const +{ + return this->fetchTime; +} +void MPD::SetFetchTime (uint32_t fetchTimeInSec) +{ + this->fetchTime = fetchTimeInSec; +} + + +const std::vector& MPD::GetTCPConnectionList () const +{ + return (std::vector &) this->tcpConnections; +} +void MPD::AddTCPConnection (TCPConnection *tcpConn) +{ + this->tcpConnections.push_back(tcpConn); +} +const std::vector& MPD::GetHTTPTransactionList () const +{ + return (std::vector &) this->httpTransactions; +} +void MPD::AddHTTPTransaction (HTTPTransaction *httpTransAct) +{ + this->httpTransactions.push_back(httpTransAct); +} diff --git a/libdash/source/mpd/MPD.h b/libdash/source/mpd/MPD.h new file mode 100644 index 00000000..9bcb38af --- /dev/null +++ b/libdash/source/mpd/MPD.h @@ -0,0 +1,107 @@ +/* + * MPD.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef MPD_H_ +#define MPD_H_ + +#include "config.h" + +#include "IMPD.h" +#include "ProgramInformation.h" +#include "BaseUrl.h" +#include "Period.h" +#include "Metrics.h" +#include "AbstractMPDElement.h" +#include "../metrics/HTTPTransaction.h" +#include "../metrics/TCPConnection.h" + +namespace dash +{ + namespace mpd + { + class MPD : public IMPD, public AbstractMPDElement + { + public: + MPD (); + virtual ~MPD(); + + const std::vector& GetProgramInformations () const; + const std::vector& GetBaseUrls () const; + const std::vector& GetLocations () const; + const std::vector& GetPeriods () const; + const std::vector& GetMetrics () const; + const std::string& GetId () const; + const std::vector& GetProfiles () const; + const std::string& GetType () const; + const std::string& GetAvailabilityStarttime () const; + const std::string& GetAvailabilityEndtime () const; + const std::string& GetMediaPresentationDuration () const; + const std::string& GetMinimumUpdatePeriod () const; + const std::string& GetMinBufferTime () const; + const std::string& GetTimeShiftBufferDepth () const; + const std::string& GetSuggestedPresentationDelay () const; + const std::string& GetMaxSegmentDuration () const; + const std::string& GetMaxSubsegmentDuration () const; + IBaseUrl* GetMPDPathBaseUrl () const; + uint32_t GetFetchTime () const; + + const std::vector& GetTCPConnectionList () const; + const std::vector& GetHTTPTransactionList () const; + void AddTCPConnection (dash::metrics::TCPConnection *tcpConn); + void AddHTTPTransaction (dash::metrics::HTTPTransaction *httpTransAct); + + void AddProgramInformation (ProgramInformation *programInformation); + void AddBaseUrl (BaseUrl *url); + void AddLocation (const std::string& location); + void AddPeriod (Period *period); + void AddMetrics (Metrics *metrics); + void SetId (const std::string& id); + void SetProfiles (const std::string& profiles); + void SetType (const std::string& type); + void SetAvailabilityStarttime (const std::string& availabilityStarttime); + void SetAvailabilityEndtime (const std::string& availabilityEndtime); + void SetMediaPresentationDuration (const std::string& mediaPresentationDuration); + void SetMinimumUpdatePeriod (const std::string& minimumUpdatePeriod); + void SetMinBufferTime (const std::string& minBufferTime); + void SetTimeShiftBufferDepth (const std::string& timeShiftBufferDepth); + void SetSuggestedPresentationDelay (const std::string& suggestedPresentationDelay); + void SetMaxSegmentDuration (const std::string& maxSegmentDuration); + void SetMaxSubsegmentDuration (const std::string& maxSubsegmentDuration); + void SetMPDPathBaseUrl (BaseUrl *path); + void SetFetchTime (uint32_t fetchTimeInSec); + + private: + std::vector programInformations; + std::vector baseUrls; + std::vector locations; + std::vector periods; + std::vector metrics; + std::string id; + std::vector profiles; + std::string type; + std::string availabilityStarttime; + std::string availabilityEndtime; + std::string mediaPresentationDuration; + std::string minimumUpdatePeriod; + std::string minBufferTime; + std::string timeShiftBufferDepth; + std::string suggestedPresentationDelay; + std::string maxSegmentDuration; + std::string maxSubsegmentDuration; + BaseUrl *mpdPathBaseUrl; + uint32_t fetchTime; + + std::vector tcpConnections; + std::vector httpTransactions; + }; + } +} +#endif /* MPD_H_ */ diff --git a/libdash/source/mpd/Metrics.cpp b/libdash/source/mpd/Metrics.cpp new file mode 100644 index 00000000..8e64dec4 --- /dev/null +++ b/libdash/source/mpd/Metrics.cpp @@ -0,0 +1,51 @@ +/* + * Metrics.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "../mpd/Metrics.h" + +using namespace dash::mpd; + +Metrics::Metrics () : + metrics("") +{ +} +Metrics::~Metrics () +{ + for(size_t i = 0; i < this->reportings.size(); i++) + delete(this->reportings.at(i)); + for(size_t i = 0; i < this->ranges.size(); i++) + delete(this->ranges.at(i)); +} + +const std::vector& Metrics::GetReportings () const +{ + return (std::vector &)this->reportings; +} +void Metrics::AddReporting (Descriptor *reporting) +{ + this->reportings.push_back(reporting); +} +const std::vector& Metrics::GetRanges () const +{ + return (std::vector &)this->ranges; +} +void Metrics::AddRange (Range *range) +{ + this->ranges.push_back(range); +} +const std::string& Metrics::GetMetrics () const +{ + return this->metrics; +} +void Metrics::SetMetrics (const std::string& metrics) +{ + this->metrics = metrics; +} diff --git a/libdash/source/mpd/Metrics.h b/libdash/source/mpd/Metrics.h new file mode 100644 index 00000000..c879f829 --- /dev/null +++ b/libdash/source/mpd/Metrics.h @@ -0,0 +1,51 @@ +/* + * Metrics.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef METRICS_H_ +#define METRICS_H_ + +#include "config.h" + +#include +#include + +#include "IMetrics.h" +#include "Descriptor.h" +#include "Range.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class Metrics : public IMetrics, public AbstractMPDElement + { + public: + Metrics (); + virtual ~Metrics (); + + const std::vector& GetReportings () const; + const std::vector& GetRanges () const; + const std::string& GetMetrics () const; + + void AddReporting (Descriptor *reporting); + void AddRange (Range *range); + void SetMetrics (const std::string& metrics); + + private: + std::vector reportings; + std::vector ranges; + std::string metrics; + }; + } +} + +#endif /* METRICS_H_ */ diff --git a/libdash/source/mpd/MultipleSegmentBase.cpp b/libdash/source/mpd/MultipleSegmentBase.cpp new file mode 100644 index 00000000..ccfba515 --- /dev/null +++ b/libdash/source/mpd/MultipleSegmentBase.cpp @@ -0,0 +1,60 @@ +/* + * MultipleSegmentBase.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "MultipleSegmentBase.h" + +using namespace dash::mpd; + +MultipleSegmentBase::MultipleSegmentBase () : + bitstreamSwitching(NULL), + segmentTimeline(NULL), + duration(0), + startNumber(1) +{ +} +MultipleSegmentBase::~MultipleSegmentBase () +{ + delete(this->segmentTimeline); + delete(this->bitstreamSwitching); +} + +const ISegmentTimeline * MultipleSegmentBase::GetSegmentTimeline () const +{ + return (ISegmentTimeline *) this->segmentTimeline; +} +void MultipleSegmentBase::SetSegmentTimeline (SegmentTimeline *segmentTimeline) +{ + this->segmentTimeline = segmentTimeline; +} +const IURLType* MultipleSegmentBase::GetBitstreamSwitching () const +{ + return this->bitstreamSwitching; +} +void MultipleSegmentBase::SetBitstreamSwitching (URLType *bitstreamSwitching) +{ + this->bitstreamSwitching = bitstreamSwitching; +} +uint32_t MultipleSegmentBase::GetDuration () const +{ + return this->duration; +} +void MultipleSegmentBase::SetDuration (uint32_t duration) +{ + this->duration = duration; +} +uint32_t MultipleSegmentBase::GetStartNumber () const +{ + return this->startNumber; +} +void MultipleSegmentBase::SetStartNumber (uint32_t startNumber) +{ + this->startNumber = startNumber; +} diff --git a/libdash/source/mpd/MultipleSegmentBase.h b/libdash/source/mpd/MultipleSegmentBase.h new file mode 100644 index 00000000..386672ce --- /dev/null +++ b/libdash/source/mpd/MultipleSegmentBase.h @@ -0,0 +1,51 @@ +/* + * MultipleSegmentBase.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef MULTIPLESEGMENTBASE_H_ +#define MULTIPLESEGMENTBASE_H_ + +#include "config.h" + +#include "IMultipleSegmentBase.h" +#include "SegmentBase.h" +#include "SegmentTimeline.h" +#include "URLType.h" + +namespace dash +{ + namespace mpd + { + class MultipleSegmentBase : public virtual IMultipleSegmentBase, public SegmentBase + { + public: + MultipleSegmentBase (); + virtual ~MultipleSegmentBase (); + + const ISegmentTimeline* GetSegmentTimeline () const; + const IURLType* GetBitstreamSwitching () const; + uint32_t GetDuration () const; + uint32_t GetStartNumber () const; + + void SetSegmentTimeline (SegmentTimeline *segmentTimeline); + void SetBitstreamSwitching (URLType *bitstreamSwitching); + void SetDuration (uint32_t duration); + void SetStartNumber (uint32_t startNumber); + + protected: + SegmentTimeline *segmentTimeline; + URLType *bitstreamSwitching; + uint32_t duration; + uint32_t startNumber; + }; + } +} + +#endif /* MULTIPLESEGMENTBASE_H_ */ diff --git a/libdash/source/mpd/Period.cpp b/libdash/source/mpd/Period.cpp new file mode 100644 index 00000000..291677ea --- /dev/null +++ b/libdash/source/mpd/Period.cpp @@ -0,0 +1,137 @@ +/* + * Period.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "Period.h" + +using namespace dash::mpd; + +Period::Period () : + segmentBase(NULL), + segmentList(NULL), + segmentTemplate(NULL), + xlinkActuate("onRequest"), + xlinkHref(""), + id(""), + start(""), + duration(""), + isBitstreamSwitching(false) +{ +} +Period::~Period () +{ + for(size_t i = 0; i < this->baseURLs.size(); i++) + delete(this->baseURLs.at(i)); + for(size_t i = 0; i < this->adaptationSets.size(); i++) + delete(this->adaptationSets.at(i)); + for(size_t i = 0; i < this->subsets.size(); i++) + delete(this->subsets.at(i)); + delete(segmentBase); + delete(segmentList); + delete(segmentTemplate); +} + +const std::vector& Period::GetBaseURLs () const +{ + return (std::vector &) this->baseURLs; +} +void Period::AddBaseURL (BaseUrl *baseUrl) +{ + this->baseURLs.push_back(baseUrl); +} +ISegmentBase* Period::GetSegmentBase () const +{ + return this->segmentBase; +} +void Period::SetSegmentBase (SegmentBase *segmentBase) +{ + this->segmentBase = segmentBase; +} +ISegmentList* Period::GetSegmentList () const +{ + return this->segmentList; +} +void Period::SetSegmentList (SegmentList *segmentList) +{ + this->segmentList = segmentList; +} +ISegmentTemplate* Period::GetSegmentTemplate () const +{ + return this->segmentTemplate; +} +void Period::SetSegmentTemplate (SegmentTemplate *segmentTemplate) +{ + this->segmentTemplate = segmentTemplate; +} +const std::vector& Period::GetAdaptationSets () const +{ + return (std::vector &) this->adaptationSets; +} +void Period::AddAdaptationSet (AdaptationSet *adaptationSet) +{ + if(adaptationSet != NULL) + this->adaptationSets.push_back(adaptationSet); +} +const std::vector& Period::GetSubsets () const +{ + return (std::vector &) this->subsets; +} +void Period::AddSubset (Subset *subset) +{ + this->subsets.push_back(subset); +} +const std::string& Period::GetXlinkHref () const +{ + return this->xlinkHref; +} +void Period::SetXlinkHref (const std::string& xlinkHref) +{ + this->xlinkHref = xlinkHref; +} +const std::string& Period::GetXlinkActuate () const +{ + return this->xlinkActuate; +} +void Period::SetXlinkActuate (const std::string& xlinkActuate) +{ + this->xlinkActuate = xlinkActuate; +} +const std::string& Period::GetId () const +{ + return this->id; +} +void Period::SetId (const std::string& id) +{ + this->id = id; +} +const std::string& Period::GetStart () const +{ + return this->start; +} +void Period::SetStart (const std::string& start) +{ + this->start = start; +} +const std::string& Period::GetDuration () const +{ + return this->duration; +} +void Period::SetDuration (const std::string& duration) +{ + this->duration = duration; +} +bool Period::GetBitstreamSwitching () const +{ + return this->isBitstreamSwitching; +} +void Period::SetBitstreamSwitching (bool value) +{ + this->isBitstreamSwitching = value; +} diff --git a/libdash/source/mpd/Period.h b/libdash/source/mpd/Period.h new file mode 100644 index 00000000..9e97f0cb --- /dev/null +++ b/libdash/source/mpd/Period.h @@ -0,0 +1,79 @@ +/* + * Period.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef PERIOD_H_ +#define PERIOD_H_ + +#include "config.h" + +#include "IPeriod.h" +#include "BaseUrl.h" +#include "AdaptationSet.h" +#include "Subset.h" +#include "SegmentBase.h" +#include "SegmentList.h" +#include "SegmentTemplate.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class Period : public IPeriod, public AbstractMPDElement + { + public: + Period (); + virtual ~Period (); + + const std::vector& GetBaseURLs () const; + ISegmentBase* GetSegmentBase () const; + ISegmentList* GetSegmentList () const; + ISegmentTemplate* GetSegmentTemplate () const; + const std::vector& GetAdaptationSets () const; + const std::vector& GetSubsets () const; + const std::string& GetXlinkHref () const; + const std::string& GetXlinkActuate () const; + const std::string& GetId () const; + const std::string& GetStart () const; + const std::string& GetDuration () const; + bool GetBitstreamSwitching () const; + + void AddBaseURL (BaseUrl *baseURL); + void SetSegmentBase (SegmentBase *segmentBase); + void SetSegmentList (SegmentList *segmentList); + void SetSegmentTemplate (SegmentTemplate *segmentTemplate); + void AddAdaptationSet (AdaptationSet *AdaptationSet); + void AddSubset (Subset *subset); + void SetXlinkHref (const std::string& xlinkHref); + void SetXlinkActuate (const std::string& xlinkActuate); + void SetId (const std::string& id); + void SetStart (const std::string& start); + void SetDuration (const std::string& duration); + void SetBitstreamSwitching (bool value); + + private: + std::vector baseURLs; + SegmentBase *segmentBase; + SegmentList *segmentList; + SegmentTemplate *segmentTemplate; + std::vector adaptationSets; + std::vector subsets; + std::string xlinkHref; + std::string xlinkActuate; + std::string id; + std::string start; + std::string duration; + bool isBitstreamSwitching; + }; + } +} + +#endif /* PERIOD_H_ */ diff --git a/libdash/source/mpd/ProgramInformation.cpp b/libdash/source/mpd/ProgramInformation.cpp new file mode 100644 index 00000000..2ba0d6ff --- /dev/null +++ b/libdash/source/mpd/ProgramInformation.cpp @@ -0,0 +1,67 @@ +/* + * ProgramInformation.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "ProgramInformation.h" + +using namespace dash::mpd; + +ProgramInformation::ProgramInformation () : + title(""), + source(""), + copyright(""), + lang(""), + moreInformationURL("") +{ +} +ProgramInformation::~ProgramInformation () +{ +} + +const std::string& ProgramInformation::GetTitle () const +{ + return this->title; +} +void ProgramInformation::SetTitle (const std::string& title) +{ + this->title = title; +} +const std::string& ProgramInformation::GetSource () const +{ + return this->source; +} +void ProgramInformation::SetSource (const std::string& source) +{ + this->source = source; +} +const std::string& ProgramInformation::GetCopyright () const +{ + return this->copyright; +} +void ProgramInformation::SetCopyright (const std::string& copyright) +{ + this->copyright = copyright; +} +const std::string& ProgramInformation::GetLang () const +{ + return this->lang; +} +void ProgramInformation::SetLang (const std::string& lang) +{ + this->lang = lang; +} +const std::string& ProgramInformation::GetMoreInformationURL () const +{ + return this->moreInformationURL; +} +void ProgramInformation::SetMoreInformationURL (const std::string& moreInfoURL) +{ + this->moreInformationURL = moreInfoURL; +} diff --git a/libdash/source/mpd/ProgramInformation.h b/libdash/source/mpd/ProgramInformation.h new file mode 100644 index 00000000..9b12f33d --- /dev/null +++ b/libdash/source/mpd/ProgramInformation.h @@ -0,0 +1,52 @@ +/* + * ProgramInformation.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef PROGRAMINFORMATION_H_ +#define PROGRAMINFORMATION_H_ + +#include "config.h" + +#include "IProgramInformation.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class ProgramInformation : public IProgramInformation, public AbstractMPDElement + { + public: + ProgramInformation (); + virtual ~ProgramInformation (); + + const std::string& GetTitle () const; + const std::string& GetSource () const; + const std::string& GetCopyright () const; + const std::string& GetLang () const; + const std::string& GetMoreInformationURL () const; + + void SetTitle (const std::string& title); + void SetSource (const std::string& source); + void SetCopyright (const std::string& copyright); + void SetLang (const std::string& lang); + void SetMoreInformationURL (const std::string& moreInformationURL); + + private: + std::string title; + std::string source; + std::string copyright; + std::string lang; + std::string moreInformationURL; + }; + } +} + +#endif /* PROGRAMINFORMATION_H_ */ diff --git a/libdash/source/mpd/Range.cpp b/libdash/source/mpd/Range.cpp new file mode 100644 index 00000000..6f0aed9f --- /dev/null +++ b/libdash/source/mpd/Range.cpp @@ -0,0 +1,40 @@ +/* + * Range.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "Range.h" + +using namespace dash::mpd; + +Range::Range () : + starttime(""), + duration("") +{ +} +Range::~Range () +{ +} + +const std::string& Range::GetStarttime () const +{ + return this->starttime; +} +void Range::SetStarttime (const std::string& starttime) +{ + this->starttime = starttime; +} +const std::string& Range::GetDuration () const +{ + return this->duration; +} +void Range::SetDuration (const std::string& duration) +{ + this->duration = duration; +} diff --git a/libdash/source/mpd/Range.h b/libdash/source/mpd/Range.h new file mode 100644 index 00000000..f847b096 --- /dev/null +++ b/libdash/source/mpd/Range.h @@ -0,0 +1,42 @@ +/* + * Range.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef RANGE_H_ +#define RANGE_H_ + +#include "config.h" + +#include "IRange.h" + +namespace dash +{ + namespace mpd + { + class Range : public IRange + { + public: + Range (); + virtual ~Range (); + + const std::string& GetStarttime () const; + const std::string& GetDuration () const; + + void SetStarttime (const std::string& start); + void SetDuration (const std::string& duration); + + private: + std::string starttime; + std::string duration; + }; + } +} + +#endif /* RANGE_H_ */ diff --git a/libdash/source/mpd/Representation.cpp b/libdash/source/mpd/Representation.cpp new file mode 100644 index 00000000..fab7b493 --- /dev/null +++ b/libdash/source/mpd/Representation.cpp @@ -0,0 +1,116 @@ +/* + * Representation.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "Representation.h" + +using namespace dash::mpd; + +Representation::Representation () : + segmentBase (NULL), + segmentList (NULL), + segmentTemplate (NULL), + id(""), + bandwidth (0), + qualityRanking (0) +{ +} +Representation::~Representation () +{ + for(size_t i = 0; i < this->baseURLs.size(); i++) + delete(this->baseURLs.at(i)); + for(size_t i = 0; i < this->subRepresentations.size(); i++) + delete(this->subRepresentations.at(i)); + + delete(this->segmentTemplate); + delete(this->segmentBase); + delete(this->segmentList); +} + +const std::vector& Representation::GetBaseURLs () const +{ + return (std::vector &) this->baseURLs; +} +void Representation::AddBaseURL (BaseUrl *baseUrl) +{ + this->baseURLs.push_back(baseUrl); +} +const std::vector& Representation::GetSubRepresentations () const +{ + return (std::vector &) this->subRepresentations; +} +void Representation::AddSubRepresentation (SubRepresentation *subRepresentation) +{ + this->subRepresentations.push_back(subRepresentation); +} +ISegmentBase* Representation::GetSegmentBase () const +{ + return this->segmentBase; +} +void Representation::SetSegmentBase (SegmentBase *segmentBase) +{ + this->segmentBase = segmentBase; +} +ISegmentList* Representation::GetSegmentList () const +{ + return this->segmentList; +} +void Representation::SetSegmentList (SegmentList *segmentList) +{ + this->segmentList = segmentList; +} +ISegmentTemplate* Representation::GetSegmentTemplate () const +{ + return this->segmentTemplate; +} +void Representation::SetSegmentTemplate (SegmentTemplate *segmentTemplate) +{ + this->segmentTemplate = segmentTemplate; +} +const std::string& Representation::GetId () const +{ + return this->id; +} +void Representation::SetId (const std::string &id) +{ + this->id = id; +} +uint32_t Representation::GetBandwidth () const +{ + return this->bandwidth; +} +void Representation::SetBandwidth (uint32_t bandwidth) +{ + this->bandwidth = bandwidth; +} +uint32_t Representation::GetQualityRanking () const +{ + return this->qualityRanking; +} +void Representation::SetQualityRanking (uint32_t qualityRanking) +{ + this->qualityRanking = qualityRanking; +} +const std::vector& Representation::GetDependencyId () const +{ + return this->dependencyId; +} +void Representation::SetDependencyId (const std::string &dependencyId) +{ + dash::helpers::String::Split(dependencyId, ' ', this->dependencyId); +} +const std::vector& Representation::GetMediaStreamStructureId () const +{ + return this->mediaStreamStructureId; +} +void Representation::SetMediaStreamStructureId (const std::string& mediaStreamStructureId) +{ + dash::helpers::String::Split(mediaStreamStructureId, ' ', this->mediaStreamStructureId); +} diff --git a/libdash/source/mpd/Representation.h b/libdash/source/mpd/Representation.h new file mode 100644 index 00000000..a56f5828 --- /dev/null +++ b/libdash/source/mpd/Representation.h @@ -0,0 +1,75 @@ +/* + * Representation.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef REPRESENTATION_H_ +#define REPRESENTATION_H_ + +#include "config.h" + +#include "IRepresentation.h" +#include "SegmentTemplate.h" +#include "RepresentationBase.h" +#include "BaseUrl.h" +#include "SubRepresentation.h" +#include "SegmentBase.h" +#include "SegmentList.h" +#include "../helpers/String.h" + +namespace dash +{ + namespace mpd + { + class Representation : public IRepresentation, public RepresentationBase + { + public: + Representation (); + virtual ~Representation (); + + const std::vector& GetBaseURLs () const; + const std::vector& GetSubRepresentations () const; + ISegmentBase* GetSegmentBase () const; + ISegmentList* GetSegmentList () const; + ISegmentTemplate* GetSegmentTemplate () const; + const std::string& GetId () const; + uint32_t GetBandwidth () const; + uint32_t GetQualityRanking () const; + const std::vector& GetDependencyId () const; + const std::vector& GetMediaStreamStructureId () const; + + + void AddBaseURL (BaseUrl *baseURL); + void AddSubRepresentation (SubRepresentation *subRepresentation); + void SetSegmentBase (SegmentBase *segmentBase); + void SetSegmentList (SegmentList *segmentList); + void SetSegmentTemplate (SegmentTemplate *segmentTemplate); + void SetId (const std::string &id); + void SetBandwidth (uint32_t bandwidth); + void SetQualityRanking (uint32_t qualityRanking); + void SetDependencyId (const std::string &dependencyId); + void SetMediaStreamStructureId (const std::string &mediaStreamStructureId); + + private: + std::vector baseURLs; + std::vector subRepresentations; + SegmentBase *segmentBase; + SegmentList *segmentList; + SegmentTemplate *segmentTemplate; + std::string id; + uint32_t bandwidth; + uint32_t qualityRanking; + std::vector dependencyId; + std::vector mediaStreamStructureId; + + }; + } +} + +#endif /* REPRESENTATION_H_ */ diff --git a/libdash/source/mpd/RepresentationBase.cpp b/libdash/source/mpd/RepresentationBase.cpp new file mode 100644 index 00000000..f7b8970c --- /dev/null +++ b/libdash/source/mpd/RepresentationBase.cpp @@ -0,0 +1,175 @@ +/* + * RepresentationBase.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "RepresentationBase.h" + +using namespace dash::mpd; + +RepresentationBase::RepresentationBase () : + width(0), + height(0), + sar(""), + frameRate(""), + audioSamplingRate(""), + mimeType(""), + maximumSAPPeriod(0.0), + startWithSAP(0), + maxPlayoutRate(0.0), + codingDependency(false), + scanType("") +{ +} +RepresentationBase::~RepresentationBase () +{ + for(size_t i = 0; i < this->framePacking.size(); i++) + delete(this->framePacking.at(i)); + for(size_t i = 0; i < this->audioChannelConfiguration.size(); i++) + delete(this->audioChannelConfiguration.at(i)); + for(size_t i = 0; i < this->contentProtection.size(); i++) + delete(this->contentProtection.at(i)); +} + +const std::vector& RepresentationBase::GetFramePacking () const +{ + return (std::vector &) this->framePacking; +} +void RepresentationBase::AddFramePacking (Descriptor *framePacking) +{ + this->framePacking.push_back(framePacking); +} +const std::vector& RepresentationBase::GetAudioChannelConfiguration () const +{ + return (std::vector &) this->audioChannelConfiguration; +} +void RepresentationBase::AddAudioChannelConfiguration (Descriptor *audioChannelConfiguration) +{ + this->audioChannelConfiguration.push_back(audioChannelConfiguration); +} +const std::vector& RepresentationBase::GetContentProtection () const +{ + return (std::vector &) this->contentProtection; +} +void RepresentationBase::AddContentProtection (Descriptor *contentProtection) +{ + this->contentProtection.push_back(contentProtection); +} +const std::vector& RepresentationBase::GetProfiles () const +{ + return this->profiles; +} +void RepresentationBase::SetProfiles (const std::string& profiles) +{ + dash::helpers::String::Split(profiles, ',', this->profiles); +} +uint32_t RepresentationBase::GetWidth () const +{ + return this->width; +} +void RepresentationBase::SetWidth (uint32_t width) +{ + this->width = width; +} +uint32_t RepresentationBase::GetHeight () const +{ + return this->height; +} +void RepresentationBase::SetHeight (uint32_t height) +{ + this->height = height; +} +std::string RepresentationBase::GetSar () const +{ + return this->sar; +} +void RepresentationBase::SetSar (const std::string& sar) +{ + this->sar = sar; +} +std::string RepresentationBase::GetFrameRate () const +{ + return this->frameRate; +} +void RepresentationBase::SetFrameRate (const std::string& frameRate) +{ + this->frameRate = frameRate; +} +std::string RepresentationBase::GetAudioSamplingRate () const +{ + return this->audioSamplingRate; +} +void RepresentationBase::SetAudioSamplingRate (const std::string& audioSamplingRate) +{ + this->audioSamplingRate = audioSamplingRate; +} +std::string RepresentationBase::GetMimeType () const +{ + return this->mimeType; +} +void RepresentationBase::SetMimeType (const std::string& mimeType) +{ + this->mimeType = mimeType; +} +const std::vector& RepresentationBase::GetSegmentProfiles () const +{ + return this->segmentProfiles; +} +void RepresentationBase::SetSegmentProfiles (const std::string& segmentProfiles) +{ + dash::helpers::String::Split(segmentProfiles, ',', this->segmentProfiles); +} +const std::vector& RepresentationBase::GetCodecs () const +{ + return this->codecs; +} +void RepresentationBase::SetCodecs (const std::string& codecs) +{ + dash::helpers::String::Split(codecs, ',', this->codecs); +} +double RepresentationBase::GetMaximumSAPPeriod () const +{ + return this->maximumSAPPeriod; +} +void RepresentationBase::SetMaximumSAPPeriod (double maximumSAPPeriod) +{ + this->maximumSAPPeriod = maximumSAPPeriod; +} +uint8_t RepresentationBase::GetStartWithSAP () const +{ + return this->startWithSAP; +} +void RepresentationBase::SetStartWithSAP (uint8_t startWithSAP) +{ + this->startWithSAP = startWithSAP; +} +double RepresentationBase::GetMaxPlayoutRate () const +{ + return this->maxPlayoutRate; +} +void RepresentationBase::SetMaxPlayoutRate (double maxPlayoutRate) +{ + this->maxPlayoutRate = maxPlayoutRate; +} +bool RepresentationBase::HasCodingDependency () const +{ + return this->codingDependency; +} +void RepresentationBase::SetCodingDependency (bool codingDependency) +{ + this->codingDependency = codingDependency; +} +std::string RepresentationBase::GetScanType () const +{ + return this->scanType; +} +void RepresentationBase::SetScanType (const std::string& scanType) +{ + this->scanType = scanType; +} diff --git a/libdash/source/mpd/RepresentationBase.h b/libdash/source/mpd/RepresentationBase.h new file mode 100644 index 00000000..bb7fd287 --- /dev/null +++ b/libdash/source/mpd/RepresentationBase.h @@ -0,0 +1,89 @@ +/* + * RepresentationBase.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef REPRESENTATIONBASE_H_ +#define REPRESENTATIONBASE_H_ + +#include "config.h" + +#include "IRepresentationBase.h" +#include "Descriptor.h" +#include "../helpers/String.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class RepresentationBase : public virtual IRepresentationBase, public AbstractMPDElement + { + public: + RepresentationBase (); + virtual ~RepresentationBase (); + + const std::vector& GetFramePacking () const; + const std::vector& GetAudioChannelConfiguration () const; + const std::vector& GetContentProtection () const; + const std::vector& GetProfiles () const; + uint32_t GetWidth () const; + uint32_t GetHeight () const; + std::string GetSar () const; + std::string GetFrameRate () const; + std::string GetAudioSamplingRate () const; + std::string GetMimeType () const; + const std::vector& GetSegmentProfiles () const; + const std::vector& GetCodecs () const; + double GetMaximumSAPPeriod () const; + uint8_t GetStartWithSAP () const; + double GetMaxPlayoutRate () const; + bool HasCodingDependency () const; + std::string GetScanType () const; + + void AddFramePacking (Descriptor *framePacking); + void AddAudioChannelConfiguration (Descriptor *audioChannelConfiguration); + void AddContentProtection (Descriptor *contentProtection); + void SetProfiles (const std::string& profiles); + void SetWidth (uint32_t width); + void SetHeight (uint32_t height); + void SetSar (const std::string& sar); + void SetFrameRate (const std::string& frameRate); + void SetAudioSamplingRate (const std::string& audioSamplingRate); + void SetMimeType (const std::string& mimeType); + void SetSegmentProfiles (const std::string& segmentProfiles); + void SetCodecs (const std::string& codecs); + void SetMaximumSAPPeriod (double maximumSAPPeroid); + void SetStartWithSAP (uint8_t startWithSAP); + void SetMaxPlayoutRate (double maxPlayoutRate); + void SetCodingDependency (bool codingDependency); + void SetScanType (const std::string& scanType); + + protected: + std::vector framePacking; + std::vector audioChannelConfiguration; + std::vector contentProtection; + std::vector profiles; + uint32_t width; + uint32_t height; + std::string sar; + std::string frameRate; + std::string audioSamplingRate; + std::string mimeType; + std::vector segmentProfiles; + std::vector codecs; + double maximumSAPPeriod; + uint8_t startWithSAP; + double maxPlayoutRate; + bool codingDependency; + std::string scanType; + }; + } +} +#endif /* REPRESENTATIONBASE_H_ */ diff --git a/libdash/source/mpd/Segment.cpp b/libdash/source/mpd/Segment.cpp new file mode 100644 index 00000000..31200762 --- /dev/null +++ b/libdash/source/mpd/Segment.cpp @@ -0,0 +1,138 @@ +/* + * Segment.cpp + ***************************************************************************** + * Copyright (C) 2013, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "Segment.h" + +using namespace dash::mpd; +using namespace dash::helpers; +using namespace dash::metrics; + +Segment::Segment () : + host(""), + port(0), + path(""), + startByte(0), + endByte(0), + hasByteRange(false) +{ +} +Segment::~Segment () +{ +} + +bool Segment::Init (const std::vector& baseurls, const std::string &uri, const std::string &range, HTTPTransactionType type) +{ + std::string host = ""; + size_t port = 80; + std::string path = ""; + size_t startByte = 0; + size_t endByte = 0; + + this->absoluteuri = ""; + + for(size_t i = 0; i < baseurls.size(); i++) + this->absoluteuri = Path::CombinePaths(this->absoluteuri, baseurls.at(i)->GetUrl()); + + this->absoluteuri = Path::CombinePaths(this->absoluteuri, uri); + + if (uri != "" && dash::helpers::Path::GetHostPortAndPath(this->absoluteuri, host, port, path)) + { + this->host = host; + this->port = port; + this->path = path; + + if (range != "" && dash::helpers::Path::GetStartAndEndBytes(range, startByte, endByte)) + { + this->range = range; + this->hasByteRange = true; + this->startByte = startByte; + this->endByte = endByte; + } + + this->type = type; + + return true; + } + + return false; +} +std::string& Segment::AbsoluteURI () +{ + return this->absoluteuri; +} +std::string& Segment::Host () +{ + return this->host; +} +size_t Segment::Port () +{ + return this->port; +} +std::string& Segment::Path () +{ + return this->path; +} +std::string& Segment::Range () +{ + return this->range; +} +size_t Segment::StartByte () +{ + return this->startByte; +} +size_t Segment::EndByte () +{ + return this->endByte; +} +bool Segment::HasByteRange () +{ + return this->hasByteRange; +} +void Segment::AbsoluteURI (std::string uri) +{ + this->absoluteuri = uri; +} +void Segment::Host (std::string host) +{ + this->host = host; +} +void Segment::Port (size_t port) +{ + this->port = port; +} +void Segment::Path (std::string path) +{ + this->path = path; +} +void Segment::Range (std::string range) +{ + this->range = range; +} +void Segment::StartByte (size_t startByte) +{ + this->startByte = startByte; +} +void Segment::EndByte (size_t endByte) +{ + this->endByte = endByte; +} +void Segment::HasByteRange (bool hasByteRange) +{ + this->hasByteRange = hasByteRange; +} +HTTPTransactionType Segment::GetType () +{ + return this->type; +} +void Segment::SetType (HTTPTransactionType type) +{ + this->type = type; +} diff --git a/libdash/source/mpd/Segment.h b/libdash/source/mpd/Segment.h new file mode 100644 index 00000000..6f44a6f2 --- /dev/null +++ b/libdash/source/mpd/Segment.h @@ -0,0 +1,69 @@ +/* + * Segment.h + ***************************************************************************** + * Copyright (C) 2013, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef SEGMENT_H_ +#define SEGMENT_H_ + +#include "config.h" + +#include "../network/AbstractChunk.h" +#include "../helpers/Path.h" +#include "ISegment.h" +#include "IBaseUrl.h" +#include "../metrics/HTTPTransaction.h" + +namespace dash +{ + namespace mpd + { + class Segment : public network::AbstractChunk, public virtual ISegment + { + public: + Segment (); + virtual ~Segment(); + + bool Init (const std::vector& baseurls, const std::string &uri, + const std::string &range, dash::metrics::HTTPTransactionType type); + std::string& AbsoluteURI (); + std::string& Host (); + size_t Port (); + std::string& Path (); + std::string& Range (); + size_t StartByte (); + size_t EndByte (); + bool HasByteRange (); + dash::metrics::HTTPTransactionType GetType (); + + void AbsoluteURI (std::string uri); + void Host (std::string host); + void Port (size_t port); + void Path (std::string path); + void Range (std::string range); + void StartByte (size_t startByte); + void EndByte (size_t endByte); + void HasByteRange (bool hasByteRange); + void SetType (dash::metrics::HTTPTransactionType type); + + private: + std::string absoluteuri; + std::string host; + size_t port; + std::string path; + std::string range; + size_t startByte; + size_t endByte; + bool hasByteRange; + dash::metrics::HTTPTransactionType type; + }; + } +} + +#endif /* SEGMENT_H_ */ diff --git a/libdash/source/mpd/SegmentBase.cpp b/libdash/source/mpd/SegmentBase.cpp new file mode 100644 index 00000000..b1d890d9 --- /dev/null +++ b/libdash/source/mpd/SegmentBase.cpp @@ -0,0 +1,78 @@ +/* + * SegmentBase.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "SegmentBase.h" + +using namespace dash::mpd; + +SegmentBase::SegmentBase () : + initialization(NULL), + representationIndex(NULL), + timescale(1), + presentationTimeOffset(0), + indexRange(""), + indexRangeExact(false) +{ +} +SegmentBase::~SegmentBase () +{ + delete(this->initialization); + delete(this->representationIndex); +} + +const IURLType* SegmentBase::GetInitialization () const +{ + return this->initialization; +} +void SegmentBase::SetInitialization (URLType *initialization) +{ + this->initialization = initialization; +} +const IURLType* SegmentBase::GetRepresentationIndex () const +{ + return this->representationIndex; +} +void SegmentBase::SetRepresentationIndex (URLType *representationIndex) +{ + this->representationIndex = representationIndex; +} +uint32_t SegmentBase::GetTimescale () const +{ + return this->timescale; +} +void SegmentBase::SetTimescale (uint32_t timescale) +{ + this->timescale = timescale; +} +uint32_t SegmentBase::GetPresentationTimeOffset () const +{ + return this->presentationTimeOffset; +} +void SegmentBase::SetPresentationTimeOffset (uint32_t presentationTimeOffset) +{ + this->presentationTimeOffset = presentationTimeOffset; +} +const std::string& SegmentBase::GetIndexRange () const +{ + return this->indexRange; +} +void SegmentBase::SetIndexRange (const std::string& indexRange) +{ + this->indexRange = indexRange; +} +bool SegmentBase::HasIndexRangeExact () const +{ + return this->indexRangeExact; +} +void SegmentBase::SetIndexRangeExact (bool indexRangeExact) +{ + this->indexRangeExact = indexRangeExact; +} diff --git a/libdash/source/mpd/SegmentBase.h b/libdash/source/mpd/SegmentBase.h new file mode 100644 index 00000000..d627eb04 --- /dev/null +++ b/libdash/source/mpd/SegmentBase.h @@ -0,0 +1,56 @@ +/* + * SegmentBase.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef SEGMENTBASE_H_ +#define SEGMENTBASE_H_ + +#include "config.h" + +#include "ISegmentBase.h" +#include "URLType.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class SegmentBase : public virtual ISegmentBase, public AbstractMPDElement + { + public: + SegmentBase (); + virtual ~SegmentBase (); + + const IURLType* GetInitialization () const; + const IURLType* GetRepresentationIndex () const; + uint32_t GetTimescale () const; + uint32_t GetPresentationTimeOffset () const; + const std::string& GetIndexRange () const; + bool HasIndexRangeExact () const; + + void SetInitialization (URLType *initialization); + void SetRepresentationIndex (URLType *representationIndex); + void SetTimescale (uint32_t timescale); + void SetPresentationTimeOffset (uint32_t presentationTimeOffset); + void SetIndexRange (const std::string& indexRange); + void SetIndexRangeExact (bool indexRangeExact); + + protected: + URLType *initialization; + URLType *representationIndex; + uint32_t timescale; + uint32_t presentationTimeOffset; + std::string indexRange; + bool indexRangeExact; + }; + } +} + +#endif /* SEGMENTBASE_H_ */ diff --git a/libdash/source/mpd/SegmentList.cpp b/libdash/source/mpd/SegmentList.cpp new file mode 100644 index 00000000..d96264cb --- /dev/null +++ b/libdash/source/mpd/SegmentList.cpp @@ -0,0 +1,50 @@ +/* + * SegmentList.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "SegmentList.h" + +using namespace dash::mpd; + +SegmentList::SegmentList () : + xlinkHref(""), + xlinkActuate("onRequest") +{ +} +SegmentList::~SegmentList () +{ + for (size_t i = 0; i < segmentURLs.size(); i++) + delete(this->segmentURLs.at(i)); +} + +const std::vector& SegmentList::GetSegmentURLs () const +{ + return (std::vector &) this->segmentURLs; +} +void SegmentList::AddSegmentURL (SegmentURL *segmentURL) +{ + this->segmentURLs.push_back(segmentURL); +} +const std::string& SegmentList::GetXlinkHref () const +{ + return this->xlinkHref; +} +void SegmentList::SetXlinkHref (const std::string& xlinkHref) +{ + this->xlinkHref = xlinkHref; +} +const std::string& SegmentList::GetXlinkActuate () const +{ + return this->xlinkActuate; +} +void SegmentList::SetXlinkActuate (const std::string& xlinkActuate) +{ + this->xlinkActuate = xlinkActuate; +} diff --git a/libdash/source/mpd/SegmentList.h b/libdash/source/mpd/SegmentList.h new file mode 100644 index 00000000..b743c009 --- /dev/null +++ b/libdash/source/mpd/SegmentList.h @@ -0,0 +1,47 @@ +/* + * SegmentList.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef SEGMENTLIST_H_ +#define SEGMENTLIST_H_ + +#include "config.h" + +#include "ISegmentList.h" +#include "MultipleSegmentBase.h" +#include "SegmentURL.h" + +namespace dash +{ + namespace mpd + { + class SegmentList : public ISegmentList, public MultipleSegmentBase + { + public: + SegmentList (); + virtual ~SegmentList (); + + const std::vector& GetSegmentURLs () const; + const std::string& GetXlinkHref () const; + const std::string& GetXlinkActuate () const; + + void AddSegmentURL (SegmentURL *segmetURL); + void SetXlinkHref (const std::string& xlinkHref); + void SetXlinkActuate (const std::string& xlinkActuate); + + private: + std::vector segmentURLs; + std::string xlinkHref; + std::string xlinkActuate; + }; + } +} + +#endif /* SEGMENTLIST_H_ */ diff --git a/libdash/source/mpd/SegmentTemplate.cpp b/libdash/source/mpd/SegmentTemplate.cpp new file mode 100644 index 00000000..b92c4692 --- /dev/null +++ b/libdash/source/mpd/SegmentTemplate.cpp @@ -0,0 +1,152 @@ +/* + * SegmentTemplate.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "SegmentTemplate.h" + +using namespace dash::mpd; +using namespace dash::metrics; + +SegmentTemplate::SegmentTemplate () : + media(""), + index(""), + initialization(""), + bitstreamSwitching("") +{ +} +SegmentTemplate::~SegmentTemplate () +{ +} + +const std::string& SegmentTemplate::Getmedia () const +{ + return this->media; +} +void SegmentTemplate::SetMedia (const std::string& media) +{ + this->media = media; +} +const std::string& SegmentTemplate::Getindex () const +{ + return this->index; +} +void SegmentTemplate::SetIndex (const std::string& index) +{ + this->index = index; +} +const std::string& SegmentTemplate::Getinitialization () const +{ + return this->initialization; +} +void SegmentTemplate::SetInitialization (const std::string& initialization) +{ + this->initialization = initialization; +} +const std::string& SegmentTemplate::GetbitstreamSwitching () const +{ + return this->bitstreamSwitching; +} +void SegmentTemplate::SetBitstreamSwitching (const std::string& bitstreamSwitching) +{ + this->bitstreamSwitching = bitstreamSwitching; +} +ISegment* SegmentTemplate::ToInitializationSegment (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth) const +{ + return ToSegment(this->initialization, baseurls, representationID, bandwidth, dash::metrics::InitializationSegment); +} +ISegment* SegmentTemplate::ToBitstreamSwitchingSegment (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth) const +{ + return ToSegment(this->bitstreamSwitching, baseurls, representationID, bandwidth, dash::metrics::BitstreamSwitchingSegment); +} +ISegment* SegmentTemplate::GetMediaSegmentFromNumber (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t number) const +{ + return ToSegment(this->media, baseurls, representationID, bandwidth, dash::metrics::MediaSegment, number); +} +ISegment* SegmentTemplate::GetIndexSegmentFromNumber (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t number) const +{ + return ToSegment(this->index, baseurls, representationID, bandwidth, dash::metrics::IndexSegment, number); +} +ISegment* SegmentTemplate::GetMediaSegmentFromTime (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t time) const +{ + return ToSegment(this->media, baseurls, representationID, bandwidth, dash::metrics::MediaSegment, 0, time); +} +ISegment* SegmentTemplate::GetIndexSegmentFromTime (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t time) const +{ + return ToSegment(this->index, baseurls, representationID, bandwidth, dash::metrics::IndexSegment, 0, time); +} +std::string SegmentTemplate::ReplaceParameters (const std::string& uri, const std::string& representationID, uint32_t bandwidth, uint32_t number, uint32_t time) const +{ + std::vector chunks; + std::string replacedUri = ""; + + dash::helpers::String::Split(uri, '$', chunks); + + if (chunks.size() > 1) + { + for(size_t i = 0; i < chunks.size(); i++) + { + if ( chunks.at(i) == "RepresentationID") { + chunks.at(i) = representationID; + continue; + } + + if (chunks.at(i).find("Bandwidth") == 0) + { + FormatChunk(chunks.at(i), bandwidth); + continue; + } + + if (chunks.at(i).find("Number") == 0) + { + FormatChunk(chunks.at(i), number); + continue; + } + + if (chunks.at(i).find("Time") == 0) + { + FormatChunk(chunks.at(i), time); + continue; + } + } + + for(size_t i = 0; i < chunks.size(); i++) + replacedUri += chunks.at(i); + + return replacedUri; + } + else + { + replacedUri = uri; + return replacedUri; + } +} +void SegmentTemplate::FormatChunk (std::string& uri, uint32_t number) const +{ + char formattedNumber [50]; + size_t pos = 0; + std::string formatTag = "%01d"; + + if ( (pos = uri.find("%0")) != std::string::npos) + formatTag = uri.substr(pos).append("d"); + + sprintf(formattedNumber, formatTag.c_str(), number); + uri = formattedNumber; +} +ISegment* SegmentTemplate::ToSegment (const std::string& uri, const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, HTTPTransactionType type, uint32_t number, uint32_t time) const +{ + Segment *seg = new Segment(); + + if(seg->Init(baseurls, ReplaceParameters(uri, representationID, bandwidth, number, time), "", type)) + return seg; + + delete(seg); + + return NULL; +} \ No newline at end of file diff --git a/libdash/source/mpd/SegmentTemplate.h b/libdash/source/mpd/SegmentTemplate.h new file mode 100644 index 00000000..e5782a83 --- /dev/null +++ b/libdash/source/mpd/SegmentTemplate.h @@ -0,0 +1,61 @@ +/* + * SegmentTemplate.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef SEGMENTTEMPLATE_H_ +#define SEGMENTTEMPLATE_H_ + +#include "config.h" + +#include "ISegmentTemplate.h" +#include "MultipleSegmentBase.h" +#include "../helpers/String.h" + +namespace dash +{ + namespace mpd + { + class SegmentTemplate : public ISegmentTemplate, public MultipleSegmentBase + { + public: + SegmentTemplate (); + virtual ~SegmentTemplate (); + + const std::string& Getmedia () const; + const std::string& Getindex () const; + const std::string& Getinitialization () const; + const std::string& GetbitstreamSwitching () const; + ISegment* ToInitializationSegment (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth) const; + ISegment* ToBitstreamSwitchingSegment (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth) const; + ISegment* GetMediaSegmentFromNumber (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t number) const; + ISegment* GetIndexSegmentFromNumber (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t number) const; + ISegment* GetMediaSegmentFromTime (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t time) const; + ISegment* GetIndexSegmentFromTime (const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, uint32_t time) const; + + void SetMedia (const std::string& media); + void SetIndex (const std::string& index); + void SetInitialization (const std::string& initialization); + void SetBitstreamSwitching (const std::string& bitstreamSwichting); + + private: + std::string ReplaceParameters (const std::string& uri, const std::string& representationID, uint32_t bandwidth, uint32_t number, uint32_t time) const; + void FormatChunk (std::string& uri, uint32_t number) const; + ISegment* ToSegment (const std::string& uri, const std::vector& baseurls, const std::string& representationID, uint32_t bandwidth, + dash::metrics::HTTPTransactionType type, uint32_t number = 0, uint32_t time = 0) const; + + std::string media; + std::string index; + std::string initialization; + std::string bitstreamSwitching; + }; + } +} + +#endif /* SEGMENTTEMPLATE_H_ */ diff --git a/libdash/source/mpd/SegmentTimeline.cpp b/libdash/source/mpd/SegmentTimeline.cpp new file mode 100644 index 00000000..8a55dc46 --- /dev/null +++ b/libdash/source/mpd/SegmentTimeline.cpp @@ -0,0 +1,32 @@ +/* + * SegmentTimeline.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "SegmentTimeline.h" + +using namespace dash::mpd; + +SegmentTimeline::SegmentTimeline () +{ +} +SegmentTimeline::~SegmentTimeline () +{ + for (size_t i=0; i < this->timelines.size(); i++) + delete(this->timelines.at(i)); +} + +std::vector& SegmentTimeline::GetTimelines () const +{ + return (std::vector &) this->timelines; +} +void SegmentTimeline::AddTimeline (Timeline *timeline) +{ + this->timelines.push_back(timeline); +} \ No newline at end of file diff --git a/libdash/source/mpd/SegmentTimeline.h b/libdash/source/mpd/SegmentTimeline.h new file mode 100644 index 00000000..2d0ff2f8 --- /dev/null +++ b/libdash/source/mpd/SegmentTimeline.h @@ -0,0 +1,40 @@ +/* + * SegmentTimeline.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef SEGMENTTIMELINE_H_ +#define SEGMENTTIMELINE_H_ + +#include "config.h" + +#include "ISegmentTimeline.h" +#include "AbstractMPDElement.h" +#include "Timeline.h" + +namespace dash +{ + namespace mpd + { + class SegmentTimeline : public ISegmentTimeline, public AbstractMPDElement + { + public: + SegmentTimeline (); + virtual ~SegmentTimeline (); + + std::vector& GetTimelines () const; + void AddTimeline (Timeline *timeline); + + private: + std::vector timelines; + }; + } +} + +#endif /* SEGMENTTIMELINE_H_ */ diff --git a/libdash/source/mpd/SegmentURL.cpp b/libdash/source/mpd/SegmentURL.cpp new file mode 100644 index 00000000..765cc969 --- /dev/null +++ b/libdash/source/mpd/SegmentURL.cpp @@ -0,0 +1,90 @@ +/* + * SegmentURL.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "SegmentURL.h" + +using namespace dash::mpd; +using namespace dash::helpers; + +SegmentURL::SegmentURL () : + mediaURI(""), + mediaRange(""), + indexURI(""), + indexRange(""), + bitrate(0) +{ +} +SegmentURL::~SegmentURL () +{ +} + +const std::string& SegmentURL::GetMediaURI () const +{ + return this->mediaURI; +} +void SegmentURL::SetMediaURI (const std::string& mediaURI) +{ + this->mediaURI = mediaURI; +} +const std::string& SegmentURL::GetMediaRange () const +{ + return this->mediaRange; +} +void SegmentURL::SetMediaRange (const std::string& mediaRange) +{ + this->mediaRange = mediaRange; +} +const std::string& SegmentURL::GetIndexURI () const +{ + return this->indexURI; +} +uint64_t SegmentURL::GetActualRate () +{ + return this->bitrate; +} +void SegmentURL::SetIndexURI (const std::string& indexURI) +{ + this->indexURI = indexURI; +} +const std::string& SegmentURL::GetIndexRange () const +{ + return this->indexRange; +} +void SegmentURL::SetIndexRange (const std::string& indexRange) +{ + this->indexRange = indexRange; +} +void SegmentURL::SetBitrate (const std::string& bitrate) +{ + this->bitrate = atoi(bitrate.c_str()); +} +ISegment* SegmentURL::ToMediaSegment (const std::vector& baseurls) const +{ + Segment *seg = new Segment(); + + if(seg->Init(baseurls, this->mediaURI, this->mediaRange, dash::metrics::MediaSegment)) + return seg; + + delete(seg); + + return NULL; +} +ISegment* SegmentURL::ToIndexSegment (const std::vector& baseurls) const +{ + Segment *seg = new Segment(); + + if(seg->Init(baseurls, this->indexURI, this->indexRange, dash::metrics::IndexSegment)) + return seg; + + delete(seg); + + return NULL; +} diff --git a/libdash/source/mpd/SegmentURL.h b/libdash/source/mpd/SegmentURL.h new file mode 100644 index 00000000..7dfdb56b --- /dev/null +++ b/libdash/source/mpd/SegmentURL.h @@ -0,0 +1,56 @@ +/* + * SegmentURL.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef SEGMENTURL_H_ +#define SEGMENTURL_H_ + +#include "config.h" + +#include "ISegmentURL.h" +#include "../helpers/Path.h" +#include "Segment.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class SegmentURL : public ISegmentURL, public AbstractMPDElement + { + public: + SegmentURL (); + virtual ~SegmentURL(); + + const std::string& GetMediaURI () const; + const std::string& GetMediaRange () const; + const std::string& GetIndexURI () const; + const std::string& GetIndexRange () const; + uint64_t GetActualRate (); + + ISegment* ToMediaSegment (const std::vector& baseurls) const; + ISegment* ToIndexSegment (const std::vector& baseurls) const; + + void SetMediaURI (const std::string& mediaURI); + void SetMediaRange (const std::string& mediaRange); + void SetIndexURI (const std::string& indexURI); + void SetIndexRange (const std::string& indexRange); + void SetBitrate (const std::string& bitrate); + private: + std::string mediaURI; + std::string mediaRange; + std::string indexURI; + std::string indexRange; + int bitrate; + }; + } +} + +#endif /* SEGMENTURL_H_ */ diff --git a/libdash/source/mpd/SubRepresentation.cpp b/libdash/source/mpd/SubRepresentation.cpp new file mode 100644 index 00000000..34b11e57 --- /dev/null +++ b/libdash/source/mpd/SubRepresentation.cpp @@ -0,0 +1,55 @@ +/* + * SubRepresentation.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "SubRepresentation.h" + +using namespace dash::mpd; + +SubRepresentation::SubRepresentation () : + level(0), + bandWidth(0) +{ +} +SubRepresentation::~SubRepresentation () +{ +} +uint32_t SubRepresentation::GetLevel () const +{ + return this->level; +} +void SubRepresentation::SetLevel (uint32_t level) +{ + this->level = level; +} +const std::vector& SubRepresentation::GetDependencyLevel () const +{ + return this->dependencyLevel; +} +void SubRepresentation::SetDependencyLevel (const std::string& dependencyLevel) +{ + dash::helpers::String::Split(dependencyLevel, ' ', this->dependencyLevel); +} +uint32_t SubRepresentation::GetBandWidth () const +{ + return this->bandWidth; +} +void SubRepresentation::SetBandWidth (uint32_t bandWidth) +{ + this->bandWidth = bandWidth; +} +const std::vector& SubRepresentation::GetContentComponent () const +{ + return this->contentComponent; +} +void SubRepresentation::SetContentComponent (const std::string& contentComponent) +{ + dash::helpers::String::Split(contentComponent, ' ', this->contentComponent); +} diff --git a/libdash/source/mpd/SubRepresentation.h b/libdash/source/mpd/SubRepresentation.h new file mode 100644 index 00000000..f14fbfae --- /dev/null +++ b/libdash/source/mpd/SubRepresentation.h @@ -0,0 +1,50 @@ +/* + * SubRepresentation.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef SUBREPRESENTATION_H_ +#define SUBREPRESENTATION_H_ + +#include "config.h" + +#include "ISubRepresentation.h" +#include "Descriptor.h" +#include "RepresentationBase.h" +#include "../helpers/String.h" + +namespace dash +{ + namespace mpd + { + class SubRepresentation : public ISubRepresentation, public RepresentationBase + { + public: + SubRepresentation (); + virtual ~SubRepresentation (); + + uint32_t GetLevel () const; + const std::vector& GetDependencyLevel () const; + uint32_t GetBandWidth () const; + const std::vector& GetContentComponent () const; + + void SetLevel (uint32_t level); + void SetDependencyLevel (const std::string& dependencyLevel); + void SetBandWidth (uint32_t bandWidth); + void SetContentComponent (const std::string& contentComponent); + + protected: + uint32_t level; + std::vector dependencyLevel; + uint32_t bandWidth; + std::vector contentComponent; + }; + } +} +#endif /* SUBREPRESENTATION_H_ */ diff --git a/libdash/source/mpd/Subset.cpp b/libdash/source/mpd/Subset.cpp new file mode 100644 index 00000000..6b0d2df6 --- /dev/null +++ b/libdash/source/mpd/Subset.cpp @@ -0,0 +1,30 @@ +/* + * Subset.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "Subset.h" + +using namespace dash::mpd; + +Subset::Subset () +{ +} +Subset::~Subset () +{ +} + +const std::vector& Subset::Contains () const +{ + return this->subset; +} +void Subset::SetSubset (const std::string& subset) +{ + dash::helpers::String::Split(subset, ' ', this->subset); +} diff --git a/libdash/source/mpd/Subset.h b/libdash/source/mpd/Subset.h new file mode 100644 index 00000000..c56eb6e2 --- /dev/null +++ b/libdash/source/mpd/Subset.h @@ -0,0 +1,41 @@ +/* + * Subset.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef SUBSET_H_ +#define SUBSET_H_ + +#include "config.h" + +#include "ISubset.h" +#include "../helpers/String.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class Subset : public ISubset, public AbstractMPDElement + { + public: + Subset (); + virtual ~Subset (); + + const std::vector& Contains () const; + + void SetSubset (const std::string& subset); + + private: + std::vector subset; + }; + } +} + +#endif /* SUBSET_H_ */ diff --git a/libdash/source/mpd/Timeline.cpp b/libdash/source/mpd/Timeline.cpp new file mode 100644 index 00000000..624c6585 --- /dev/null +++ b/libdash/source/mpd/Timeline.cpp @@ -0,0 +1,49 @@ +/* + * Timeline.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "Timeline.h" + +using namespace dash::mpd; + +Timeline::Timeline () : + startTime(0), + duration(0), + repeatCount(0) +{ +} +Timeline::~Timeline () +{ +} + +uint32_t Timeline::GetStartTime () const +{ + return this->startTime; +} +void Timeline::SetStartTime (uint32_t startTime) +{ + this->startTime = startTime; +} +uint32_t Timeline::GetDuration () const +{ + return this->duration; +} +void Timeline::SetDuration (uint32_t duration) +{ + this->duration = duration; +} +uint32_t Timeline::GetRepeatCount () const +{ + return this->repeatCount; +} +void Timeline::SetRepeatCount (uint32_t repeatCount) +{ + this->repeatCount = repeatCount; +} \ No newline at end of file diff --git a/libdash/source/mpd/Timeline.h b/libdash/source/mpd/Timeline.h new file mode 100644 index 00000000..3caa331f --- /dev/null +++ b/libdash/source/mpd/Timeline.h @@ -0,0 +1,46 @@ +/* + * Timeline.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef TIMELINE_H_ +#define TIMELINE_H_ + +#include "config.h" + +#include "ITimeline.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class Timeline : public ITimeline, public AbstractMPDElement + { + public: + Timeline (); + virtual ~Timeline (); + + uint32_t GetStartTime () const; + uint32_t GetDuration () const; + uint32_t GetRepeatCount () const; + + void SetStartTime (uint32_t startTime); + void SetDuration (uint32_t duration); + void SetRepeatCount (uint32_t repeatCount); + + private: + uint32_t startTime; + uint32_t duration; + uint32_t repeatCount; + }; + } +} + +#endif /* TIMELINE_H_ */ diff --git a/libdash/source/mpd/URLType.cpp b/libdash/source/mpd/URLType.cpp new file mode 100644 index 00000000..699672e7 --- /dev/null +++ b/libdash/source/mpd/URLType.cpp @@ -0,0 +1,57 @@ +/* + * URLType.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "URLType.h" + +using namespace dash::mpd; +using namespace dash::helpers; +using namespace dash::metrics; + +URLType::URLType () : + sourceURL(""), + range("") +{ +} +URLType::~URLType () +{ +} + +const std::string& URLType::GetSourceURL () const +{ + return this->sourceURL; +} +void URLType::SetSourceURL (const std::string& sourceURL) +{ + this->sourceURL = sourceURL; +} +const std::string& URLType::GetRange () const +{ + return this->range; +} +void URLType::SetRange (const std::string& range) +{ + this->range = range; +} +void URLType::SetType (HTTPTransactionType type) +{ + this->type = type; +} +ISegment* URLType::ToSegment (const std::vector& baseurls) const +{ + Segment *seg = new Segment(); + + if(seg->Init(baseurls, this->sourceURL, this->range, this->type)) + return seg; + + delete(seg); + + return NULL; +} diff --git a/libdash/source/mpd/URLType.h b/libdash/source/mpd/URLType.h new file mode 100644 index 00000000..1fcd00c7 --- /dev/null +++ b/libdash/source/mpd/URLType.h @@ -0,0 +1,48 @@ +/* + * URLType.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef URLTYPE_H_ +#define URLTYPE_H_ + +#include "config.h" + +#include "IURLType.h" +#include "Segment.h" +#include "../helpers/Path.h" +#include "AbstractMPDElement.h" + +namespace dash +{ + namespace mpd + { + class URLType : public IURLType, public AbstractMPDElement + { + public: + URLType (); + virtual ~URLType (); + + const std::string& GetSourceURL () const; + const std::string& GetRange () const; + ISegment* ToSegment (const std::vector& baseurls) const; + + void SetSourceURL (const std::string& sourceURL); + void SetRange (const std::string& range); + void SetType (dash::metrics::HTTPTransactionType type); + + private: + std::string sourceURL; + std::string range; + dash::metrics::HTTPTransactionType type; + }; + } +} + +#endif /* URLTYPE_H_ */ diff --git a/libdash/source/network/AbstractChunk.cpp b/libdash/source/network/AbstractChunk.cpp new file mode 100644 index 00000000..35774efe --- /dev/null +++ b/libdash/source/network/AbstractChunk.cpp @@ -0,0 +1,271 @@ +/* + * AbstractChunk.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "AbstractChunk.h" + +using namespace dash::network; +using namespace dash::helpers; +using namespace dash::metrics; + +uint32_t AbstractChunk::BLOCKSIZE = 32768; + +AbstractChunk::AbstractChunk () : + connection (NULL), + dlThread (NULL), + bytesDownloaded (0) +{ +} +AbstractChunk::~AbstractChunk () +{ + this->AbortDownload(); + this->blockStream.Clear(); + DestroyThreadPortable(this->dlThread); +} + +void AbstractChunk::AbortDownload () +{ + this->stateManager.CheckAndSet(IN_PROGRESS, REQUEST_ABORT); + this->stateManager.CheckAndWait(REQUEST_ABORT, ABORTED); +} +bool AbstractChunk::StartDownload () +{ + if(this->stateManager.State() != NOT_STARTED) + return false; + curl_global_init(CURL_GLOBAL_ALL); + this->curlm = curl_multi_init(); + + this->curl = curl_easy_init(); + curl_easy_setopt(this->curl, CURLOPT_URL, this->AbsoluteURI().c_str()); + curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, CurlResponseCallback); + curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, (void *)this); + /* Debug Callback */ + curl_easy_setopt(this->curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(this->curl, CURLOPT_DEBUGFUNCTION, CurlDebugCallback); + curl_easy_setopt(this->curl, CURLOPT_DEBUGDATA, (void *)this); + curl_easy_setopt(this->curl, CURLOPT_FAILONERROR, true); + + if(this->HasByteRange()) + curl_easy_setopt(this->curl, CURLOPT_RANGE, this->Range().c_str()); + + curl_multi_add_handle(this->curlm, this->curl); + this->dlThread = CreateThreadPortable (DownloadInternalConnection, this); + + if(this->dlThread == NULL) + return false; + + this->stateManager.State(IN_PROGRESS); + + return true; +} +bool AbstractChunk::StartDownload (IConnection *connection) +{ + if(this->stateManager.State() != NOT_STARTED) + return false; + + this->connection = connection; + this->dlThread = CreateThreadPortable (DownloadExternalConnection, this); + + if(this->dlThread == NULL) + return false; + + this->stateManager.State(IN_PROGRESS); + + + return true; +} +int AbstractChunk::Read (uint8_t *data, size_t len) +{ + return this->blockStream.GetBytes(data, len); +} +int AbstractChunk::Peek (uint8_t *data, size_t len) +{ + return this->blockStream.PeekBytes(data, len); +} +int AbstractChunk::Peek (uint8_t *data, size_t len, size_t offset) +{ + return this->blockStream.PeekBytes(data, len, offset); +} +void AbstractChunk::AttachDownloadObserver (IDownloadObserver *observer) +{ + this->observers.push_back(observer); + this->stateManager.Attach(observer); +} +void AbstractChunk::DetachDownloadObserver (IDownloadObserver *observer) +{ + uint32_t pos = -1; + + for(size_t i = 0; i < this->observers.size(); i++) + if(this->observers.at(i) == observer) + pos = i; + + if(pos != -1) + this->observers.erase(this->observers.begin() + pos); + + this->stateManager.Detach(observer); +} +void* AbstractChunk::DownloadExternalConnection (void *abstractchunk) +{ + AbstractChunk *chunk = (AbstractChunk *) abstractchunk; + block_t *block = AllocBlock(chunk->BLOCKSIZE); + int ret = 0; + + int count = 0; + do + { + ret = chunk->connection->Read(block->data, block->len, chunk); + if(ret > 0) + { + block_t *streamblock = AllocBlock(ret); + memcpy(streamblock->data, block->data, ret); + chunk->blockStream.PushBack(streamblock); + chunk->bytesDownloaded += ret; + + // chunk->NotifyDownloadRateChanged(); + } + if(chunk->stateManager.State() == REQUEST_ABORT) + ret = 0; + count += ret; + }while(ret); + + double speed = chunk->connection->GetAverageDownloadingSpeed(); + double time = chunk->connection->GetDownloadingTime(); + chunk->NotifyDownloadRateChanged(speed); + chunk->NotifyDownloadTimeChanged(time); + DeleteBlock(block); + + if(chunk->stateManager.State() == REQUEST_ABORT) + chunk->stateManager.State(ABORTED); + else + chunk->stateManager.State(COMPLETED); + + chunk->blockStream.SetEOS(true); + + return NULL; +} +void* AbstractChunk::DownloadInternalConnection (void *abstractchunk) +{ + AbstractChunk *chunk = (AbstractChunk *) abstractchunk; + + //chunk->response = curl_easy_perform(chunk->curl); + int u =1; + + while(chunk->stateManager.State() != REQUEST_ABORT && u) + { + curl_multi_perform(chunk->curlm, &u); + } + double speed; + double size; + double time; + curl_easy_getinfo(chunk->curl, CURLINFO_SPEED_DOWNLOAD,&speed); + curl_easy_getinfo(chunk->curl, CURLINFO_SIZE_DOWNLOAD, &size); + curl_easy_getinfo(chunk->curl, CURLINFO_TOTAL_TIME, &time); + + //Speed is in Bps ==> *8 for the bps + speed = 8*speed; + //size = 8*size; //Uncomment for the size in bits. + chunk->NotifyDownloadRateChanged(speed); + chunk->NotifyDownloadTimeChanged(time); + curl_easy_cleanup(chunk->curl); + //curl_global_cleanup(); + + curl_multi_cleanup(chunk->curlm); + if(chunk->stateManager.State() == REQUEST_ABORT) + { + chunk->stateManager.State(ABORTED); + } + else + { + chunk->stateManager.State(COMPLETED); + } + + chunk->blockStream.SetEOS(true); + + return NULL; +} +void AbstractChunk::NotifyDownloadRateChanged (double bitrate) +{ + for(size_t i = 0; i < this->observers.size(); i++) + this->observers.at(i)->OnDownloadRateChanged((uint64_t)bitrate); +} +void AbstractChunk::NotifyDownloadTimeChanged (double dnltime) +{ + for(size_t i = 0; i < this->observers.size(); i++) + this->observers.at(i)->OnDownloadTimeChanged(dnltime); +} +size_t AbstractChunk::CurlResponseCallback (void *contents, size_t size, size_t nmemb, void *userp) +{ + size_t realsize = size * nmemb; + AbstractChunk *chunk = (AbstractChunk *)userp; + + if(chunk->stateManager.State() == REQUEST_ABORT) + return 0; + + block_t *block = AllocBlock(realsize); + + memcpy(block->data, contents, realsize); + chunk->blockStream.PushBack(block); + + chunk->bytesDownloaded += realsize; +// chunk->NotifyDownloadRateChanged(); + + return realsize; +} +size_t AbstractChunk::CurlDebugCallback (CURL *url, curl_infotype infoType, char * data, size_t length, void *userdata) +{ + AbstractChunk *chunk = (AbstractChunk *)userdata; + + switch (infoType) { + case CURLINFO_TEXT: + break; + case CURLINFO_HEADER_OUT: + chunk->HandleHeaderOutCallback(); + break; + case CURLINFO_HEADER_IN: + chunk->HandleHeaderInCallback(std::string(data)); + break; + case CURLINFO_DATA_IN: + break; + default: + return 0; + } + return 0; +} +void AbstractChunk::HandleHeaderOutCallback () +{ + HTTPTransaction *httpTransaction = new HTTPTransaction(); + + httpTransaction->SetOriginalUrl(this->AbsoluteURI()); + httpTransaction->SetRange(this->Range()); + httpTransaction->SetType(this->GetType()); + httpTransaction->SetRequestSentTime(Time::GetCurrentUTCTimeStr()); + + this->httpTransactions.push_back(httpTransaction); +} +void AbstractChunk::HandleHeaderInCallback (std::string data) +{ + HTTPTransaction *httpTransaction = this->httpTransactions.at(this->httpTransactions.size()-1); + + if (data.substr(0,4) == "HTTP") + { + httpTransaction->SetResponseReceivedTime(Time::GetCurrentUTCTimeStr()); + httpTransaction->SetResponseCode(strtoul(data.substr(9,3).c_str(), NULL, 10)); + } + + httpTransaction->AddHTTPHeaderLine(data); +} +const std::vector& AbstractChunk::GetTCPConnectionList () const +{ + return (std::vector &) this->tcpConnections; +} +const std::vector& AbstractChunk::GetHTTPTransactionList () const +{ + return (std::vector &) this->httpTransactions; +} diff --git a/libdash/source/network/AbstractChunk.h b/libdash/source/network/AbstractChunk.h new file mode 100644 index 00000000..794469fd --- /dev/null +++ b/libdash/source/network/AbstractChunk.h @@ -0,0 +1,100 @@ +/* + * AbstractChunk.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef ABSTRACTCHUNK_H_ +#define ABSTRACTCHUNK_H_ + +#include "config.h" + +#include "IDownloadableChunk.h" +#include "DownloadStateManager.h" +#include "../helpers/SyncedBlockStream.h" +#include "../portable/Networking.h" +#include +#include "../metrics/HTTPTransaction.h" +#include "../metrics/TCPConnection.h" +#include "../metrics/ThroughputMeasurement.h" +#include "../helpers/Time.h" + +#include + +namespace dash +{ + namespace network + { + class AbstractChunk : public virtual IDownloadableChunk + { + public: + AbstractChunk (); + virtual ~AbstractChunk (); + + /* + * Pure virtual IChunk Interface + */ + virtual std::string& AbsoluteURI () = 0; + virtual std::string& Host () = 0; + virtual size_t Port () = 0; + virtual std::string& Path () = 0; + virtual std::string& Range () = 0; + virtual size_t StartByte () = 0; + virtual size_t EndByte () = 0; + virtual bool HasByteRange () = 0; + virtual dash::metrics::HTTPTransactionType GetType() = 0; + /* + * IDownloadableChunk Interface + */ + virtual bool StartDownload (IConnection *connection); + virtual bool StartDownload (); + virtual void AbortDownload (); + virtual int Read (uint8_t *data, size_t len); + virtual int Peek (uint8_t *data, size_t len); + virtual int Peek (uint8_t *data, size_t len, size_t offset); + virtual void AttachDownloadObserver (IDownloadObserver *observer); + virtual void DetachDownloadObserver (IDownloadObserver *observer); + /* + * Observer Notification + */ + void NotifyDownloadRateChanged (double bitrate); + void NotifyDownloadTimeChanged (double dnltime); + /* + * IDASHMetrics + */ + const std::vector& GetTCPConnectionList () const; + const std::vector& GetHTTPTransactionList () const; + + private: + std::vector observers; + THREAD_HANDLE dlThread; + IConnection *connection; + helpers::SyncedBlockStream blockStream; + CURL *curl; + CURLM *curlm; + CURLcode response; + uint64_t bytesDownloaded; + DownloadStateManager stateManager; + + std::vector tcpConnections; + std::vector httpTransactions; + + static uint32_t BLOCKSIZE; + + static void* DownloadExternalConnection (void *chunk); + static void* DownloadInternalConnection (void *chunk); + static size_t CurlResponseCallback (void *contents, size_t size, size_t nmemb, void *userp); + static size_t CurlHeaderCallback (void *headerData, size_t size, size_t nmemb, void *userdata); + static size_t CurlDebugCallback (CURL *url, curl_infotype infoType, char * data, size_t length, void *userdata); + void HandleHeaderOutCallback (); + void HandleHeaderInCallback (std::string data); + }; + } +} + +#endif /* ABSTRACTCHUNK_H_ */ diff --git a/libdash/source/network/DownloadStateManager.cpp b/libdash/source/network/DownloadStateManager.cpp new file mode 100644 index 00000000..5117c099 --- /dev/null +++ b/libdash/source/network/DownloadStateManager.cpp @@ -0,0 +1,100 @@ +/* + * DownloadStateManager.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "DownloadStateManager.h" + +using namespace dash::network; + +DownloadStateManager::DownloadStateManager () : + state (NOT_STARTED) +{ + InitializeConditionVariable (&this->stateChanged); + InitializeCriticalSection (&this->stateLock); +} +DownloadStateManager::~DownloadStateManager () +{ + DeleteConditionVariable (&this->stateChanged); + DeleteCriticalSection (&this->stateLock); +} + +DownloadState DownloadStateManager::State () const +{ + EnterCriticalSection(&this->stateLock); + + DownloadState ret = this->state; + + LeaveCriticalSection(&this->stateLock); + + return ret; +} +void DownloadStateManager::State (DownloadState state) +{ + EnterCriticalSection(&this->stateLock); + + this->state = state; + + this->Notify(); + WakeAllConditionVariable(&this->stateChanged); + LeaveCriticalSection(&this->stateLock); +} +void DownloadStateManager::WaitState (DownloadState state) const +{ + EnterCriticalSection(&this->stateLock); + + while(this->state != state) + SleepConditionVariableCS(&this->stateChanged, &this->stateLock, INFINITE); + + LeaveCriticalSection(&this->stateLock); +} +void DownloadStateManager::CheckAndWait (DownloadState check, DownloadState wait) const +{ + EnterCriticalSection(&this->stateLock); + + if(this->state == check) + while(this->state != wait) + SleepConditionVariableCS(&this->stateChanged, &this->stateLock, INFINITE); + + LeaveCriticalSection(&this->stateLock); +} +void DownloadStateManager::Attach (IDownloadObserver *observer) +{ + EnterCriticalSection(&this->stateLock); + this->observers.push_back(observer); + LeaveCriticalSection(&this->stateLock); +} +void DownloadStateManager::Detach (IDownloadObserver *observer) +{ + EnterCriticalSection(&this->stateLock); + + uint32_t pos = -1; + + for(size_t i = 0; i < this->observers.size(); i++) + if(this->observers.at(i) == observer) + pos = i; + + if(pos != -1) + this->observers.erase(this->observers.begin() + pos); + + LeaveCriticalSection(&this->stateLock); +} +void DownloadStateManager::Notify () +{ + for(size_t i = 0; i < this->observers.size(); i++) + this->observers.at(i)->OnDownloadStateChanged(this->state); +} +void DownloadStateManager::CheckAndSet (DownloadState check, DownloadState set) +{ + EnterCriticalSection(&this->stateLock); + + if(this->state == check) + this->state = set; + LeaveCriticalSection(&this->stateLock); +} diff --git a/libdash/source/network/DownloadStateManager.h b/libdash/source/network/DownloadStateManager.h new file mode 100644 index 00000000..90dd770d --- /dev/null +++ b/libdash/source/network/DownloadStateManager.h @@ -0,0 +1,50 @@ +/* + * DownloadStateManager.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef DOWNLOADSTATEMANAGER_H_ +#define DOWNLOADSTATEMANAGER_H_ + +#include "config.h" + +#include "IDownloadObserver.h" +#include "../portable/MultiThreading.h" + +namespace dash +{ + namespace network + { + class DownloadStateManager + { + public: + DownloadStateManager (); + virtual ~DownloadStateManager (); + + DownloadState State () const; + void WaitState (DownloadState state) const; + void CheckAndWait (DownloadState check, DownloadState wait) const; + void CheckAndSet (DownloadState check, DownloadState set); + void State (DownloadState state); + void Attach (IDownloadObserver *observer); + void Detach (IDownloadObserver *observer); + + private: + DownloadState state; + mutable CRITICAL_SECTION stateLock; + mutable CONDITION_VARIABLE stateChanged; + + std::vector observers; + + void Notify (); + }; + } +} + +#endif /* DOWNLOADSTATEMANAGER_H_ */ diff --git a/libdash/source/portable/MultiThreading.cpp b/libdash/source/portable/MultiThreading.cpp new file mode 100644 index 00000000..5d1fe9c0 --- /dev/null +++ b/libdash/source/portable/MultiThreading.cpp @@ -0,0 +1,112 @@ +#include "MultiThreading.h" + +THREAD_HANDLE CreateThreadPortable (void *(*start_routine) (void *), void *arg) +{ + #if defined _WIN32 || defined _WIN64 + return CreateThread (0, 0, (LPTHREAD_START_ROUTINE)start_routine, (LPVOID)arg, 0, 0); + #else + THREAD_HANDLE th = (THREAD_HANDLE)malloc(sizeof(pthread_t)); + + if (!th) + { + std::cerr << "Error allocating thread." << std::endl; + return NULL; + } + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + if(int err = pthread_create(th, &attr, start_routine, arg)) + { + std::cerr << strerror(err) << std::endl; + return NULL; + } + return th; + #endif +} +void DestroyThreadPortable (THREAD_HANDLE th) +{ + #if !defined _WIN32 && !defined _WIN64 + if(th) + free(th); + #endif +} + +/**************************************************************************** +* Condition variables for Windows XP and older windows sytems +*****************************************************************************/ +#if defined WINXPOROLDER + void InitCondition (condition_variable_t *cv) + { + InitializeCriticalSection(&cv->waitersCountLock); + + cv->waitersCount = 0; + cv->waitGenerationCount = 0; + cv->releaseCount = 0; + + cv->waitingEvent = CreateEvent (NULL, // no security + TRUE, // manual-reset + FALSE, // non-signaled initially + NULL); // unnamed + } + void WaitCondition (condition_variable_t *cv, CRITICAL_SECTION *externalMutex) + { + EnterCriticalSection(&cv->waitersCountLock); + + cv->waitersCount++; + + int currentGenerationCount = cv->waitGenerationCount; + + LeaveCriticalSection(&cv->waitersCountLock); + LeaveCriticalSection(externalMutex); + + bool isWaitDone = false; + while(!isWaitDone) + { + WaitForSingleObject (cv->waitingEvent, INFINITE); + EnterCriticalSection (&cv->waitersCountLock); + + isWaitDone = (cv->releaseCount > 0 && cv->waitGenerationCount != currentGenerationCount); + LeaveCriticalSection (&cv->waitersCountLock); + } + + EnterCriticalSection(externalMutex); + EnterCriticalSection(&cv->waitersCountLock); + + cv->waitersCount--; + cv->releaseCount--; + bool isLastWaiter = (cv->releaseCount == 0); + + LeaveCriticalSection(&cv->waitersCountLock); + + if(isLastWaiter) + ResetEvent(cv->waitingEvent); + } + void SignalCondition (condition_variable_t *cv) + { + EnterCriticalSection(&cv->waitersCountLock); + + if(cv->waitersCount > cv->releaseCount) + { + SetEvent(cv->waitingEvent); + cv->releaseCount++; + cv->waitGenerationCount++; + } + + LeaveCriticalSection(&cv->waitersCountLock); + } + void BroadcastCondition (condition_variable_t *cv) + { + EnterCriticalSection(&cv->waitersCountLock); + + if(cv->waitersCount > 0) + { + SetEvent(cv->waitingEvent); + cv->releaseCount = cv->waitersCount; + cv->waitGenerationCount++; + } + + LeaveCriticalSection(&cv->waitersCountLock); +} +#endif diff --git a/libdash/source/portable/MultiThreading.h b/libdash/source/portable/MultiThreading.h new file mode 100644 index 00000000..c1b45f5c --- /dev/null +++ b/libdash/source/portable/MultiThreading.h @@ -0,0 +1,70 @@ +#ifndef PORTABLE_MULTITHREADING_H_ +#define PORTABLE_MULTITHREADING_H_ + +#if defined _WIN32 || defined _WIN64 + + #define _WINSOCKAPI_ + #include + #define DeleteConditionVariable(cond_p) {} + + typedef HANDLE THREAD_HANDLE; + + #if defined WINXPOROLDER + /**************************************************************************** + * Variables + *****************************************************************************/ + struct condition_variable_t + { + int waitersCount; // Count of the number of waiters. + CRITICAL_SECTION waitersCountLock; // Serialize access to . + int releaseCount; // Number of threads to release via a or a . + int waitGenerationCount; // Keeps track of the current "generation" so that we don't allow one thread to steal all the "releases" from the broadcast. + HANDLE waitingEvent; // A manual-reset event that's used to block and release waiting threads. + }; + /**************************************************************************** + * Prototypes + *****************************************************************************/ + void InitCondition (condition_variable_t *cv); + void WaitCondition (condition_variable_t *cv, CRITICAL_SECTION *externalMutex); + void SignalCondition (condition_variable_t *cv); + void BroadcastCondition (condition_variable_t *cv); + /**************************************************************************** + * Defines + *****************************************************************************/ + #define CONDITION_VARIABLE condition_variable_t + + #define InitializeConditionVariable(cond_p) InitCondition(cond_p) + #define SleepConditionVariableCS(cond_p, mutex_p, infinite) WaitCondition(cond_p, mutex_p) // INFINITE should be handled mor properly + #define WakeConditionVariable(cond_p) SignalCondition(cond_p) + #define WakeAllConditionVariable(cond_p) BroadcastCondition(cond_p) + #endif + +#else + + #include + #include + #include + #include + #include + + #define CRITICAL_SECTION pthread_mutex_t + #define CONDITION_VARIABLE pthread_cond_t + + #define InitializeCriticalSection(mutex_p) pthread_mutex_init(mutex_p, NULL) + #define DeleteCriticalSection(mutex_p) pthread_mutex_destroy(mutex_p) + #define EnterCriticalSection(mutex_p) pthread_mutex_lock(mutex_p) + #define LeaveCriticalSection(mutex_p) pthread_mutex_unlock(mutex_p) + #define InitializeConditionVariable(cond_p) pthread_cond_init(cond_p, NULL) + #define DeleteConditionVariable(cond_p) pthread_cond_destroy(cond_p) + #define SleepConditionVariableCS(cond_p, mutex_p, infinite) pthread_cond_wait(cond_p, mutex_p) // INFINITE should be handled mor properly + #define WakeConditionVariable(cond_p) pthread_cond_signal(cond_p) + #define WakeAllConditionVariable(cond_p) pthread_cond_broadcast(cond_p) + + typedef pthread_t* THREAD_HANDLE; + +#endif + +THREAD_HANDLE CreateThreadPortable (void *(*start_routine) (void *), void *arg); +void DestroyThreadPortable (THREAD_HANDLE th); + +#endif // PORTABLE_MULTITHREADING_H_ \ No newline at end of file diff --git a/libdash/source/portable/Networking.h b/libdash/source/portable/Networking.h new file mode 100644 index 00000000..6239c3a3 --- /dev/null +++ b/libdash/source/portable/Networking.h @@ -0,0 +1,27 @@ +#ifndef PORTABLE_NETWORKING_H_ +#define PORTABLE_NETWORKING_H_ + +#if defined _WIN32 || defined _WIN64 + +#include +#include + +#else + +#include +#include +#include /* superset of previous */ +#include +#include +#include +#include + +#define closesocket(socket) close(socket) +#define WSAStartup(wVersionRequested, lpWSAData) 0 +#define WSACleanup() {} + +typedef unsigned char WSADATA; + +#endif + +#endif // PORTABLE_NETWORKING_H_ diff --git a/libdash/source/sublibs.mk b/libdash/source/sublibs.mk new file mode 100644 index 00000000..69173c3b --- /dev/null +++ b/libdash/source/sublibs.mk @@ -0,0 +1,23 @@ +LIBDIR=../libs +OBJECTS=$(SOURCES:.cpp=.o) +DIRNAME=$(shell basename $(shell pwd)) +LIBRARY=$(LIBDIR)/lib$(DIRNAME).a + +all: $(SOURCES) $(LIBRARY) + +$(LIBRARY): $(OBJECTS) + mkdir -p $(LIBDIR) + rm -f $@ + $(AR) $(ARFLAGS) $@ $(OBJECTS) + +.cpp.o: + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm -f $(OBJECTS) + +distclean: clean + rm -f $(LIBRARY) + if test -d $(LIBDIR); then \ + rmdir --ignore-fail-on-non-empty $(LIBDIR) ; \ + fi diff --git a/libdash/source/targetver.h b/libdash/source/targetver.h new file mode 100644 index 00000000..2adeccf9 --- /dev/null +++ b/libdash/source/targetver.h @@ -0,0 +1,18 @@ +/* + * targetver.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#pragma once + +#if defined _WIN32 || defined _WIN64 + +#include + +#endif \ No newline at end of file diff --git a/libdash/source/xml/DOMHelper.cpp b/libdash/source/xml/DOMHelper.cpp new file mode 100644 index 00000000..43adadc0 --- /dev/null +++ b/libdash/source/xml/DOMHelper.cpp @@ -0,0 +1,54 @@ +/* + * DOMHelper.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "DOMHelper.h" + +using namespace dash::xml; + +std::vector DOMHelper::GetElementByTagName (Node *root, const std::string &name, bool selfContain) +{ + std::vector elements; + + for(unsigned int i = 0; i < root->GetSubNodes().size(); i++) + { + GetElementsByTagName(root->GetSubNodes().at(i), name, &elements, selfContain); + } + + return elements; +} +std::vector DOMHelper::GetChildElementByTagName (Node *root, const std::string &name) +{ + std::vector elements; + + for(unsigned int i = 0; i < root->GetSubNodes().size(); i++) + { + if(!root->GetSubNodes().at(i)->GetName().compare(name)) + elements.push_back(root->GetSubNodes().at(i)); + } + + return elements; +} +void DOMHelper::GetElementsByTagName (Node *root, const std::string &name, std::vector *elements, bool selfContain) +{ + if(!selfContain && !root->GetName().compare(name)) + { + elements->push_back(root); + return; + } + + if(!root->GetName().compare(name)) + elements->push_back(root); + + for(unsigned int i = 0; i < root->GetSubNodes().size(); i++) + { + GetElementsByTagName(root->GetSubNodes().at(i), name, elements, selfContain); + } +} diff --git a/libdash/source/xml/DOMHelper.h b/libdash/source/xml/DOMHelper.h new file mode 100644 index 00000000..d76d425b --- /dev/null +++ b/libdash/source/xml/DOMHelper.h @@ -0,0 +1,35 @@ +/* + * DOMHelper.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef DOMHELPER_H_ +#define DOMHELPER_H_ + +#include "config.h" + +#include "Node.h" + +namespace dash +{ + namespace xml + { + class DOMHelper + { + public: + static std::vector GetElementByTagName (Node *root, const std::string &name, bool selfContain); + static std::vector GetChildElementByTagName (Node *root, const std::string &name); + + private: + static void GetElementsByTagName(Node *root, const std::string &name, std::vector *elements, bool selfContain); + }; + } +} + +#endif /* DOMHELPER_H_ */ diff --git a/libdash/source/xml/DOMParser.cpp b/libdash/source/xml/DOMParser.cpp new file mode 100644 index 00000000..5feb3850 --- /dev/null +++ b/libdash/source/xml/DOMParser.cpp @@ -0,0 +1,160 @@ +/* + * DOMParser.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "DOMParser.h" + +using namespace dash::xml; +using namespace dash::helpers; + +DOMParser::DOMParser (std::string url) : + url (url), + reader (NULL), + root (NULL) +{ + this->Init(); +} +DOMParser::~DOMParser () +{ + xmlCleanupParser(); + delete(this->root); +} + +Node* DOMParser::GetRootNode () const +{ + return this->root; +} +bool DOMParser::Parse (std::string path) +{ + this->reader = xmlReaderForFile(this->url.c_str(), NULL, 0); + + if(this->reader == NULL) + return false; + + if(xmlTextReaderRead(this->reader)) + this->root = this->ProcessNode(path); + + if(this->root == NULL) + return false; + + xmlFreeTextReader(this->reader); + + return true; +} +Node* DOMParser::ProcessNode (std::string path) +{ + int type = xmlTextReaderNodeType(this->reader); + + if(type != WhiteSpace && type != Text) + { + while (type == Comment || type == WhiteSpace) + { + xmlTextReaderRead(this->reader); + type = xmlTextReaderNodeType(this->reader); + } + + Node *node = new Node(); + node->SetType(type); + if(!(strcmp("",path.c_str()))) + node->SetMPDPath(Path::GetDirectoryPath(url)); + else + node->SetMPDPath(Path::GetDirectoryPath(path)); + if(xmlTextReaderConstName(this->reader) == NULL) + { + delete node; + return NULL; + } + + std::string name = (const char *) xmlTextReaderConstName(this->reader); + int isEmpty = xmlTextReaderIsEmptyElement(this->reader); + + node->SetName(name); + + this->AddAttributesToNode(node); + + if(isEmpty) + return node; + + Node *subnode = NULL; + int ret = xmlTextReaderRead(this->reader); + + while(ret == 1) + { + if(!strcmp(name.c_str(), (const char *) xmlTextReaderConstName(this->reader))) + { + return node; + } + + subnode = this->ProcessNode(path); + + if(subnode != NULL) + node->AddSubNode(subnode); + + ret = xmlTextReaderRead(this->reader); + } + + return node; + } else if (type == Text) + { + const char* text = (const char *) xmlTextReaderReadString(this->reader); + + if(text != NULL) + { + Node *node = new Node(); + node->SetType(type); + node->SetText(text); + return node; + } + } + return NULL; +} +void DOMParser::AddAttributesToNode (Node *node) +{ + if(xmlTextReaderHasAttributes(this->reader)) + { + while(xmlTextReaderMoveToNextAttribute(this->reader)) + { + std::string key = (const char *) xmlTextReaderConstName(this->reader); + std::string value = (const char *) xmlTextReaderConstValue(this->reader); + node->AddAttribute(key, value); + } + } +} +void DOMParser::Print (Node *node, int offset) +{ + std::stringstream ss; + for(int i = 0; i < offset; i++) + ss << " "; + ss << node->GetName(); + + std::vector keys = node->GetAttributeKeys(); + + ss.clear(); + for(unsigned int i = 0; i < keys.size(); i++) + { + ss << " " << keys.at(i) << "=" << node->GetAttributeValue(keys.at(i)); + } + + offset++; + + for(unsigned int i = 0; i < node->GetSubNodes().size(); i++) + { + this->Print(node->GetSubNodes().at(i), offset); + } +} +void DOMParser::Init () +{ + this->root = NULL; + this->reader = NULL; +} +void DOMParser::Print () +{ + this->Print(this->root, 0); +} diff --git a/libdash/source/xml/DOMParser.h b/libdash/source/xml/DOMParser.h new file mode 100644 index 00000000..a8b3a9b7 --- /dev/null +++ b/libdash/source/xml/DOMParser.h @@ -0,0 +1,56 @@ +/* + * DOMParser.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef DOMPARSER_H_ +#define DOMPARSER_H_ + +#include "config.h" + +#include "Node.h" +#include +#include "../helpers/Path.h" + +namespace dash +{ + namespace xml + { + enum NodeType + { + Start = 1, + End = 15, + Comment = 8, + WhiteSpace = 14, + Text = 3, + }; + + class DOMParser + { + public: + DOMParser (std::string url); + virtual ~DOMParser (); + + bool Parse (std::string path = ""); + Node* GetRootNode () const; + void Print (); + + private: + xmlTextReaderPtr reader; + Node *root; + std::string url; + + void Init (); + Node* ProcessNode (std::string); + void AddAttributesToNode (Node *node); + void Print (Node *node, int offset); + }; + } +} +#endif /* DOMPARSER_H_ */ diff --git a/libdash/source/xml/Node.cpp b/libdash/source/xml/Node.cpp new file mode 100644 index 00000000..d04558b6 --- /dev/null +++ b/libdash/source/xml/Node.cpp @@ -0,0 +1,1029 @@ +/* + * Node.cpp + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#include "Node.h" +#include + +using namespace dash::xml; +using namespace dash::metrics; + +Node::Node () +{ +} +Node::Node (const Node& other) : + name(other.name), + text(other.text), + type(other.type), + attributes(other.attributes) +{ + for (size_t i = 0; i < other.subNodes.size(); i++) + this->subNodes.push_back(new Node(*(other.subNodes.at(i)))); +} +Node::~Node () +{ + for(size_t i = 0; i < this->subNodes.size(); i++) + delete(this->subNodes.at(i)); +} + +dash::mpd::ProgramInformation* Node::ToProgramInformation () const +{ + dash::mpd::ProgramInformation *programInformation = new dash::mpd::ProgramInformation(); + + if (this->HasAttribute("lang")) + { + programInformation->SetLang(this->GetAttributeValue("lang")); + } + if (this->HasAttribute("moreInformationURL")) + { + programInformation->SetMoreInformationURL(this->GetAttributeValue("moreInformationURL")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "Title") + { + programInformation->SetTitle(subNodes.at(i)->GetText()); + continue; + } + if (subNodes.at(i)->GetName() == "Source") + { + programInformation->SetSource(subNodes.at(i)->GetText()); + continue; + } + if (subNodes.at(i)->GetName() == "Copyright") + { + programInformation->SetCopyright(subNodes.at(i)->GetText()); + continue; + } + programInformation->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + programInformation->AddRawAttributes(this->attributes); + return programInformation; +} +dash::mpd::BaseUrl* Node::ToBaseUrl () const +{ + dash::mpd::BaseUrl *baseUrl = new dash::mpd::BaseUrl(); + + if(this->HasAttribute("serviceLocation")) + { + baseUrl->SetServiceLocation(this->GetAttributeValue("serviceLocation")); + } + if(this->HasAttribute("byteRange")) + { + baseUrl->SetByteRange(this->GetAttributeValue("byteRange")); + } + if (this->GetText() == "./") + { + baseUrl->SetUrl(this->mpdPath); + } + else + { + baseUrl->SetUrl(this->GetText()); + } + + baseUrl->AddRawAttributes(this->attributes); + return baseUrl; +} +dash::mpd::Descriptor* Node::ToDescriptor () const +{ + dash::mpd::Descriptor *descriptor = new dash::mpd::Descriptor(); + std::vector subNodes = this->GetSubNodes(); + + if (this->HasAttribute("schemeIdUri")) + { + descriptor->SetSchemeIdUri(this->GetAttributeValue("schemeIdUri")); + } + if (this->HasAttribute("value")) + { + descriptor->SetValue(this->GetAttributeValue("value")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + descriptor->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + descriptor->AddRawAttributes(this->attributes); + return descriptor; +} +dash::mpd::ContentComponent* Node::ToContentComponent () const +{ + dash::mpd::ContentComponent *contentComponent = new dash::mpd::ContentComponent(); + std::vector subNodes = this->GetSubNodes(); + + if (this->HasAttribute("id")) + { + contentComponent->SetId(strtoul(this->GetAttributeValue("id").c_str(), NULL, 10)); + } + if (this->HasAttribute("lang")) + { + contentComponent->SetLang(this->GetAttributeValue("lang")); + } + if (this->HasAttribute("contentType")) + { + contentComponent->SetContentType(this->GetAttributeValue("contentType")); + } + if (this->HasAttribute("par")) + { + contentComponent->SetPar(this->GetAttributeValue("par")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "Accessibility") + { + contentComponent->AddAccessibity(subNodes.at(i)->ToDescriptor()); + continue; + } + if (subNodes.at(i)->GetName() == "Role") + { + contentComponent->AddRole(subNodes.at(i)->ToDescriptor()); + continue; + } + if (subNodes.at(i)->GetName() == "Rating") + { + contentComponent->AddRating(subNodes.at(i)->ToDescriptor()); + continue; + } + if (subNodes.at(i)->GetName() == "Viewpoint") + { + contentComponent->AddViewpoint(subNodes.at(i)->ToDescriptor()); + continue; + } + contentComponent->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + contentComponent->AddRawAttributes(this->attributes); + return contentComponent; +} +dash::mpd::URLType* Node::ToURLType (HTTPTransactionType type) const +{ + dash::mpd::URLType* urlType = new dash::mpd::URLType(); + + if (this->HasAttribute("sourceURL")) + { + urlType->SetSourceURL(this->GetAttributeValue("sourceURL")); + } + if (this->HasAttribute("range")) + { + urlType->SetRange(this->GetAttributeValue("range")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + urlType->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + urlType->SetType(type); + urlType->AddRawAttributes(this->attributes); + return urlType; +} +dash::mpd::SegmentBase* Node::ToSegmentBase () const +{ + dash::mpd::SegmentBase* segmentBase = new dash::mpd::SegmentBase(); + std::vector subNodes = this->GetSubNodes(); + + SetCommonValuesForSeg(*segmentBase); + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() != "Initialization" && subNodes.at(i)->GetName() != "RepresentationIndex") + segmentBase->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + segmentBase->AddRawAttributes(this->attributes); + return segmentBase; +} +dash::mpd::Timeline* Node::ToTimeline () const +{ + dash::mpd::Timeline* timeline = new dash::mpd::Timeline(); + + if (this->HasAttribute("t")) + { + timeline->SetStartTime(strtoul(this->GetAttributeValue("t").c_str(), NULL, 10)); + } + if (this->HasAttribute("d")) + { + timeline->SetDuration(strtoul(this->GetAttributeValue("d").c_str(), NULL, 10)); + } + if (this->HasAttribute("r")) + { + timeline->SetRepeatCount(strtoul(this->GetAttributeValue("r").c_str(), NULL, 10)); + } + + timeline->AddRawAttributes(this->attributes); + return timeline; +} +dash::mpd::SegmentTimeline* Node::ToSegmentTimeline () const +{ + dash::mpd::SegmentTimeline* segmentTimeline = new dash::mpd::SegmentTimeline(); + + std::vector subNodes = this->GetSubNodes(); + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "S") + { + segmentTimeline->AddTimeline(subNodes.at(i)->ToTimeline()); + continue; + } + segmentTimeline->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + segmentTimeline->AddRawAttributes(this->attributes); + return segmentTimeline; +} +dash::mpd::SegmentURL* Node::ToSegmentURL () const +{ + dash::mpd::SegmentURL *segmentUrl = new dash::mpd::SegmentURL(); + + if (this->HasAttribute("media")) + { + segmentUrl->SetMediaURI(this->GetAttributeValue("media")); + } + if (this->HasAttribute("mediaRange")) + { + segmentUrl->SetMediaRange(this->GetAttributeValue("mediaRange")); + } + if (this->HasAttribute("index")) + { + segmentUrl->SetIndexURI(this->GetAttributeValue("index")); + } + if (this->HasAttribute("indexRange")) + { + segmentUrl->SetIndexRange(this->GetAttributeValue("indexRange")); + } + if(this->HasAttribute("size")) + { + segmentUrl->SetBitrate(this->GetAttributeValue("size")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + segmentUrl->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + segmentUrl->AddRawAttributes(this->attributes); + return segmentUrl; +} +dash::mpd::SegmentList* Node::ToSegmentList () const +{ + dash::mpd::SegmentList* segmentList = new dash::mpd::SegmentList(); + std::vector subNodes = this->GetSubNodes(); + + SetCommonValuesForMSeg(*segmentList); + + if (this->HasAttribute("xlink:href")) + { + segmentList->SetXlinkHref(this->GetAttributeValue("xlink:href")); + } + if (this->HasAttribute("xlink:actuate")) + { + segmentList->SetXlinkActuate(this->GetAttributeValue("xlink:actuate")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "SegmentURL") + { + segmentList->AddSegmentURL(subNodes.at(i)->ToSegmentURL()); + continue; + } + if (subNodes.at(i)->GetName() != "SegmentTimeline" && subNodes.at(i)->GetName() != "BitstreamSwitching" && + subNodes.at(i)->GetName() != "Initialization" && subNodes.at(i)->GetName() != "RepresentationIndex") + segmentList->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + segmentList->AddRawAttributes(this->attributes); + return segmentList; +} +dash::mpd::SegmentTemplate* Node::ToSegmentTemplate () const +{ + dash::mpd::SegmentTemplate *segmentTemplate = new dash::mpd::SegmentTemplate(); + std::vector subNodes = this->GetSubNodes(); + + SetCommonValuesForMSeg(*segmentTemplate); + + if (this->HasAttribute("media")) + { + segmentTemplate->SetMedia(this->GetAttributeValue("media")); + } + if (this->HasAttribute("index")) + { + segmentTemplate->SetIndex(this->GetAttributeValue("index")); + } + if (this->HasAttribute("initialization")) + { + segmentTemplate->SetInitialization(this->GetAttributeValue("initialization")); + } + if (this->HasAttribute("bitstreamSwitching")) + { + segmentTemplate->SetBitstreamSwitching(this->GetAttributeValue("bitstreamSwitching")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() != "SegmentTimeline" && subNodes.at(i)->GetName() != "BitstreamSwitching" && + subNodes.at(i)->GetName() != "Initialization" && subNodes.at(i)->GetName() != "RepresentationIndex") + segmentTemplate->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + segmentTemplate->AddRawAttributes(this->attributes); + return segmentTemplate; +} +dash::mpd::SubRepresentation* Node::ToSubRepresentation () const +{ + dash::mpd::SubRepresentation* subRepresentation = new dash::mpd::SubRepresentation(); + std::vector subNodes = this->GetSubNodes(); + + SetCommonValuesForRep(*subRepresentation); + + if (this->HasAttribute("level")) + { + subRepresentation->SetLevel(strtoul(this->GetAttributeValue("level").c_str(), NULL, 10)); + } + if (this->HasAttribute("dependencyLevel")) + { + subRepresentation->SetDependencyLevel(this->GetAttributeValue("dependencyLevel")); + } + if (this->HasAttribute("bandwidth")) + { + subRepresentation->SetBandWidth(strtoul(this->GetAttributeValue("bandwidth").c_str(), NULL, 10)); + } + if (this->HasAttribute("contentComponent")) + { + subRepresentation->SetContentComponent(this->GetAttributeValue("contentComponent")); + } + for (size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() != "FramePacking" && subNodes.at(i)->GetName() != "AudioChannelConfiguration" && subNodes.at(i)->GetName() != "ContentProtection") + subRepresentation->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + subRepresentation->AddRawAttributes(this->attributes); + return subRepresentation; +} +dash::mpd::Representation* Node::ToRepresentation () const +{ + dash::mpd::Representation* representation = new dash::mpd::Representation(); + std::vector subNodes = this->GetSubNodes(); + + SetCommonValuesForRep(*representation); + + if (this->HasAttribute("id")) + { + representation->SetId(this->GetAttributeValue("id")); + } + if (this->HasAttribute("bandwidth")) + { + representation->SetBandwidth(strtoul(this->GetAttributeValue("bandwidth").c_str(), NULL, 10)); + } + if (this->HasAttribute("qualityRanking")) + { + representation->SetQualityRanking(strtoul(this->GetAttributeValue("qualityRanking").c_str(), NULL, 10)); + } + if (this->HasAttribute("dependencyId")) + { + representation->SetDependencyId(this->GetAttributeValue("dependencyId")); + } + if (this->HasAttribute("mediaStreamStructureId")) + { + representation->SetMediaStreamStructureId(this->GetAttributeValue("mediaStreamStructureId")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "BaseURL") + { + representation->AddBaseURL(subNodes.at(i)->ToBaseUrl()); + continue; + } + if (subNodes.at(i)->GetName() == "SubRepresentation") + { + representation->AddSubRepresentation(subNodes.at(i)->ToSubRepresentation()); + continue; + } + if (subNodes.at(i)->GetName() == "SegmentBase") + { + representation->SetSegmentBase(subNodes.at(i)->ToSegmentBase()); + continue; + } + if (subNodes.at(i)->GetName() == "SegmentList") + { + representation->SetSegmentList(subNodes.at(i)->ToSegmentList()); + continue; + } + if (subNodes.at(i)->GetName() == "SegmentTemplate") + { + representation->SetSegmentTemplate(subNodes.at(i)->ToSegmentTemplate()); + continue; + } + if (subNodes.at(i)->GetName() != "FramePacking" && subNodes.at(i)->GetName() != "AudioChannelConfiguration" && subNodes.at(i)->GetName() != "ContentProtection") + representation->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + representation->AddRawAttributes(this->attributes); + return representation; +} +dash::mpd::AdaptationSet* Node::ToAdaptationSet () const +{ + dash::mpd::AdaptationSet *adaptationSet = new dash::mpd::AdaptationSet(); + std::vector subNodes = this->GetSubNodes(); + + SetCommonValuesForRep(*adaptationSet); + + if (this->HasAttribute("xlink:href")) + { + adaptationSet->SetXlinkHref(this->GetAttributeValue("xlink:href")); + } + if (this->HasAttribute("xlink:actuate")) + { + adaptationSet->SetXlinkActuate(this->GetAttributeValue("xlink:actuate")); + } + if (this->HasAttribute("id")) + { + adaptationSet->SetId(strtoul(this->GetAttributeValue("id").c_str(), NULL, 10)); + } + if (this->HasAttribute("group")) + { + adaptationSet->SetGroup(strtoul(this->GetAttributeValue("group").c_str(), NULL, 10)); + } + if (this->HasAttribute("lang")) + { + adaptationSet->SetLang(this->GetAttributeValue("lang")); + } + if (this->HasAttribute("contentType")) + { + adaptationSet->SetContentType(this->GetAttributeValue("contentType")); + } + if (this->HasAttribute("par")) + { + adaptationSet->SetPar(this->GetAttributeValue("par")); + } + if (this->HasAttribute("minBandwidth")) + { + adaptationSet->SetMinBandwidth(strtoul(this->GetAttributeValue("minBandwidth").c_str(), NULL, 10)); + } + if (this->HasAttribute("maxBandwidth")) + { + adaptationSet->SetMaxBandwidth(strtoul(this->GetAttributeValue("maxBandwidth").c_str(), NULL, 10)); + } + if (this->HasAttribute("minWidth")) + { + adaptationSet->SetMinWidth(strtoul(this->GetAttributeValue("minWidth").c_str(), NULL, 10)); + } + if (this->HasAttribute("maxWidth")) + { + adaptationSet->SetMaxWidth(strtoul(this->GetAttributeValue("maxWidth").c_str(), NULL, 10)); + } + if (this->HasAttribute("minHeight")) + { + adaptationSet->SetMinHeight(strtoul(this->GetAttributeValue("minHeight").c_str(), NULL, 10)); + } + if (this->HasAttribute("maxHeight")) + { + adaptationSet->SetMaxHeight(strtoul(this->GetAttributeValue("maxHeight").c_str(), NULL, 10)); + } + if (this->HasAttribute("minFrameRate")) + { + adaptationSet->SetMinFramerate(this->GetAttributeValue("minFrameRate")); + } + if (this->HasAttribute("maxFrameRate")) + { + adaptationSet->SetMaxFramerate(this->GetAttributeValue("maxFrameRate")); + } + if (this->HasAttribute("segmentAlignment")) + { + adaptationSet->SetSegmentAlignment(this->GetAttributeValue("segmentAlignment")); + } + if (this->HasAttribute("subsegmentAlignment")) + { + adaptationSet->SetSubsegmentAlignment(this->GetAttributeValue("subsegmentAlignment")); + } + if (this->HasAttribute("subsegmentStartsWithSAP")) + { + adaptationSet->SetMaxHeight((uint8_t) strtoul(this->GetAttributeValue("subsegmentStartsWithSAP").c_str(), NULL, 10)); + } + if (this->HasAttribute("bitstreamSwitching")) + { + adaptationSet->SetBitstreamSwitching(dash::helpers::String::ToBool(this->GetAttributeValue("bitstreamSwitching"))); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "Accessibility") + { + adaptationSet->AddAccessibity(subNodes.at(i)->ToDescriptor()); + continue; + } + if (subNodes.at(i)->GetName() == "Role") + { + adaptationSet->AddRole(subNodes.at(i)->ToDescriptor()); + continue; + } + if (subNodes.at(i)->GetName() == "Rating") + { + adaptationSet->AddRating(subNodes.at(i)->ToDescriptor()); + continue; + } + if (subNodes.at(i)->GetName() == "Viewpoint") + { + adaptationSet->AddViewpoint(subNodes.at(i)->ToDescriptor()); + continue; + } + if (subNodes.at(i)->GetName() == "ContentComponent") + { + adaptationSet->AddContentComponent(subNodes.at(i)->ToContentComponent()); + continue; + } + if (subNodes.at(i)->GetName() == "BaseURL") + { + adaptationSet->AddBaseURL(subNodes.at(i)->ToBaseUrl()); + continue; + } + if (subNodes.at(i)->GetName() == "SegmentBase") + { + adaptationSet->SetSegmentBase(subNodes.at(i)->ToSegmentBase()); + continue; + } + if (subNodes.at(i)->GetName() == "SegmentList") + { + adaptationSet->SetSegmentList(subNodes.at(i)->ToSegmentList()); + continue; + } + if (subNodes.at(i)->GetName() == "SegmentTemplate") + { + adaptationSet->SetSegmentTemplate(subNodes.at(i)->ToSegmentTemplate()); + continue; + } + if (subNodes.at(i)->GetName() == "Representation") + { + adaptationSet->AddRepresentation(subNodes.at(i)->ToRepresentation()); + continue; + } + if (subNodes.at(i)->GetName() != "FramePacking" && subNodes.at(i)->GetName() != "AudioChannelConfiguration" && subNodes.at(i)->GetName() != "ContentProtection") + adaptationSet->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + adaptationSet->AddRawAttributes(this->attributes); + return adaptationSet; +} +dash::mpd::Subset* Node::ToSubset () const +{ + dash::mpd::Subset *subset = new dash::mpd::Subset(); + + if (this->HasAttribute("contains")) + { + subset->SetSubset(this->GetAttributeValue("contains")); + } + + subset->AddRawAttributes(this->attributes); + return subset; +} +dash::mpd::Period* Node::ToPeriod () const +{ + dash::mpd::Period *period = new dash::mpd::Period(); + std::vector subNodes = this->GetSubNodes(); + + if (this->HasAttribute("xlink:href")) + { + period->SetXlinkHref(this->GetAttributeValue("xlink:href")); + } + if (this->HasAttribute("xlink:actuate")) + { + period->SetXlinkActuate(this->GetAttributeValue("xlink:actuate")); + } + if (this->HasAttribute("id")) + { + period->SetId(this->GetAttributeValue("id")); + } + if (this->HasAttribute("start")) + { + period->SetStart(this->GetAttributeValue("start")); + } + if (this->HasAttribute("duration")) + { + period->SetDuration(this->GetAttributeValue("duration")); + } + if (this->HasAttribute("bitstreamSwitching")) + { + period->SetBitstreamSwitching(dash::helpers::String::ToBool(this->GetAttributeValue("bitstreamSwitching"))); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "BaseURL") + { + period->AddBaseURL(subNodes.at(i)->ToBaseUrl()); + continue; + } + if (subNodes.at(i)->GetName() == "AdaptationSet") + { + period->AddAdaptationSet(subNodes.at(i)->ToAdaptationSet()); + continue; + } + if (subNodes.at(i)->GetName() == "Subset") + { + period->AddSubset(subNodes.at(i)->ToSubset()); + continue; + } + if (subNodes.at(i)->GetName() == "SegmentBase") + { + period->SetSegmentBase(subNodes.at(i)->ToSegmentBase()); + continue; + } + if (subNodes.at(i)->GetName() == "SegmentList") + { + period->SetSegmentList(subNodes.at(i)->ToSegmentList()); + continue; + } + if (subNodes.at(i)->GetName() == "SegmentTemplate") + { + period->SetSegmentTemplate(subNodes.at(i)->ToSegmentTemplate()); + continue; + } + period->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + period->AddRawAttributes(this->attributes); + return period; +} +dash::mpd::Range* Node::ToRange () const +{ + dash::mpd::Range* range = new dash::mpd::Range(); + + if (this->HasAttribute("starttime")) + { + range->SetStarttime(this->GetAttributeValue("starttime")); + } + if (this->HasAttribute("duration")) + { + range->SetDuration(this->GetAttributeValue("duration")); + } + + return range; +} +dash::mpd::Metrics* Node::ToMetrics () const +{ + dash::mpd::Metrics* metrics = new dash::mpd::Metrics(); + + if (this->HasAttribute("metrics")) + { + metrics->SetMetrics(this->GetAttributeValue("metrics")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "Reporting") + { + metrics->AddReporting(subNodes.at(i)->ToDescriptor()); + continue; + } + if (subNodes.at(i)->GetName() == "Range") + { + metrics->AddRange(subNodes.at(i)->ToRange()); + continue; + } + metrics->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + metrics->AddRawAttributes(this->attributes); + return metrics; +} +dash::mpd::MPD* Node::ToMPD () const +{ + dash::mpd::MPD *mpd = new dash::mpd::MPD(); + std::vector subNodes = this->GetSubNodes(); + + if (this->HasAttribute("id")) + { + mpd->SetId(this->GetAttributeValue("id")); + } + if (this->HasAttribute("profiles")) + { + mpd->SetProfiles(this->GetAttributeValue("profiles")); + } + if (this->HasAttribute("type")) + { + mpd->SetType(this->GetAttributeValue("type")); + } + if (this->HasAttribute("availabilityStartTime")) + { + mpd->SetAvailabilityStarttime(this->GetAttributeValue("availabilityStartTime")); + } + if (this->HasAttribute("availabilityEndTime")) + { + mpd->SetAvailabilityEndtime(this->GetAttributeValue("availabilityEndTime")); + } + if (this->HasAttribute("mediaPresentationDuration")) + { + mpd->SetMediaPresentationDuration(this->GetAttributeValue("mediaPresentationDuration")); + } + if (this->HasAttribute("minimumUpdatePeriod")) + { + mpd->SetMinimumUpdatePeriod(this->GetAttributeValue("minimumUpdatePeriod")); + } + if (this->HasAttribute("minBufferTime")) + { + mpd->SetMinBufferTime(this->GetAttributeValue("minBufferTime")); + } + if (this->HasAttribute("timeShiftBufferDepth")) + { + mpd->SetTimeShiftBufferDepth(this->GetAttributeValue("timeShiftBufferDepth")); + } + if (this->HasAttribute("suggestedPresentationDelay")) + { + mpd->SetSuggestedPresentationDelay(this->GetAttributeValue("suggestedPresentationDelay")); + } + if (this->HasAttribute("maxSegmentDuration")) + { + mpd->SetMaxSegmentDuration(this->GetAttributeValue("maxSegmentDuration")); + } + if (this->HasAttribute("maxSubsegmentDuration")) + { + mpd->SetMaxSubsegmentDuration(this->GetAttributeValue("maxSubsegmentDuration")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "ProgramInformation") + { + mpd->AddProgramInformation(subNodes.at(i)->ToProgramInformation()); + continue; + } + if (subNodes.at(i)->GetName() == "BaseURL") + { + mpd->AddBaseUrl(subNodes.at(i)->ToBaseUrl()); + continue; + } + if (subNodes.at(i)->GetName() == "Location") + { + mpd->AddLocation(subNodes.at(i)->GetText()); + continue; + } + if (subNodes.at(i)->GetName() == "Period") + { + mpd->AddPeriod(subNodes.at(i)->ToPeriod()); + continue; + } + if (subNodes.at(i)->GetName() == "Metrics") + { + mpd->AddMetrics(subNodes.at(i)->ToMetrics()); + continue; + } + mpd->AddAdditionalSubNode((xml::INode *) new Node(*(subNodes.at(i)))); + } + + dash::mpd::BaseUrl *mpdPathBaseUrl = new dash::mpd::BaseUrl(); + mpdPathBaseUrl->SetUrl(mpdPath); + mpd->SetMPDPathBaseUrl(mpdPathBaseUrl); + + mpd->AddRawAttributes(this->attributes); + return mpd; +} +void Node::SetMPDPath (std::string path) +{ + this->mpdPath = path; +} + +const std::vector& Node::GetNodes () const +{ + return (std::vector &) this->subNodes; +} +const std::vector& Node::GetSubNodes () const +{ + return this->subNodes; +} +void Node::AddSubNode (Node *node) +{ + this->subNodes.push_back(node); +} +const std::string& Node::GetName () const +{ + return this->name; +} +void Node::SetName (const std::string &name) +{ + this->name = name; +} +const std::string& Node::GetAttributeValue (std::string key) const +{ + //return this->attributes[key]; + return this->attributes.find(key)->second; +} +bool Node::HasAttribute (const std::string& name) const +{ + if(this->attributes.find(name) != this->attributes.end()) + return true; + + return false; +} +void Node::AddAttribute (const std::string &key, const std::string &value) +{ + this->attributes[key] = value; +} +std::vector Node::GetAttributeKeys () const +{ + std::vector keys; + std::map::const_iterator it; + + for(it = this->attributes.begin(); it != this->attributes.end(); ++it) + { + keys.push_back(it->first); + } + return keys; +} +bool Node::HasText () const +{ + return false; +} +std::string Node::GetText () const +{ + if(this->type == 3) + return this->text; + else + { + if(this->subNodes.size()) + return this->subNodes[0]->GetText(); + else + return ""; + } +} +void Node::SetText (const std::string &text) +{ + this->text = text; +} +void Node::Print (std::ostream &stream) const +{ + stream << this->name; + std::vector keys = this->GetAttributeKeys(); + for(size_t i = 0; i < keys.size(); i++) + stream << " " << keys.at(i) << "=" << this->GetAttributeValue(keys.at(i)); + + stream << std::endl; +} +const std::map& Node::GetAttributes () const +{ + return this->attributes; +} +int Node::GetType () const +{ + return this->type; +} +void Node::SetType (int type) +{ + this->type = type; +} +void Node::SetCommonValuesForRep (dash::mpd::RepresentationBase& object) const +{ + std::vector subNodes = this->GetSubNodes(); + + if (this->HasAttribute("profiles")) + { + object.SetProfiles(this->GetAttributeValue("profiles")); + } + if (this->HasAttribute("width")) + { + object.SetWidth(strtoul(this->GetAttributeValue("width").c_str(), NULL, 10)); + } + if (this->HasAttribute("height")) + { + object.SetHeight(strtoul(this->GetAttributeValue("height").c_str(), NULL, 10)); + } + if (this->HasAttribute("sar")) + { + object.SetSar(this->GetAttributeValue("sar")); + } + if (this->HasAttribute("frameRate")) + { + object.SetFrameRate(this->GetAttributeValue("frameRate")); + } + if (this->HasAttribute("audioSamplingRate")) + { + object.SetAudioSamplingRate(this->GetAttributeValue("audioSamplingRate")); + } + if (this->HasAttribute("mimeType")) + { + object.SetMimeType(this->GetAttributeValue("mimeType")); + } + if (this->HasAttribute("segmentProfiles")) + { + object.SetSegmentProfiles(this->GetAttributeValue("segmentProfiles")); + } + if (this->HasAttribute("codecs")) + { + object.SetCodecs(this->GetAttributeValue("codecs")); + } + if (this->HasAttribute("maximumSAPPeriod")) + { + object.SetMaximumSAPPeriod(strtod(this->GetAttributeValue("maximumSAPPeriod").c_str(), NULL)); + } + if (this->HasAttribute("startWithSAP")) + { + object.SetStartWithSAP((uint8_t) strtoul(this->GetAttributeValue("startWithSAP").c_str(), NULL, 10)); + } + if (this->HasAttribute("maxPlayoutRate")) + { + object.SetMaxPlayoutRate(strtod(this->GetAttributeValue("maxPlayoutRate").c_str(), NULL)); + } + if (this->HasAttribute("codingDependency")) + { + object.SetCodingDependency(dash::helpers::String::ToBool(this->GetAttributeValue("codingDependency"))); + } + if (this->HasAttribute("scanType")) + { + object.SetScanType(this->GetAttributeValue("scanType")); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "FramePacking") + { + object.AddFramePacking(subNodes.at(i)->ToDescriptor()); + continue; + } + if (subNodes.at(i)->GetName() == "AudioChannelConfiguration") + { + object.AddAudioChannelConfiguration(subNodes.at(i)->ToDescriptor()); + continue; + } + if (subNodes.at(i)->GetName() == "ContentProtection") + { + object.AddContentProtection(subNodes.at(i)->ToDescriptor()); + continue; + } + } +} +void Node::SetCommonValuesForSeg (dash::mpd::SegmentBase& object) const +{ + std::vector subNodes = this->GetSubNodes(); + + if (this->HasAttribute("timescale")) + { + object.SetTimescale(strtoul(this->GetAttributeValue("timescale").c_str(), NULL, 10)); + } + if (this->HasAttribute("presentationTimeOffset")) + { + object.SetPresentationTimeOffset(strtoul(this->GetAttributeValue("presentationTimeOffset").c_str(), NULL, 10)); + } + if (this->HasAttribute("indexRange")) + { + object.SetIndexRange(this->GetAttributeValue("indexRange")); + } + if (this->HasAttribute("indexRangeExact")) + { + object.SetIndexRangeExact(dash::helpers::String::ToBool(this->GetAttributeValue("indexRangeExact"))); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "Initialization") + { + object.SetInitialization(subNodes.at(i)->ToURLType(dash::metrics::InitializationSegment)); + continue; + } + if (subNodes.at(i)->GetName() == "RepresentationIndex") + { + object.SetRepresentationIndex(subNodes.at(i)->ToURLType(dash::metrics::IndexSegment)); + continue; + } + } +} +void Node::SetCommonValuesForMSeg(dash::mpd::MultipleSegmentBase& object) const +{ + std::vector subNodes = this->GetSubNodes(); + + SetCommonValuesForSeg(object); + + if (this->HasAttribute("duration")) + { + object.SetDuration(strtoul(this->GetAttributeValue("duration").c_str(), NULL, 10)); + } + if (this->HasAttribute("startNumber")) + { + object.SetStartNumber(strtoul(this->GetAttributeValue("startNumber").c_str(), NULL, 10)); + } + + for(size_t i = 0; i < subNodes.size(); i++) + { + if (subNodes.at(i)->GetName() == "SegmentTimeline") + { + object.SetSegmentTimeline(subNodes.at(i)->ToSegmentTimeline()); + continue; + } + if (subNodes.at(i)->GetName() == "BitstreamSwitching") + { + object.SetBitstreamSwitching(subNodes.at(i)->ToURLType(dash::metrics::BitstreamSwitchingSegment)); + continue; + } + } + +} diff --git a/libdash/source/xml/Node.h b/libdash/source/xml/Node.h new file mode 100644 index 00000000..552d83e5 --- /dev/null +++ b/libdash/source/xml/Node.h @@ -0,0 +1,105 @@ +/* + * Node.h + ***************************************************************************** + * Copyright (C) 2012, bitmovin Softwareentwicklung OG, All Rights Reserved + * + * Email: libdash-dev@vicky.bitmovin.net + * + * This source code and its use and distribution, is subject to the terms + * and conditions of the applicable license agreement. + *****************************************************************************/ + +#ifndef NODE_H_ +#define NODE_H_ + +#include "config.h" + +#include "INode.h" +#include "../helpers/String.h" +#include "../mpd/AdaptationSet.h" +#include "../mpd/BaseUrl.h" +#include "../mpd/ContentComponent.h" +#include "../mpd/Descriptor.h" +#include "../mpd/Metrics.h" +#include "../mpd/MPD.h" +#include "../mpd/MultipleSegmentBase.h" +#include "../mpd/Period.h" +#include "../mpd/ProgramInformation.h" +#include "../mpd/Range.h" +#include "../mpd/Representation.h" +#include "../mpd/RepresentationBase.h" +#include "../mpd/SegmentBase.h" +#include "../mpd/SegmentList.h" +#include "../mpd/SegmentTemplate.h" +#include "../mpd/SegmentTimeline.h" +#include "../mpd/SegmentURL.h" +#include "../mpd/SubRepresentation.h" +#include "../mpd/Subset.h" +#include "../mpd/URLType.h" +#include "IHTTPTransaction.h" + +namespace dash +{ + namespace xml + { + class Node : public INode + { + public: + Node (); + Node (const Node& other); + virtual ~Node (); + + const std::vector& GetNodes () const; + const std::vector& GetSubNodes () const; + std::vector GetAttributeKeys () const; + const std::string& GetName () const; + std::string GetText () const; + const std::map& GetAttributes () const; + int GetType () const; + void SetType (int type); + const std::string& GetAttributeValue (std::string key) const; + void AddSubNode (Node *node); + void SetName (const std::string &name); + bool HasAttribute (const std::string& name) const; + void AddAttribute (const std::string &key, const std::string &value); + bool HasText () const; + void SetText (const std::string &text); + void Print (std::ostream &stream) const; + dash::mpd::MPD* ToMPD () const; + void SetMPDPath (std::string path); + + private: + void SetCommonValuesForRep (dash::mpd::RepresentationBase& object) const; + void SetCommonValuesForSeg (dash::mpd::SegmentBase& object) const; + void SetCommonValuesForMSeg (dash::mpd::MultipleSegmentBase& object) const; + dash::mpd::AdaptationSet* ToAdaptationSet () const; + dash::mpd::BaseUrl* ToBaseUrl () const; + dash::mpd::ContentComponent* ToContentComponent () const; + dash::mpd::Descriptor* ToDescriptor () const; + dash::mpd::Metrics* ToMetrics () const; + dash::mpd::Period* ToPeriod () const; + dash::mpd::ProgramInformation* ToProgramInformation () const; + dash::mpd::Range* ToRange () const; + dash::mpd::Representation* ToRepresentation () const; + dash::mpd::SegmentBase* ToSegmentBase () const; + dash::mpd::SegmentList* ToSegmentList () const; + dash::mpd::SegmentTemplate* ToSegmentTemplate () const; + dash::mpd::Timeline* ToTimeline () const; + dash::mpd::SegmentTimeline* ToSegmentTimeline () const; + dash::mpd::SegmentURL* ToSegmentURL () const; + dash::mpd::SubRepresentation* ToSubRepresentation () const; + dash::mpd::Subset* ToSubset () const; + dash::mpd::URLType* ToURLType (dash::metrics::HTTPTransactionType transActType) const; + + std::vector subNodes; + std::map attributes; + std::string name; + std::string text; + int type; + std::string mpdPath; + + }; + } +} + +#endif /* NODE_H_ */ diff --git a/main.cpp b/main.cpp new file mode 100644 index 00000000..a50e2e21 --- /dev/null +++ b/main.cpp @@ -0,0 +1,225 @@ +/****************************************************************************** + Simple Player: this file is part of QtAV examples + Copyright (C) 2012-2016 Wang Bin +* This file is part of QtAV + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef Q_OS_ANDROID +#include +#endif +#include "Common/QtQuick2ApplicationViewer.h" +#include "../Common/Common.h" +#include "UI/DASHPlayer.h" +#include "UI/DASHPlayerNoGUI.h" +#include "UI/ViperGui.h" +#include "UI/GraphDataSource.h" +#include "Websocket/WebSocketService.h" +#include + +using namespace viper; +int main(int argc, char *argv[]) +{ + bool nogui = false; + for(int i = 0; i < argc; i++) + { + if(!strcmp(argv[i], "-nohead")) + { + nogui = true; + break; + } + } + if(nogui) + { + pthread_mutex_t mainMutex; + pthread_cond_t mainCond; + + pthread_mutex_init(&mainMutex,NULL); + pthread_cond_init(&mainCond, NULL); + + Debug("STARTING NO GUI\n"); + DASHPlayerNoGUI p(argc,argv, &mainCond, true); + + + pthread_mutex_lock(&mainMutex); + while(p.isRunning()) + { + pthread_cond_wait(&mainCond, &mainMutex); + } + pthread_mutex_unlock(&mainMutex); + + return 0; + } + QOptions options(get_common_options()); + options.add(QLatin1String("Viper options")) + ("scale", 1.0, QLatin1String("scale of graphics context. 0: auto")) + ; + options.parse(argc, argv); + Config::setName(QString::fromLatin1("Viper")); + do_common_options_before_qapp(options); + + QApplication app(argc, argv); + app.setApplicationName(QStringLiteral("Viper")); + app.setApplicationDisplayName(QStringLiteral("Viper")); + QDir::setCurrent(qApp->applicationDirPath()); + qDebug() << "event dispatcher:" << QCoreApplication::eventDispatcher(); + do_common_options(options); + qDebug() << "arguments======= " << app.arguments(); + qDebug() << "current dir: " << QDir::currentPath(); + set_opengl_backend(options.option(QStringLiteral("gl")).value().toString(), app.arguments().first()); + load_qm(QStringList() << QStringLiteral("Viper"), options.value(QStringLiteral("language")).toString()); + QtQuick2ApplicationViewer viewer; + QString binDir = qApp->applicationDirPath(); + if (binDir.endsWith(QLatin1String(".app/Contents/MacOS"))) { + binDir.remove(QLatin1String(".app/Contents/MacOS")); + binDir = binDir.left(binDir.lastIndexOf(QLatin1String("/"))); + } + QQmlEngine *engine = viewer.engine(); + if (!engine->importPathList().contains(binDir)) + engine->addImportPath(binDir); + qDebug() << engine->importPathList(); + engine->rootContext()->setContextProperty(QStringLiteral("PlayerConfig"), &Config::instance()); + qDebug(">>>>>>>>devicePixelRatio: %f", qApp->devicePixelRatio()); + QScreen *sc = app.primaryScreen(); + qDebug() << "dpi phy: " << sc->physicalDotsPerInch() << ", logical: " << sc->logicalDotsPerInch() << ", dpr: " << sc->devicePixelRatio() + << "; vis rect:" << sc->virtualGeometry(); + engine->rootContext()->setContextProperty(QStringLiteral("screenPixelDensity"), sc->physicalDotsPerInch()*sc->devicePixelRatio()); + qreal r = sc->physicalDotsPerInch()/sc->logicalDotsPerInch(); + const qreal kR = + #if defined(Q_OS_ANDROID) + 2.0; +#elif defined(Q_OS_WINRT) + 1.2; +#else + 1.0; +#endif + if (std::isinf(r) || std::isnan(r)) + r = kR; + float sr = options.value(QStringLiteral("scale")).toFloat(); +#if defined(Q_OS_ANDROID) || defined(Q_OS_WINRT) + sr = r; +#if defined(Q_OS_WINPHONE) + sr = kR; +#endif + if (sr > 2.0) + sr = 2.0; //FIXME +#endif + if (qFuzzyIsNull(sr)) + sr = r; + engine->rootContext()->setContextProperty(QStringLiteral("scaleRatio"), sr); + qDebug() << "touch devices: " << QTouchDevice::devices(); + engine->rootContext()->setContextProperty(QStringLiteral("isTouchScreen"), false); +#ifdef Q_OS_WINPHONE + engine->rootContext()->setContextProperty(QStringLiteral("isTouchScreen"), true); +#endif + foreach (const QTouchDevice* dev, QTouchDevice::devices()) { + if (dev->type() == QTouchDevice::TouchScreen) { + engine->rootContext()->setContextProperty(QStringLiteral("isTouchScreen"), true); + break; + } + } + QString qml = QStringLiteral("qml/Viper/main.qml"); + if (QFile(qApp->applicationDirPath() + QLatin1String("/") + qml).exists()) + qml.prepend(qApp->applicationDirPath() + QLatin1String("/")); + else + qml.prepend(QLatin1String("qrc:///")); + viewer.setMainQmlFile(qml); + viewer.show(); + QOption op = options.option(QStringLiteral("width")); + if (op.isSet()) + viewer.setWidth(op.value().toInt()); + op = options.option(QStringLiteral("height")); + if (op.isSet()) + viewer.setHeight(op.value().toInt()); + op = options.option(QStringLiteral("x")); + if (op.isSet()) + viewer.setX(op.value().toInt()); + op = options.option(QStringLiteral("y")); + if (op.isSet()) + viewer.setY(op.value().toInt()); + if (options.value(QStringLiteral("fullscreen")).toBool()) + viewer.showFullScreen(); + viewer.setTitle(QStringLiteral("Viper")); +#if 1 + QString json = app.arguments().join(QStringLiteral("\",\"")); + json.prepend(QLatin1String("[\"")).append(QLatin1String("\"]")); + json.replace(QLatin1String("\\"), QLatin1String("/")); //FIXME + QMetaObject::invokeMethod(viewer.rootObject(), "init", Q_ARG(QVariant, json)); + //#else + QObject *player = viewer.rootObject()->findChild(QStringLiteral("player")); + AppEventFilter *ae = new AppEventFilter(player, player); + qApp->installEventFilter(ae); + QString file; +#ifdef Q_OS_ANDROID + engine->rootContext()->setContextProperty("platform", 1); + file = QAndroidJniObject::callStaticObjectMethod("org.viper.com.ViperActivity" + , "getUrl" + , "()Ljava/lang/String;") + .toString(); +#endif + engine->rootContext()->setContextProperty("platform", 0); + if (app.arguments().size() > 1) { + file = options.value(QStringLiteral("file")).toString(); + if (file.isEmpty()) { + if (argc > 1 && !app.arguments().last().startsWith(QLatin1Char('-')) && !app.arguments().at(argc-2).startsWith(QLatin1Char('-'))) + file = app.arguments().last(); + } + } + qDebug() << "file: " << file; + if (player && !file.isEmpty()) { + if (!file.startsWith(QLatin1String("file:")) && QFile(file).exists()) + file.prepend(QLatin1String("file:")); //qml use url and will add qrc: if no scheme + file.replace(QLatin1String("\\"), QLatin1String("/")); + player->setProperty("source", QUrl(file)); + } +#endif + QObject::connect(&Config::instance(), SIGNAL(changed()), &Config::instance(), SLOT(save())); + QObject::connect(viewer.rootObject(), SIGNAL(requestFullScreen()), &viewer, SLOT(showFullScreen())); + QObject::connect(viewer.rootObject(), SIGNAL(requestNormalSize()), &viewer, SLOT(showNormal())); + GraphDataSource graphDataSource(&viewer); + viewer.rootContext()->setContextProperty("dataSource", &graphDataSource); + WebSocketService webSocketService; + webSocketService.setGraphDataSource(&graphDataSource); + webSocketService.start(); + ViperGui gui(player); + QVariantHash va_opt; + va_opt["display"] = "X11"; + va_opt["copyMode"] = "ZeroCopy"; + QVariantHash opt; + opt["VAAPI"] = va_opt; + gui.setNowLabel(viewer.rootObject()->findChild(QStringLiteral("now"))); + gui.setLifeLabel(viewer.rootObject()->findChild(QStringLiteral("life"))); + gui.setProgressBar(viewer.rootObject()->findChild(QStringLiteral("progress"))); + gui.setPlayButton(viewer.rootObject()->findChild(QStringLiteral("playBtn"))); + gui.setGraphDataSource(&graphDataSource); + gui.setRootObject(viewer.rootObject()); + DASHPlayer dashPlayer(gui, &Config::instance()); + engine->rootContext()->setContextProperty("dashPlayer",&dashPlayer); + QMetaObject::invokeMethod(viewer.rootObject(), "initGraph", Q_ARG(QVariant, (&Config::instance())->graph())); + QMetaObject::invokeMethod(viewer.rootObject(), "initRepeat", Q_ARG(QVariant, (&Config::instance())->repeat())); + QMetaObject::invokeMethod(viewer.rootObject(), "initFullScreen", Q_ARG(QVariant, (&Config::instance())->fullScreen())); + QMetaObject::invokeMethod(viewer.rootObject(), "setLastPlayed", Q_ARG(QVariant, (&Config::instance())->lastPlayed())); + QMetaObject::invokeMethod(viewer.rootObject(), "setAdaptationLogic", Q_ARG(QVariant, (&Config::instance())->adaptationLogic())); + QMetaObject::invokeMethod(viewer.rootObject(), "setIcn", Q_ARG(QVariant, (&Config::instance())->icn())); + return app.exec(); +} diff --git a/player.conf b/player.conf new file mode 100644 index 00000000..a892012b --- /dev/null +++ b/player.conf @@ -0,0 +1,38 @@ +;configuration file for the player + +ndn_prefix = /webserver/get/ +http_prefix = http://192.168.128.44:8080/ + +;ndn_suffix = /mpdsvq +;http_suffix = /mpdtcp + +segment_buffer_size = 20 +alpha = 0.95 + + +; BOLA CONF +bola_buffer_target = 23.0 + +; Buffer Based conf +buffer_reservoir_threshold = 20 +buffer_max_threshold = 80 + +; Adaptech (BufferRateBased) conf +adaptech_first_threshold = 25 +adaptech_second_threshold = 45 +adaptech_switch_up_margin = 5 +adaptech_slack_parameter = 0.8 + +; Buffer Three Threshold conf +buffer_3Threshold_first = 15 +buffer_3Threshold_second = 35 +buffer_3Threshold_third = 75 + +; Panda conf +panda_param_alpha = 0.4 +panda_param_beta = 0.6 +panda_param_Bmin = 67 +panda_param_K = 0.5 +panda_param_W = 300000 +panda_param_epsilon = 0.19 + diff --git a/qml/Viper/Button.qml b/qml/Viper/Button.qml new file mode 100755 index 00000000..7af64c5f --- /dev/null +++ b/qml/Viper/Button.qml @@ -0,0 +1,89 @@ +/****************************************************************************** + Copyright (C) 2013-2016 Wang Bin + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + id: root + property string text + property url icon + property alias iconChecked: iconChecked.source + property bool checkable: false + property bool checked: false + property color bgColor: "#555555" + property color bgColorSelected: "#ee6666dd" + property color textColor: "white" + property bool hovered: false //mouseArea.containsMouse + readonly property alias pressed: mouseArea.pressed + signal clicked() + signal pressAndHold() + + opacity: 0.7 + color: checked ? bgColorSelected : mouseArea.pressed ? Qt.darker(bgColor) : bgColor + border.color: Qt.lighter(color) + + Text { + id: text + anchors.fill: parent + text: root.text + font.pixelSize: 0.5 * parent.height + color: textColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + Image { + source: icon + anchors.fill: parent + visible: !checked + } + Image { + id: iconChecked + anchors.fill: parent + visible: checked + } + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + if (root.checkable) + root.checked = !root.checked + root.clicked() + } + onHoveredChanged: { + if (mouseX > 65535) //qt5.6 touch screen release finger becomes very large e.g. 0x7fffffff + return + hovered = mouseArea.containsMouse + } + onPressAndHold: root.pressAndHold() + } + states: [ + State { + name: "brighter" + when: hovered // only the first true State is applied, so put scale and opacity together + PropertyChanges { target: root; opacity: 1.0; scale: mouseArea.pressed ? 1.06 : 1.0 } + } + ] + transitions: [ + Transition { + from: "*"; to: "*" + PropertyAnimation { + properties: "opacity,scale" + easing.type: Easing.OutQuart + duration: 300 + } + } + ] +} diff --git a/qml/Viper/ControlPanel.qml b/qml/Viper/ControlPanel.qml new file mode 100755 index 00000000..4f5848d7 --- /dev/null +++ b/qml/Viper/ControlPanel.qml @@ -0,0 +1,461 @@ +/****************************************************************************** + QtAV: Multimedia framework based on Qt and FFmpeg + Copyright (C) 2012-2016 Wang Bin +* This file is part of QtAV (from 2013) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + +import QtQuick 2.0 +import "utils.js" as Utils +import QtQuick.Window 2.1 +import QtAV 1.4 + +Rectangle { + id: root + function scaled(x) { + console.log("Screen " + screenPixelDensity + "; r: " + Screen.pixelDensity/Screen.logicalPixelDensity + "; size: " + Screen.width + "x" + Screen.height); + console.log("screen density logical: " + Screen.logicalPixelDensity + " pixel: " + Screen.pixelDensity + "; " + x + ">>>" +x*Screen.pixelDensity/Screen.logicalPixelDensity); + return x*Screen.pixelDensity/Screen.logicalPixelDensity; + } + + color: "black" + opacity: 0.9 + radius: Utils.scaled(10) + height: Utils.scaled(80) + width: itemWidth-25*pixDens + + property string playState: "stop" + property int duration: 0 + property real volume: 1 + property bool mute: false + property bool hiding: false + signal startGraph + signal pauseGraph + signal stopGraph + signal resizeWindowFullScreen + signal resizeWindow + signal openMpd + signal openOptions + signal openOptionConnections + signal openGraph + signal hideGraph + signal repeatVideo + signal donotRepeatVideo + signal saveFullScreen + signal saveExitFullScreen + signal togglePause + signal showInfo + signal showHelp + signal openFile + signal openUrl + signal downloadMPD + + function setPlayingProgress(value) + { + playState = "play" + } + + function setStopState() + { + isPlaying = "stop" + playBtn.checked = false + + } + + function setPlayingState() { + playBtn.checked = true + playState = "play" + + } + + function setPauseState() { + playBtn.checked = false + playState = "pause" + } + + function toggleFullScreen() { + fullScreenBtn.checked = !fullScreenBtn.checked + } + + gradient: Gradient { + GradientStop { position: 0.0; color: "#88445566" } + GradientStop { position: 0.618; color: "#cc1a2b3a" } + GradientStop { position: 1.0; color: "#ee000000" } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onHoveredChanged: { + if (containsMouse) { + if (timer.running) //timer may ran a few seconds(<3) ago + timer.stop(); + root.aniShow() + } else { + + } + } + + onPressed: { + if (timer.running) //timer may ran a few seconds(<3) ago + timer.stop(); + root.aniShow() + } + } + + ProgressBar { + id: progress + objectName: "progress" + + anchors { + top: parent.top + topMargin: Utils.scaled(8) + left: parent.left + leftMargin: Utils.scaled(20) + right: parent.right + rightMargin: Utils.scaled(20) + } + height: Utils.scaled(8) + onValueChangedByUi: { + dashPlayer.seekVideo(value); + } + + onEnter: { + if (playState == "stop") + return + } + + onLeave: { + if (playState == "stop") + return + } + onHoverAt: { + if (playState == "stop") + return; + } + } + + Item { + layer.enabled: true + anchors { + top: progress.bottom + bottom: parent.bottom + left: parent.left + right: parent.right + margins: Utils.scaled(8) + } + + Text { + id: now + objectName: "now" + text: "00:00:00"//Utils.msec2string(progress.value*duration) + anchors { + top: parent.top + topMargin: Utils.scaled(2) + left: parent.left + } + color: "white" + font { + pixelSize: Utils.scaled(12) //or point size? + } + } + Text { + id: life + objectName: "life" + text: "00:00:00"//Utils.msec2string(duration) + anchors { + top: parent.top + topMargin: Utils.scaled(2) + right: parent.right + } + color: "white" + font { + pixelSize: Utils.scaled(12) + } + } + Button { + id: playBtn + enabled: true + objectName: "playBtn" + anchors.centerIn: parent + checkable: true + bgColor: "transparent" + bgColorSelected: "transparent" + width: Utils.scaled(50) + height: Utils.scaled(50) + icon: "qrc:///qml/images/play.svg" + iconChecked: "qrc:///qml/images/pause.svg" + + onClicked: { + if (checked === true) { + console.log(adaptationLogic) + console.log(lastPlayed) + dashPlayer.downloadMPD(lastPlayed, adaptationLogic, icn) + } else { + dashPlayer.pause(); + } + } + } + + Button { + id: stopBtn + enabled: true + anchors.verticalCenter: playBtn.verticalCenter + anchors.right: playBtn.left + bgColor: "transparent" + bgColorSelected: "transparent" + width: Utils.scaled(35) + height: Utils.scaled(35) + icon: "qrc:///qml/images/stop.svg" + onClicked: { + playBtn.checked = false + isPlaying = false + canBuffer = false + dashPlayer.onStopButtonPressed() + } + } + + Button { + id: fullScreenBtn + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(60) + anchors.verticalCenter: parent.verticalCenter + checkable: true + bgColor: "transparent" + bgColorSelected: "transparent" + width: Utils.scaled(30) + height: Utils.scaled(30) + icon: "qrc:///qml/images/fullscreen.png" + iconChecked: "qrc:///qml/images/fullscreen-selected.png" + visible: (Qt.platform.os != "android") + checked: enabledFullScreen + onCheckedChanged: { + if (checked) { + fullScreen() + saveFullScreen() + } else { + exitFullScreen() + saveExitFullScreen() + } + } + } + + Row { + anchors.right: parent.right + anchors.rightMargin: Utils.scaled(70) + anchors.verticalCenter: parent.verticalCenter + spacing: Utils.scaled(20) + + Button { + id: graphBtn + bgColor: "transparent" + bgColorSelected: "transparent" + checkable: true + width: Utils.scaled(30) + height: Utils.scaled(30) + icon: "qrc:///qml/images/graph.png" + iconChecked: "qrc:///qml/images/graph-selected.png" + visible: true + checked: graph + onCheckedChanged: { + if ( !graphBtn.checked) { + hideGraph() + } else { + openGraph() + } + } + } + + Button { + id: optionsBtn + bgColor: "transparent" + bgColorSelected: "transparent" + checkable: true + width: Utils.scaled(30) + height: Utils.scaled(30) + icon: "qrc:///qml/images/options.png" + iconChecked: "qrc:///qml/images/options-selected.png" + visible: true + onCheckedChanged: { + if (checked) + openOptions() + } + } + + Button { + id: optionConnectionsBtn + bgColor: "transparent" + bgColorSelected: "transparent" + checkable: true + width: Utils.scaled(30) + height: Utils.scaled(30) + icon: "qrc:///qml/images/option-connections.png" + iconChecked: "qrc:///qml/images/option-connections-selected.png" + visible: true + onCheckedChanged: { + + if (checked) + openOptionConnections() + } + } + + Button { + id: openBtn + bgColor: "transparent" + bgColorSelected: "transparent" + checkable: true + width: Utils.scaled(30) + height: Utils.scaled(30) + icon: "qrc:///qml/images/open.png" + iconChecked: "qrc:///qml/images/open-selected.png" + onCheckedChanged: { + if(checked) + openMpd() + } + } + Button { + id: repeatBtn + bgColor: "transparent" + bgColorSelected: "transparent" + checkable: true + width: Utils.scaled(30) + height: Utils.scaled(30) + icon: "qrc:///qml/images/repeat.png" + iconChecked: "qrc:///qml/images/repeat-selected.png" + checked: repeat + onCheckedChanged: { + if ( !repeatBtn.checked) { + donotRepeatVideo() + } else { + repeatVideo() + } + + } + + } + } + } + + Timer { + id: timer + interval: 3000 + onTriggered: { + root.aniHide() + } + } + + function hideIfTimedout() + { + timer.start() + } + + PropertyAnimation { + id: anim + target: root + properties: "opacity" + function reverse() + { + duration = 1500 + to = 0 + from = root.opacity + } + + function reset() + { + duration = 200 + from = root.opacity + to = 0.9 + } + } + + function aniShow() + { + hiding = false + anim.stop() + anim.reset() + anim.start() + } + + function aniHide() + { + hiding = true + anim.stop() + anim.reverse() + anim.start() + } + + function toggleVisible() + { + if (hiding) + aniShow() + else + aniHide() + } + + function enable() + { + playBtn.enabled = true + stopBtn.enabled = true + } + + function disable() + { + playBtn.enabled = false + stopBtn.enabled = false + } + + + function fullScreen() + { + requestFullScreen() + resizeWindowFullScreen() + } + + function exitFullScreen() + { + requestNormalSize() + resizeWindow() + } + + function checkRepeatButton() + { + repeatBtn.checked = !repeatBtn.checked; + } + + function uncheckOpenBtn() + { + openBtn.checked = false; + } + + function enableOpenBtn() + { + openBtn.enabled = true; + } + + function uncheckOptionsBtn() + { + + optionsBtn.checked = false; + } + + function uncheckOptionConnectionsBtn() + { + + optionConnectionsBtn.checked = false; + } + + function enableOptionsBtn() + { + optionsBtn.enabled = true; + } +} diff --git a/qml/Viper/DelegateItem.qml b/qml/Viper/DelegateItem.qml new file mode 100755 index 00000000..edc45572 --- /dev/null +++ b/qml/Viper/DelegateItem.qml @@ -0,0 +1,65 @@ +/****************************************************************************** + QtAV: Multimedia framework based on Qt and FFmpeg + Copyright (C) 2012-2016 Wang Bin +* This file is part of QtAV (from 2013) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + +import QtQuick 2.0 +import "utils.js" as Utils + +Rectangle { + id: root + width: Math.max(Utils.kItemWidth, itemText.contentWidth+8) + height: itemText.contentWidth + property color selectedColor: "#66ddaadd" + property alias text: itemText.text + color: "#99000000" + signal clicked + Text { + id: itemText + color: "white" + anchors.fill: parent + anchors.margins: 4 + font.pixelSize: Utils.kFontSize + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + MouseArea { + anchors.fill: parent + onClicked: { + root.state = "selected" + root.clicked() + } + } + states: [ + State { + name: "selected" + PropertyChanges { + target: delegateItem + color: selectedColor + } + } + ] + transitions: [ + Transition { + from: "*"; to: "*" + ColorAnimation { + properties: "color" + easing.type: Easing.OutQuart + duration: 500 + } + } + ] +} diff --git a/qml/Viper/GraphPanel.qml b/qml/Viper/GraphPanel.qml new file mode 100755 index 00000000..24105225 --- /dev/null +++ b/qml/Viper/GraphPanel.qml @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.0 +import QtCharts 2.1 + + +Rectangle { + id: root + anchors.fill: parent; + color: Qt.rgba(0,0,0,0.5) + ChartView { + anchors.bottomMargin: parent.height/2 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.rightMargin: windowWidth*0.015 + + id: chartViewBitRateFps + opacity: 1 + animationOptions: ChartView.NoAnimation + backgroundColor: "transparent" + legend.visible: true + legend.labelColor: "white" + legend.font:Qt.font({pointSize: windowWidth*0.015, bold:true}) + antialiasing: enabled; + property bool openGL: true + onOpenGLChanged: { + series("signal 1").useOpenGL = openGL; + } + + ValueAxis { + labelFormat: "%d%" + id: bitBufferLevelY + labelsColor: "white" + + labelsFont:Qt.font({pointSize: windowWidth*0.015, bold:true}) + min: 0 + max: 100 + + } + ValueAxis { + labelFormat: "%d" + id: bitRateAxisY + labelsColor: "white" + + labelsFont:Qt.font({pointSize: windowWidth*0.015, bold:true}) + min: 0 + max: 20 + + } + + ValueAxis { + labelsVisible: false + labelFormat: "%d" + labelsAngle: 90 + labelsFont:Qt.font({pointSize: 1}) + id: axisX + gridVisible: true + min: -100 + max: 0 + } + + CategoryAxis { + id: axesYQualityVideo + min: 1 + max: 19 + gridVisible: false + tickCount: 6 + labelsColor: "white" + labelsFont:Qt.font({pointSize: windowWidth*0.015, bold:true}) + CategoryRange { + label: "LD" + endValue: 4 + } + + CategoryRange { + label: "SD" + endValue: 7 + } + + CategoryRange { + label: "HD" + endValue: 10 + } + + CategoryRange { + label: "FHD" + endValue: 13 + } + + CategoryRange { + label: "QHD" + endValue: 16 + } + + CategoryRange { + label: "UHD" + endValue: 19 + } + } + + LineSeries { + id: bufferLevelSeries + name: "Buffer Level (%)" + axisX: axisX + color: "green" + width: pixDens*3 + axisY: bitBufferLevelY + useOpenGL: chartViewBitRateFps.openGL + } + + LineSeries { + id: bitRateSeries + name: "Download Quality" + axisX: axisX + color: "yellow" + width: pixDens*3 + axisYRight: axesYQualityVideo + useOpenGL: chartViewBitRateFps.openGL + } + } + + ChartView { + anchors.topMargin: parent.height/2 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.leftMargin: windowWidth*0.048 + id: chartViewQuality + opacity: 1 + animationOptions: ChartView.NoAnimation + backgroundColor: "transparent" + legend.visible: true + legend.labelColor: "white" + legend.font:Qt.font({pointSize: windowWidth*0.015, bold:true}) + + antialiasing: enabled; + property bool openGL: true + onOpenGLChanged: { + series("signal 1").useOpenGL = openGL; + } + + ValueAxis { + labelsVisible: false + labelFormat: "%d" + labelsAngle: 90 + labelsFont:Qt.font({pointSize: 1}) + id: axisX2 + gridVisible: true + min: -100 + max: 0 + } + + ValueAxis { + labelFormat: "%d" + id: bitRateAxisY2 + gridVisible: false + labelsColor: "white" + labelsFont:Qt.font({pointSize: windowWidth*0.015, bold:true}) + min: 0 + max: 20 + } + + LineSeries { + id: dummySeries + visible: false + axisX: axisX2 + axisY: bitRateAxisY2 + } + + CategoryAxis { + id: axeYQuality + min: 1 + max: 19 + tickCount: 6 + labelsColor: "white" + labelsFont:Qt.font({pointSize: windowWidth*0.015, bold:true}) + CategoryRange { + label: "LD" + endValue: 4 + } + CategoryRange { + label: "SD" + endValue: 7 + } + CategoryRange { + label: "HD" + endValue: 10 + } + + CategoryRange { + label: "FHD" + endValue: 13 + } + + CategoryRange { + label: "QHD" + endValue: 16 + } + CategoryRange { + label: "UHD" + endValue: 19 + } + } + + LineSeries { + id: qualitySeries + name: "Displayed Quality (Mbps)" + axisX: axisX2 + width: pixDens*3 + color: "white" + axisYRight: axeYQuality + + useOpenGL: chartViewQuality.openGL + } + } + + Timer { + id: refreshTimer + interval: 10 + objectName: "refreshTimer" + running: false + repeat: true + onTriggered: { + dataSource.update(bitRateSeries, qualitySeries, bufferLevelSeries); + qualitySeries.axisX.min = qualitySeries.at(qualitySeries.count-1).x - 1000 + qualitySeries.axisX.max = qualitySeries.at(qualitySeries.count-1).x + bitRateSeries.axisX.min = bitRateSeries.at(bitRateSeries.count-1).x - 1000 + bitRateSeries.axisX.max = bitRateSeries.at(bitRateSeries.count-1).x + } + } + + function startTimer() + { + refreshTimer.running = true; + } + + function pauseTimer() + { + refreshTimer.running = false; + } + + function stopTimer() + { + refreshTimer.running = false; + bitRateSeries.clear(); + bufferLevelSeries.clear(); + qualitySeries.clear(); + dataSource.clearData(); + } +} + diff --git a/qml/Viper/OpenMpd.qml b/qml/Viper/OpenMpd.qml new file mode 100755 index 00000000..d821f072 --- /dev/null +++ b/qml/Viper/OpenMpd.qml @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.5 +import QtQuick.Extras 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 +import "utils.js" as Utils +Rectangle { + signal closeOpenMpd + signal saveAndPlayMpd(string newOpenMpd) + + id: root + color: "#88445566" + + opacity: 0 + radius: Utils.scaled(10) + height: Utils.scaled(100) + width: Utils.scaled(300) + enabled: false; + GridLayout { + id : grid + z: parent.z + 1 + anchors.fill: parent + rows : 2 + columns : 2 + anchors.leftMargin: Utils.scaled(12) + + anchors.rightMargin: Utils.scaled(12) + anchors.topMargin: Utils.scaled(12) + anchors.bottomMargin: Utils.scaled(12) + property double colMulti : grid.width / grid.columns + property double rowMulti : grid.height / grid.rows + + function prefWidth(item) + { + return colMulti * item.Layout.columnSpan + } + + function prefHeight(item) + { + return rowMulti * item.Layout.rowSpan + } + + ComboBox { + z: parent.z + 1 + id: comboBoxList + Layout.rowSpan : 1 + Layout.columnSpan : 2 + Layout.preferredWidth : parent.colMulti * 2 + Utils.scaled(5) //grid.prefWidth(this) + Layout.preferredHeight : parent.rowMulti//grid.prefHeight(this) + + onCurrentIndexChanged: { + } + + model: ListModel { + id: mpdItems + ListElement { text: "gastown"; } + ListElement { text: "sintel"; } + } + currentIndex: find(lastPlayed) + } + + Button { + id: cancelBtn + z: parent.z + 1 + + text: "Cancel" + Layout.rowSpan : 1 + Layout.columnSpan: 1 + Layout.preferredWidth : grid.prefWidth(this) + Layout.preferredHeight : grid.prefHeight(this) + onClicked: { + + closeOpenMpd(); + } + } + + Button { + id: downloadBtn + z: parent.z + 1 + Layout.rowSpan : 1 + Layout.columnSpan: 1 + Layout.preferredWidth : grid.prefWidth(this) + Layout.preferredHeight : grid.prefHeight(this) + text: "Download" + onClicked: { + saveAndPlayMpd(mpdItems.get(comboBoxList.currentIndex).text) + closeOpenMpd(); + } + + + } + + + + } + +} + diff --git a/qml/Viper/OptionConnections.qml b/qml/Viper/OptionConnections.qml new file mode 100755 index 00000000..daab7e98 --- /dev/null +++ b/qml/Viper/OptionConnections.qml @@ -0,0 +1,673 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.5 +import QtQuick.Extras 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 +import "utils.js" as Utils +Rectangle { + signal closeOptionConnections + signal saveAutotune(bool selectedAutotune) + signal saveLifetime(int selectedLifetime) + signal saveRetransmissions(int selectedRetransmissions) + signal saveAlpha(real selectedAlpha) + signal saveBeta(real selectedBeta) + signal saveDrop(real selectedDrop) + signal saveBetaWifi(real selectedBetaWifi) + signal saveDropWifi(real selectedDropWifi) + signal saveDelayWifi(int selectedDelayWifi) + signal saveBetaLte(real selectedBetaLte) + signal saveDropLte(real selectedDropLte) + signal saveDelayLte(int selectedDelayLte) + signal saveBatchingParameter(int selectedBatchingParameter) + signal saveRateEstimator(int selectedRateEstimator) + property int heightRow: Utils.scaled(60) + + id: root + color: "#88445566" + property variant target + opacity: 0 + radius: Utils.scaled(10) + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + enabled: false + + Item { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + id: itemAutotune + Label { + id: labelAdaptationSetList + color: "white" + anchors.top: parent.top + anchors.right: comboAutotune.left + anchors.rightMargin: Utils.scaled(5) + + anchors.topMargin: (comboAutotune.height - height)/2 + text: "Auto Tune" + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + ComboBox { + z: parent.z + 1 + id: comboAutotune + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + width: Utils.scaled(200) + enabled: true + + textRole: "text" + model: ListModel { + id: adaptationLogicModel + + ListElement { text: "True"; } + ListElement { text: "False"; } + + } + onCurrentIndexChanged: { + console.debug( currentIndex + " " + currentText) + } + currentIndex: autotune == true ? 0 : 1 + + } + } + + Item { + id: itemLifetime + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow + Label { + text: "Lifetime" + id: labelLifetime + color: " white" + anchors.top: parent.top + anchors.right: spinboxLifetime.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxLifetime.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + SpinBox { + id: spinboxLifetime + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 1 + value: lifetime + to: 10000 + } + } + + Item { + id: itemRetransmissions + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow + + Label { + text: "Retransmissions" + id: labelRetransmissions + color: " white" + anchors.top: parent.top + anchors.right: spinboxRetransmissions.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxRetransmissions.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + SpinBox { + id: spinboxRetransmissions + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 1 + value: retransmissions + to: 10000 + stepSize: 1 + } + } + + Item { + id: itemAlpha + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + 2*heightRow + + Label { + text: "Alpha" + id: labelAlpha + color: " white" + anchors.top: parent.top + anchors.right: spinboxAlpha.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxAlpha.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + SpinBox { + id: spinboxAlpha + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: alpha*100 + to: 10000 + stepSize: 1 + property int decimals: 2 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxAlpha.from, spinboxAlpha.to) + top: Math.max(spinboxAlpha.from, spinboxAlpha.to) + } + + textFromValue: function(value, locale) { + return Number(value / 100).toLocaleString(locale, 'f', spinboxAlpha.decimals) + } + + valueFromText: function(text, locale) { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemBeta + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow * 2 + + Label { + text: "Beta" + id: labelBeta + color: " white" + anchors.top: parent.top + anchors.right: spinboxBeta.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBeta.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + SpinBox { + id: spinboxBeta + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 0 + value: beta*100 + to: 10000 + stepSize: 1 + + property int decimals: 2 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxBeta.from, spinboxBeta.to) + top: Math.max(spinboxBeta.from, spinboxBeta.to) + } + + textFromValue: function(value, locale) { + return Number(value / 100).toLocaleString(locale, 'f', spinboxBeta.decimals) + } + + valueFromText: function(text, locale) { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemDrop + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow * 2 + + Label { + text: "Drop" + id: labelDrop + color: " white" + anchors.top: parent.top + anchors.right: spinboxDrop.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxDrop.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxDrop + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(700) + from: 0 + value: drop*1000 + to: 100000 + stepSize: 1 + property int decimals: 3 + property real realValue: value / 1000 + + validator: DoubleValidator { + bottom: Math.min(spinboxDrop.from, spinboxDrop.to) + top: Math.max(spinboxDrop.from, spinboxDrop.to) + } + + textFromValue: function(value, locale) { + return Number(value / 1000).toLocaleString(locale, 'f', spinboxDrop.decimals) + } + + valueFromText: function(text, locale) { + return Number.fromLocaleString(locale, text) * 1000 + } + } + } + + + Item { + id: itemBetaWifi + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow * 3 + + Label { + text: "BetaWifi" + id: labelBetaWifi + color: " white" + anchors.top: parent.top + anchors.right: spinboxBetaWifi.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBetaWifi.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxBetaWifi + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: betaWifi*100 + to: 10000 + stepSize: 1 + property int decimals: 2 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxBetaWifi.from, spinboxBetaWifi.to) + top: Math.max(spinboxBetaWifi.from, spinboxBetaWifi.to) + } + + textFromValue: function(value, locale) { + return Number(value / 100).toLocaleString(locale, 'f', spinboxBetaWifi.decimals) + } + + valueFromText: function(text, locale) { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemDropWifi + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow * 3 + + Label { + text: "DropWifi" + id: labelDropWifi + color: " white" + anchors.top: parent.top + anchors.right: spinboxDropWifi.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxDropWifi.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxDropWifi + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 0 + value: dropWifi*1000 + to: 100000 + stepSize: 1 + property int decimals: 3 + property real realValue: value / 1000 + + validator: DoubleValidator { + bottom: Math.min(spinboxDropWifi.from, spinboxDropWifi.to) + top: Math.max(spinboxDropWifi.from, spinboxDropWifi.to) + } + + textFromValue: function(value, locale) { + return Number(value / 1000).toLocaleString(locale, 'f', spinboxDropWifi.decimals) + } + + valueFromText: function(text, locale) { + return Number.fromLocaleString(locale, text) * 1000 + } + } + } + + Item { + id: itemDelayWifi + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow * 3 + + Label { + text: "DelayWifi" + id: labelDelayWifi + color: " white" + anchors.top: parent.top + anchors.right: spinboxDelayWifi.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxDelayWifi.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxDelayWifi + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(700) + from: 1 + value: delayWifi + to: 100000 + stepSize: 1 + } + } + + Item { + id: itemBetaLte + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow * 4 + + Label { + text: "BetaLte" + id: labelBetaLte + color: " white" + anchors.top: parent.top + anchors.right: spinboxBetaLte.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBetaLte.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxBetaLte + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: betaLte*100 + to: 10000 + stepSize: 1 + property int decimals: 2 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxBetaLte.from, spinboxBetaLte.to) + top: Math.max(spinboxBetaLte.from, spinboxBetaLte.to) + } + + textFromValue: function(value, locale) { + return Number(value / 100).toLocaleString(locale, 'f', spinboxBetaLte.decimals) + } + + valueFromText: function(text, locale) { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemDropLte + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow * 4 + + Label { + text: "DropLte" + id: labelDropLte + color: " white" + anchors.top: parent.top + anchors.right: spinboxDropLte.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxDropLte.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxDropLte + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 0 + value: dropLte*1000 + to: 100000 + stepSize: 1 + property int decimals: 3 + property real realValue: value / 1000 + + validator: DoubleValidator { + bottom: Math.min(spinboxDropLte.from, spinboxDropLte.to) + top: Math.max(spinboxDropLte.from, spinboxDropLte.to) + } + + textFromValue: function(value, locale) { + return Number(value / 1000).toLocaleString(locale, 'f', spinboxDropLte.decimals) + } + + valueFromText: function(text, locale) { + return Number.fromLocaleString(locale, text) * 1000 + } + } + } + + Item { + id: itemDelayLte + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow * 4 + + Label { + text: "DelayLte" + id: labelDelayLte + color: " white" + anchors.top: parent.top + anchors.right: spinboxDelayLte.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxDelayLte.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxDelayLte + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(700) + from: 1 + value: delayLte + to: 100000 + stepSize: 1 + } + } + + Item { + id: itemBatchingParameter + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow * 5 + + Label { + text: "Batching Parameter" + id: labelBatchingParameter + color: " white" + anchors.top: parent.top + anchors.right: spinboxBatchingParameter.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBatchingParameter.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxBatchingParameter + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 1 + value: batchingParameter + to: 100000 + stepSize: 1 + } + } + + Item { + id: itemRateEstimator + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow * 5 + + Label { + text: "Rate Estimator" + id: labelRateEstimator + color: " white" + anchors.top: parent.top + anchors.right: spinboxRateEstimator.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxRateEstimator.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxRateEstimator + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 0 + value: rateEstimator + to: 1 + stepSize: 1 + } + } + + Item { + id: itemButton + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.rightMargin: Utils.scaled(12) + anchors.bottomMargin: Utils.scaled(12) + + Button { + id: cancelBtn + z: parent.z + 1 + text: "Cancel" + anchors.right: saveBtn.left + anchors.bottom: parent.bottom + anchors.rightMargin: Utils.scaled(5) + onClicked: { + closeOptionConnections(); + } + } + + Button { + id: saveBtn + z: parent.z + 1 + anchors.right: parent.right + anchors.bottom: parent.bottom + + text: "Save" + onClicked: { + saveAutotune(comboAutotune.currentIndex == 0 ? true : false) + saveLifetime(spinboxLifetime.value) + saveRetransmissions(spinboxRetransmissions.value) + saveAlpha(spinboxAlpha.value/100) + saveBeta(spinboxBeta.value/100) + saveDrop(spinboxDrop.value/1000) + saveBetaWifi(spinboxBetaWifi.value/100) + saveDropWifi(spinboxDropWifi.value/1000) + saveDelayWifi(spinboxDelayWifi.value) + saveBetaLte(spinboxBetaLte.value/100) + saveDropLte(spinboxDropLte.value/1000) + saveDelayLte(spinboxDelayLte.value) + saveBatchingParameter(spinboxBatchingParameter.value) + saveRateEstimator(spinboxRateEstimator.value) + dashPlayer.reloadParameters() + closeOptionConnections(); + } + } + } +} diff --git a/qml/Viper/Options.qml b/qml/Viper/Options.qml new file mode 100755 index 00000000..82d3e3e7 --- /dev/null +++ b/qml/Viper/Options.qml @@ -0,0 +1,1710 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.5 +import QtQuick.Extras 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 +import "utils.js" as Utils + +Rectangle { + signal closeOptions + signal saveAdaptationLogic(string selectedAdaptationLogic, int adaptationLogicKey) + signal saveIcn(bool selectedIcn) + signal saveIcnPrefix(string selectedIcnPrefix) + signal saveHttpPrefix(string selectedHttpPrefix) + signal saveIcnSuffix(string selectedIcnSuffix) + signal saveHttpSuffix(string selectedHttpSuffix) + signal saveSegmentBufferSize(real selectedSegmentBufferSize) + signal saveRateAlpha(real selectedRateAlpha) + signal saveBufferReservoirThreshold(real selectedBufferReservoirThreshold) + signal saveBufferMaxThreshold(real selectedBufferMaxThreshold) + signal saveAdaptechFirstThreshold(real selectedAdaptechFirstThreshold) + signal saveAdaptechSecondThreshold(real selectedAdaptechSecondThreshold) + signal saveAdaptechSwitchUpMargin(real selectedAdaptechSwitchUpMargin) + signal saveAdaptechSlackParameter(real selectedAdaptechSlackParameter) + signal saveAdaptechAlpha(real selectedAdaptechAlpha) + signal saveBufferThreeThresholdFirst(real selectedBufferThreeThresholdFirst) + signal saveBufferThreeThresholdSecond(real selectedBufferThreeThresholdSecond) + signal saveBufferThreeThresholdThird(real selectedBufferThreeThresholdThird) + signal savePandaParamAlpha(real selectedPandaParamAlpha) + signal savePandaParamBeta(real selectedPandaParamBeta) + signal savePandaParamBMin(real selectedPandaParamBMin) + signal savePandaParamK(real selectedPandaParamK) + signal savePandaParamW(real selectedPandaParamW) + signal savePandaParamEpsilon(real selectedPandaParamEpsilon) + signal saveBolaBufferTarget(real selectedBolaBufferTarget) + signal saveBolaAlpha(real selectedBolaAlpha) + signal reloadRateBasedConf + signal reloadBufferBasedConf + signal reloadBufferRateBasedConf + signal reloadBufferThreeThresholdConf + signal reloadPandaConf + signal reloadBolaConf + property int heightRow: Utils.scaled(60) + + function scaled(x) + { + return x*Screen.pixelDensity/Screen.logicalPixelDensity; + } + + id: root + color: "#88445566" + property variant target + opacity: 0 + radius: Utils.scaled(10) + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + enabled: false + + Item { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + id: itemAdaptationSetList + Label { + id: labelAdaptationSetList + color: "white" + anchors.top: parent.top + anchors.right: comboAdaptationSetList.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (comboAdaptationSetList.height - height)/2 + text: "Video AdaptationSet" + font.bold: true + font.pixelSize: Utils.scaled(10); + + } + + ComboBox { + z: parent.z + 1 + id: comboAdaptationSetList + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + width: Utils.scaled(200) + enabled: true + textRole: "text" + + model: ListModel { + id: adaptationLogicModel + ListElement { text: "Always Lowest"; } + ListElement { text: "Rate Based"; } + ListElement { text: "Buffer Based"; } + ListElement { text: "Buffer Rate Based"; } + ListElement { text: "Buffer Based Three Threshold"; } + ListElement { text: "Panda"; } + ListElement { text: "Bola"; } + } + + onCurrentIndexChanged: { + switch (currentIndex) { + case 0: + case 7: + reloadRateBasedConf() + spinboxRateAlpha.value = rateAlpha*100 + rectangleRateBasedConf.enabled = false + rectangleRateBasedConf.opacity = 0 + reloadBufferBasedConf() + spinboxBufferReservoirThreshold.value = bufferReservoirThreshold*100 + spinboxBufferMaxThreshold.value = bufferMaxThreshold*100 + rectangleBufferBasedConf.enabled = false + rectangleBufferBasedConf.opacity = 0 + reloadBufferRateBasedConf() + spinboxAdaptechFirstThreshold.value = adaptechFirstThreshold*100 + spinboxAdaptechSecondThreshold.value = adaptechSecondThreshold*100 + spinboxAdaptechSwitchUpMargin.value = adaptechSwitchUpMargin*100 + spinboxAdaptechSlackParameter.value = adaptechSlackParameter*100 + spinboxAdaptechAlpha.value = adaptechAlpha*100 + rectangleBufferRateBasedConf.enabled = false + rectangleBufferRateBasedConf.opacity = 0 + reloadBufferThreeThresholdConf() + spinboxBufferThreeThresoldFirst.value = bufferThreeThresholdFirst*100 + spinboxBufferThreeThresoldSecond.value = bufferThreeThresholdSecond*100 + spinboxBufferThreeThresoldThird.value = bufferThreeThresholdThird*100 + rectangleBufferThreeThresholdConf.enabled = false + rectangleBufferThreeThresholdConf.opacity = 0 + reloadPandaConf() + spinboxPandaParamAlpha.value = pandaParamAlpha*100 + spinboxPandaParamBeta.value = pandaParamBeta*100 + spinboxPandaParamBMin.value = pandaParamBMin*100 + spinboxPandaParamK.value = pandaParamK*100 + spinboxPandaParamW.value = pandaParamW + spinboxPandaParamEpsilon.value = pandaParamEpsilon*100 + rectanglePandaConf.enabled = false + rectanglePandaConf.opacity = 0 + reloadBolaConf() + spinboxBolaBufferTarget.value = bolaBufferTarget*100 + spinboxBolaAlpha.value = bolaAlpha*100 + rectangleBolaConf.enabled = false + rectangleBolaConf.opacity = 0 + break + case 1: + rectangleRateBasedConf.enabled = true + rectangleRateBasedConf.opacity = 1 + reloadBufferBasedConf() + spinboxBufferReservoirThreshold.value = bufferReservoirThreshold*100 + spinboxBufferMaxThreshold.value = bufferMaxThreshold*100 + rectangleBufferBasedConf.enabled = false + rectangleBufferBasedConf.opacity = 0 + reloadBufferRateBasedConf() + spinboxAdaptechFirstThreshold.value = adaptechFirstThreshold*100 + spinboxAdaptechSecondThreshold.value = adaptechSecondThreshold*100 + spinboxAdaptechSwitchUpMargin.value = adaptechSwitchUpMargin*100 + spinboxAdaptechSlackParameter.value = adaptechSlackParameter*100 + spinboxAdaptechAlpha.value = adaptechAlpha*100 + rectangleBufferRateBasedConf.enabled = false + rectangleBufferRateBasedConf.opacity = 0 + reloadBufferThreeThresholdConf() + spinboxBufferThreeThresoldFirst.value = bufferThreeThresholdFirst*100 + spinboxBufferThreeThresoldSecond.value = bufferThreeThresholdSecond*100 + spinboxBufferThreeThresoldThird.value = bufferThreeThresholdThird*100 + rectangleBufferThreeThresholdConf.enabled = false + rectangleBufferThreeThresholdConf.opacity = 0 + reloadPandaConf() + spinboxPandaParamAlpha.value = pandaParamAlpha*100 + spinboxPandaParamBeta.value = pandaParamBeta*100 + spinboxPandaParamBMin.value = pandaParamBMin*100 + spinboxPandaParamK.value = pandaParamK*100 + spinboxPandaParamW.value = pandaParamW + spinboxPandaParamEpsilon.value = pandaParamEpsilon*100 + rectanglePandaConf.enabled = false + rectanglePandaConf.opacity = 0 + reloadBolaConf() + spinboxBolaBufferTarget.value = bolaBufferTarget*100 + spinboxBolaAlpha.value = bolaAlpha*100 + rectangleBolaConf.enabled = false + rectangleBolaConf.opacity = 0 + break + case 2: + reloadRateBasedConf() + spinboxRateAlpha.value = rateAlpha*100 + rectangleRateBasedConf.enabled = false + rectangleRateBasedConf.opacity = 0 + rectangleBufferBasedConf.enabled = true + rectangleBufferBasedConf.opacity = 1 + reloadBufferRateBasedConf() + spinboxAdaptechFirstThreshold.value = adaptechFirstThreshold*100 + spinboxAdaptechSecondThreshold.value = adaptechSecondThreshold*100 + spinboxAdaptechSwitchUpMargin.value = adaptechSwitchUpMargin*100 + spinboxAdaptechSlackParameter.value = adaptechSlackParameter*100 + spinboxAdaptechAlpha.value = adaptechAlpha*100 + rectangleBufferRateBasedConf.enabled = false + rectangleBufferRateBasedConf.opacity = 0 + reloadBufferThreeThresholdConf() + spinboxBufferThreeThresoldFirst.value = bufferThreeThresholdFirst*100 + spinboxBufferThreeThresoldSecond.value = bufferThreeThresholdSecond*100 + spinboxBufferThreeThresoldThird.value = bufferThreeThresholdThird*100 + rectangleBufferThreeThresholdConf.enabled = false + rectangleBufferThreeThresholdConf.opacity = 0 + reloadPandaConf() + spinboxPandaParamAlpha.value = pandaParamAlpha*100 + spinboxPandaParamBeta.value = pandaParamBeta*100 + spinboxPandaParamBMin.value = pandaParamBMin*100 + spinboxPandaParamK.value = pandaParamK*100 + spinboxPandaParamW.value = pandaParamW + spinboxPandaParamEpsilon.value = pandaParamEpsilon*100 + rectanglePandaConf.enabled = false + rectanglePandaConf.opacity = 0 + reloadBolaConf() + spinboxBolaBufferTarget.value = bolaBufferTarget*100 + spinboxBolaAlpha.value = bolaAlpha*100 + rectangleBolaConf.enabled = false + rectangleBolaConf.opacity = 0 + break + case 3: + reloadRateBasedConf() + spinboxRateAlpha.value = rateAlpha*100 + rectangleRateBasedConf.enabled = false + rectangleRateBasedConf.opacity = 0 + reloadBufferBasedConf() + spinboxBufferReservoirThreshold.value = bufferReservoirThreshold*100 + spinboxBufferMaxThreshold.value = bufferMaxThreshold*100 + rectangleBufferBasedConf.enabled = false + rectangleBufferBasedConf.opacity = 0 + rectangleBufferRateBasedConf.enabled = true + rectangleBufferRateBasedConf.opacity = 1 + reloadBufferThreeThresholdConf() + spinboxBufferThreeThresoldFirst.value = bufferThreeThresholdFirst*100 + spinboxBufferThreeThresoldSecond.value = bufferThreeThresholdSecond*100 + spinboxBufferThreeThresoldThird.value = bufferThreeThresholdThird*100 + rectangleBufferThreeThresholdConf.enabled = false + rectangleBufferThreeThresholdConf.opacity = 0 + reloadPandaConf() + spinboxPandaParamAlpha.value = pandaParamAlpha*100 + spinboxPandaParamBeta.value = pandaParamBeta*100 + spinboxPandaParamBMin.value = pandaParamBMin*100 + spinboxPandaParamK.value = pandaParamK*100 + spinboxPandaParamW.value = pandaParamW + spinboxPandaParamEpsilon.value = pandaParamEpsilon*100 + rectanglePandaConf.enabled = false + rectanglePandaConf.opacity = 0 + reloadBolaConf() + spinboxBolaBufferTarget.value = bolaBufferTarget*100 + spinboxBolaAlpha.value = bolaAlpha*100 + rectangleBolaConf.enabled = false + rectangleBolaConf.opacity = 0 + break + case 4: + reloadRateBasedConf() + spinboxRateAlpha.value = rateAlpha*100 + rectangleRateBasedConf.enabled = false + rectangleRateBasedConf.opacity = 0 + reloadBufferBasedConf() + spinboxBufferReservoirThreshold.value = bufferReservoirThreshold*100 + spinboxBufferMaxThreshold.value = bufferMaxThreshold*100 + rectangleBufferBasedConf.enabled = false + rectangleBufferBasedConf.opacity = 0 + reloadBufferRateBasedConf() + spinboxAdaptechFirstThreshold.value = adaptechFirstThreshold*100 + spinboxAdaptechSecondThreshold.value = adaptechSecondThreshold*100 + spinboxAdaptechSwitchUpMargin.value = adaptechSwitchUpMargin*100 + spinboxAdaptechSlackParameter.value = adaptechSlackParameter*100 + spinboxAdaptechAlpha.value = adaptechAlpha*100 + rectangleBufferRateBasedConf.enabled = false + rectangleBufferRateBasedConf.opacity = 0 + rectangleBufferThreeThresholdConf.enabled = true + rectangleBufferThreeThresholdConf.opacity = 1 + reloadPandaConf() + spinboxPandaParamAlpha.value = pandaParamAlpha*100 + spinboxPandaParamBeta.value = pandaParamBeta*100 + spinboxPandaParamBMin.value = pandaParamBMin*100 + spinboxPandaParamK.value = pandaParamK*100 + spinboxPandaParamW.value = pandaParamW + spinboxPandaParamEpsilon.value = pandaParamEpsilon*100 + rectanglePandaConf.enabled = false + rectanglePandaConf.opacity = 0 + reloadBolaConf() + spinboxBolaBufferTarget.value = bolaBufferTarget*100 + spinboxBolaAlpha.value = bolaAlpha*100 + rectangleBolaConf.enabled = false + rectangleBolaConf.opacity = 0 + break + case 5: + reloadRateBasedConf() + spinboxRateAlpha.value = rateAlpha*100 + rectangleRateBasedConf.enabled = false + rectangleRateBasedConf.opacity = 0 + reloadBufferBasedConf() + spinboxBufferReservoirThreshold.value = bufferReservoirThreshold*100 + spinboxBufferMaxThreshold.value = bufferMaxThreshold*100 + rectangleBufferBasedConf.enabled = false + rectangleBufferBasedConf.opacity = 0 + reloadBufferRateBasedConf() + spinboxAdaptechFirstThreshold.value = adaptechFirstThreshold*100 + spinboxAdaptechSecondThreshold.value = adaptechSecondThreshold*100 + spinboxAdaptechSwitchUpMargin.value = adaptechSwitchUpMargin*100 + spinboxAdaptechSlackParameter.value = adaptechSlackParameter*100 + spinboxAdaptechAlpha.value = adaptechAlpha*100 + rectangleBufferRateBasedConf.enabled = false + rectangleBufferRateBasedConf.opacity = 0 + reloadBufferThreeThresholdConf() + spinboxBufferThreeThresoldFirst.value = bufferThreeThresholdFirst*100 + spinboxBufferThreeThresoldSecond.value = bufferThreeThresholdSecond*100 + spinboxBufferThreeThresoldThird.value = bufferThreeThresholdThird*100 + rectangleBufferThreeThresholdConf.enabled = false + rectangleBufferThreeThresholdConf.opacity = 0 + rectanglePandaConf.enabled = true + rectanglePandaConf.opacity = 1 + reloadBolaConf() + spinboxBolaBufferTarget.value = bolaBufferTarget*100 + spinboxBolaAlpha.value = bolaAlpha*100 + rectangleBolaConf.enabled = false + rectangleBolaConf.opacity = 0 + break + case 6: + reloadRateBasedConf() + spinboxRateAlpha.value = rateAlpha*100 + rectangleRateBasedConf.enabled = false + rectangleRateBasedConf.opacity = 0 + reloadBufferBasedConf() + spinboxBufferReservoirThreshold.value = bufferReservoirThreshold*100 + spinboxBufferMaxThreshold.value = bufferMaxThreshold*100 + rectangleBufferBasedConf.enabled = false + rectangleBufferBasedConf.opacity = 0 + reloadBufferRateBasedConf() + spinboxAdaptechFirstThreshold.value = adaptechFirstThreshold*100 + spinboxAdaptechSecondThreshold.value = adaptechSecondThreshold*100 + spinboxAdaptechSwitchUpMargin.value = adaptechSwitchUpMargin*100 + spinboxAdaptechSlackParameter.value = adaptechSlackParameter*100 + spinboxAdaptechAlpha.value = adaptechAlpha*100 + rectangleBufferRateBasedConf.enabled = false + rectangleBufferRateBasedConf.opacity = 0 + reloadBufferThreeThresholdConf() + spinboxBufferThreeThresoldFirst.value = bufferThreeThresholdFirst*100 + spinboxBufferThreeThresoldSecond.value = bufferThreeThresholdSecond*100 + spinboxBufferThreeThresoldThird.value = bufferThreeThresholdThird*100 + rectangleBufferThreeThresholdConf.enabled = false + rectangleBufferThreeThresholdConf.opacity = 0 + reloadPandaConf() + spinboxPandaParamAlpha.value = pandaParamAlpha*100 + spinboxPandaParamBeta.value = pandaParamBeta*100 + spinboxPandaParamBMin.value = pandaParamBMin*100 + spinboxPandaParamK.value = pandaParamK*100 + spinboxPandaParamW.value = pandaParamW + spinboxPandaParamEpsilon.value = pandaParamEpsilon*100 + rectanglePandaConf.enabled = false + rectanglePandaConf.opacity = 0 + rectangleBolaConf.enabled = true + rectangleBolaConf.opacity = 1 + break + } + } + currentIndex: find(adaptationLogic) + } + + Item { + id: switchRectangle + anchors.left: comboAdaptationSetList.right + anchors.top: parent.top + anchors.leftMargin: Utils.scaled(12) + + Label { + text: "TCP" + id: labelLegacy + color: "white" + anchors.top: parent.top + anchors.left: parent.left + font.bold: true + font.pixelSize: switchIcn.height + } + + Switch { + id: switchIcn + height: comboAdaptationSetList.height + anchors.top: parent.top + anchors.left: labelLegacy.right + checked: icn + } + + Label { + id: labelIcn + color: "white" + anchors.top: parent.top + anchors.right: parent.right + anchors.left: switchIcn.right + text: "ICN" + font.bold: true + font.pixelSize: switchIcn.height + } + } + } + + Item { + id: itemIcnPrefix + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow + + Label { + text: "ICN Prefix:" + id: labelIcnPrefix + color: " white" + anchors.top: parent.top + anchors.right: textInputIcnPrefix.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (textInputIcnPrefix.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + TextInput { + width: parent.width/4*3 + id: textInputIcnPrefix + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + font.pixelSize: Utils.scaled(20) + color: "white" + text: icnPrefix + } + } + + Item { + id: itemIcnSuffix + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow + + Label { + text: "ICN Suffix:" + id: labelIcnSuffix + color: " white" + anchors.top: parent.top + anchors.right: textInputIcnSuffix.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (textInputIcnSuffix.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + TextInput { + width: parent.width/4*3 + id: textInputIcnSuffix + anchors.top: parent.top + anchors.right: parent.right + font.pixelSize: Utils.scaled(20) + color: "white" + text: icnSuffix + } + } + + Item { + id: itemHttpPrefix + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + 2*heightRow + + Label { + text: "TCP Prefix:" + id: labelHttpPrefix + color: " white" + anchors.top: parent.top + anchors.right: textInputHttpPrefix.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (textInputHttpPrefix.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + TextInput { + width: parent.width/4*3 + id: textInputHttpPrefix + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + font.pixelSize: Utils.scaled(20) + color: "white" + text: httpPrefix + } + } + + Item { + id: itemHttpSuffix + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + 2*heightRow + + Label { + text: "TCP Suffix:" + id: labelHttpSuffix + color: " white" + anchors.top: parent.top + anchors.right: textInputHttpSuffix.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (textInputHttpSuffix.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + TextInput { + width: parent.width/4*3 + id: textInputHttpSuffix + anchors.top: parent.top + anchors.right: parent.right + font.pixelSize: Utils.scaled(20) + color: "white" + text: httpSuffix + } + } + + Item { + id: itemSegmentBufferSize + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + 3*heightRow + + Label { + text: "Segment Buffer Size" + id: labelSegmentBufferSize + color: " white" + anchors.top: parent.top + anchors.right: spinboxSegmentBufferSize.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxSegmentBufferSize.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxSegmentBufferSize + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: segmentBufferSize*100 + to: 10000 + stepSize: 100 + property int decimals: 0 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxSegmentBufferSize.from, spinboxSegmentBufferSize.to) + top: Math.max(spinboxSegmentBufferSize.from, spinboxSegmentBufferSize.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxSegmentBufferSize.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Rectangle { + id: rectangleRateBasedConf + z: parent.z + 1 + enabled: false + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + 4*heightRow + + Label { + text: "Rate Based Conf" + id: labelRateBasedConf + color: " white" + anchors.top: parent.top + anchors.leftMargin: Utils.scaled(10) + anchors.left: parent.left + font.bold: true + font.pixelSize: Utils.scaled(20); + } + + Item { + id: itemRateAlpha + anchors.top: labelRateBasedConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Rate\nAlpha" + id: labelRateAlpha + color: " white" + anchors.top: parent.top + anchors.right: spinboxRateAlpha.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxRateAlpha.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxRateAlpha + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: rateAlpha*100 + to: 100000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxRateAlpha.from, spinboxRateAlpha.to) + top: Math.max(spinboxRateAlpha.from, spinboxRateAlpha.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxRateAlpha.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + } + + Rectangle { + id: rectangleBufferBasedConf + radius: Utils.scaled(10) + z: parent.z + 1 + enabled: false + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + 4*heightRow + + Label { + text: "Buffer Based Conf" + id: labelBufferBasedConf + color: " white" + anchors.top: parent.top + anchors.leftMargin: Utils.scaled(10) + anchors.left: parent.left + font.bold: true + font.pixelSize: Utils.scaled(20); + } + + Item { + id: itemBufferReservoirThreshold + anchors.top: labelBufferBasedConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Buffer Reservoir\nThreshold" + id: labelBufferReservoirThreshold + color: " white" + anchors.top: parent.top + anchors.right: spinboxBufferReservoirThreshold.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBufferReservoirThreshold.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxBufferReservoirThreshold + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: bufferReservoirThreshold*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxBufferReservoirThreshold.from, spinboxBufferReservoirThreshold.to) + top: Math.max(spinboxBufferReservoirThreshold.from, spinboxBufferReservoirThreshold.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxBufferReservoirThreshold.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemBufferMaxThreshold + anchors.top: labelBufferBasedConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Buffer Max\nThreshold" + id: labelBufferMaxThreshold + color: " white" + anchors.top: parent.top + anchors.right: spinboxBufferMaxThreshold.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBufferMaxThreshold.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxBufferMaxThreshold + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 0 + value: bufferMaxThreshold*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxBufferMaxThreshold.from, spinboxBufferMaxThreshold.to) + top: Math.max(spinboxBufferMaxThreshold.from, spinboxBufferMaxThreshold.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxBufferMaxThreshold.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + } + + Rectangle { + id: rectangleBufferRateBasedConf + z: parent.z + 1 + enabled: false + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + 4*heightRow + + Label { + text: "Buffer Rate Based Conf" + id: labelBufferRateBasedConf + color: " white" + anchors.top: parent.top + anchors.leftMargin: Utils.scaled(10) + anchors.left: parent.left + font.bold: true + font.pixelSize: Utils.scaled(20); + } + + Item { + id: itemAdaptechFirstThreshold + anchors.top: labelBufferRateBasedConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Adaptech First\nThreshold" + id: labelAdaptechFirstThreshold + color: " white" + anchors.top: parent.top + anchors.right: spinboxAdaptechFirstThreshold.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxAdaptechFirstThreshold.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxAdaptechFirstThreshold + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: adaptechFirstThreshold*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator{ + bottom: Math.min(spinboxAdaptechFirstThreshold.from, spinboxAdaptechFirstThreshold.to) + top: Math.max(spinboxAdaptechFirstThreshold.from, spinboxAdaptechFirstThreshold.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxAdaptechFirstThreshold.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemAdaptechSecondThreshold + anchors.top: labelBufferRateBasedConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Adaptech\nSecond\nThreshold" + id: labelAdaptechSecondThreshold + color: " white" + anchors.top: parent.top + anchors.right: spinboxAdaptechSecondThreshold.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxAdaptechSecondThreshold.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxAdaptechSecondThreshold + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 0 + value: adaptechSecondThreshold*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxAdaptechSecondThreshold.from, spinboxAdaptechSecondThreshold.to) + top: Math.max(spinboxAdaptechSecondThreshold.from, spinboxAdaptechSecondThreshold.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxAdaptechSecondThreshold.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemAdaptechSwitchUpMargin + anchors.top: labelBufferRateBasedConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Adaptech\nSwitchUp\nMargin" + id: labelAdaptechswitchUpMargin + color: " white" + anchors.top: parent.top + anchors.right: spinboxAdaptechSwitchUpMargin.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxAdaptechSwitchUpMargin.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxAdaptechSwitchUpMargin + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(700) + from: 0 + value: adaptechSwitchUpMargin*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxAdaptechSwitchUpMargin.from, spinboxAdaptechSwitchUpMargin.to) + top: Math.max(spinboxAdaptechSwitchUpMargin.from, spinboxAdaptechSwitchUpMargin.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxAdaptechSwitchUpMargin.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemAdaptechSlackParameter + anchors.top: labelBufferRateBasedConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Adaptech\nSlack\nParameter" + id: labelAdaptechSwitchUpMargin + color: " white" + anchors.top: parent.top + anchors.right: spinboxAdaptechSlackParameter.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxAdaptechSlackParameter.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxAdaptechSlackParameter + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(950) + from: 0 + value: adaptechSlackParameter*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxAdaptechSlackParameter.from, spinboxAdaptechSlackParameter.to) + top: Math.max(spinboxAdaptechSlackParameter.from, spinboxAdaptechSlackParameter.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxAdaptechSlackParameter.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemAdaptechAlpha + anchors.top: labelBufferRateBasedConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow + + Label { + textFormat: Text.RichText + text: "Adaptech\nAlpha" + id: labelAdaptechAlpha + color: " white" + anchors.top: parent.top + anchors.right: spinboxAdaptechAlpha.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxAdaptechAlpha.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxAdaptechAlpha + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: adaptechAlpha*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxAdaptechAlpha.from, spinboxAdaptechAlpha.to) + top: Math.max(spinboxAdaptechAlpha.from, spinboxAdaptechAlpha.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxAdaptechAlpha.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + } + + Rectangle { + id: rectangleBufferThreeThresholdConf + radius: Utils.scaled(10) + z: parent.z + 1 + enabled: false + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + 4*heightRow + + Label { + text: "Buffer Three Conf" + id: labelBufferThreeThresholdConf + color: " white" + anchors.top: parent.top + anchors.leftMargin: Utils.scaled(10) + anchors.left: parent.left + font.bold: true + font.pixelSize: Utils.scaled(20); + } + + Item { + id: itemBufferThreeThresoldFirst + anchors.top: labelBufferThreeThresholdConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Buffer Three\nThreshold\nFirst" + id: labelBufferThreeThresoldFirst + color: " white" + anchors.top: parent.top + anchors.right: spinboxBufferThreeThresoldFirst.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBufferThreeThresoldFirst.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxBufferThreeThresoldFirst + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: bufferThreeThresholdFirst*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxBufferThreeThresoldFirst.from, spinboxBufferThreeThresoldFirst.to) + top: Math.max(spinboxBufferThreeThresoldFirst.from, spinboxBufferThreeThresoldFirst.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxBufferThreeThresoldFirst.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemBufferThreeThresoldSecond + anchors.top: labelBufferThreeThresholdConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Buffer Three\nThreshold\nSecond" + id: labelBufferThreeThresoldSecond + color: " white" + anchors.top: parent.top + anchors.right: spinboxBufferThreeThresoldSecond.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBufferThreeThresoldSecond.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxBufferThreeThresoldSecond + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 0 + value: bufferThreeThresholdSecond*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxBufferThreeThresoldSecond.from, spinboxBufferThreeThresoldSecond.to) + top: Math.max(spinboxBufferThreeThresoldSecond.from, spinboxBufferThreeThresoldSecond.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxBufferThreeThresoldSecond.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemBufferThreeThresoldThird + anchors.top: labelBufferThreeThresholdConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Buffer Three\nThreshold\nThird" + id: labelBufferThreeThresoldThird + color: " white" + anchors.top: parent.top + anchors.right: spinboxBufferThreeThresoldThird.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBufferThreeThresoldThird.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxBufferThreeThresoldThird + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(700) + from: 0 + value: bufferThreeThresholdThird*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxBufferThreeThresoldThird.from, spinboxBufferThreeThresoldThird.to) + top: Math.max(spinboxBufferThreeThresoldThird.from, spinboxBufferThreeThresoldThird.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxBufferThreeThresoldThird.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + } + + Rectangle { + id: rectanglePandaConf + radius: Utils.scaled(10) + z: parent.z + 1 + enabled: false + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + 4*heightRow + + Label { + text: "Panda Conf" + id: labelPandaConf + color: " white" + anchors.top: parent.top + anchors.leftMargin: Utils.scaled(10) + anchors.left: parent.left + font.bold: true + font.pixelSize: Utils.scaled(20); + } + + Item { + id: itemPandaParamAlpha + anchors.top: labelPandaConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + + Label { + textFormat: Text.RichText + text: "Param α" + id: labelPandaParamAlpha + color: " white" + anchors.top: parent.top + anchors.right: spinboxPandaParamAlpha.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxPandaParamAlpha.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxPandaParamAlpha + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: pandaParamAlpha*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxPandaParamAlpha.from, spinboxPandaParamAlpha.to) + top: Math.max(spinboxPandaParamAlpha.from, spinboxPandaParamAlpha.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxPandaParamAlpha.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + + Item { + id: itemPandaParamBeta + anchors.top: labelPandaConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + + Label { + textFormat: Text.RichText + text: "Param β" + id: labelPandaParamBeta + color: " white" + anchors.top: parent.top + anchors.right: spinboxPandaParamBeta.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxPandaParamBeta.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxPandaParamBeta + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 0 + value: pandaParamBeta*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxPandaParamBeta.from, spinboxPandaParamBeta.to) + top: Math.max(spinboxPandaParamBeta.from, spinboxPandaParamBeta.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxPandaParamBeta.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemPandaParamBMin + anchors.top: labelPandaConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + + Label { + textFormat: Text.RichText + text: "Param Bmin" + id: labelPandaParamBMin + color: " white" + anchors.top: parent.top + anchors.right: spinboxPandaParamBMin.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxPandaParamBMin.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxPandaParamBMin + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(700) + from: 0 + value: pandaParamBMin*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxPandaParamBMin.from, spinboxPandaParamBMin.to) + top: Math.max(spinboxPandaParamBMin.from, spinboxPandaParamBMin.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxPandaParamBMin.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemPandaParamK + anchors.top: labelPandaConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Param K" + id: labelPandaParamK + color: " white" + anchors.top: parent.top + anchors.right: spinboxPandaParamK.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxPandaParamK.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxPandaParamK + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(950) + from: 0 + value: pandaParamK*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxPandaParamK.from, spinboxPandaParamK.to) + top: Math.max(spinboxPandaParamK.from, spinboxPandaParamK.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxPandaParamK.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemPandaParamW + anchors.top: labelPandaConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow + + Label { + textFormat: Text.RichText + text: "Param ω" + id: labelPandaParamW + color: " white" + anchors.top: parent.top + anchors.right: spinboxPandaParamW.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxPandaParamW.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxPandaParamW + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: pandaParamW + to: 1000000 + stepSize: 1 + } + } + + Item { + id: itemPandaParamEpsilon + anchors.top: labelPandaConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + heightRow + + Label { + textFormat: Text.RichText + text: "Param ε" + id: labelPandaParamEpsilon + color: " white" + anchors.top: parent.top + anchors.right: spinboxPandaParamEpsilon.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxPandaParamEpsilon.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxPandaParamEpsilon + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 0 + value: pandaParamEpsilon*100 + to: 10000 + stepSize: 1 + property int decimals: 2 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxPandaParamEpsilon.from, spinboxPandaParamEpsilon.to) + top: Math.max(spinboxPandaParamEpsilon.from, spinboxPandaParamEpsilon.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxPandaParamEpsilon.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + } + + Rectangle { + id: rectangleBolaConf + radius: Utils.scaled(10) + z: parent.z + 1 + enabled: false + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: Utils.scaled(12) + 4*heightRow + + Label { + text: "Bola Conf" + id: labelBolaConf + color: " white" + anchors.top: parent.top + anchors.leftMargin: Utils.scaled(10) + anchors.left: parent.left + font.bold: true + font.pixelSize: Utils.scaled(20); + + } + + Item { + id: itemBolaBufferTarget + anchors.top: labelBolaConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: Utils.scaled(parent.width/2) + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Bola Buffer\nTarget" + id: labelBolaBufferTarget + color: " white" + anchors.top: parent.top + anchors.right: spinboxBolaBufferTarget.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBolaBufferTarget.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxBolaBufferTarget + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(200) + from: 0 + value: bolaBufferTarget*100 + to: 100000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxBolaBufferTarget.from, spinboxBolaBufferTarget.to) + top: Math.max(spinboxBolaBufferTarget.from, spinboxBolaBufferTarget.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxBolaBufferTarget.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + + Item { + id: itemBolaAlpha + anchors.top: labelBolaConf.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: parent.width/2 + anchors.topMargin: Utils.scaled(12) + + Label { + text: "Bola\nAlpha" + id: labelBolaAlpha + color: " white" + anchors.top: parent.top + anchors.right: spinboxBolaAlpha.left + anchors.rightMargin: Utils.scaled(5) + anchors.topMargin: (spinboxBolaAlpha.height - height)/2 + font.bold: true + font.pixelSize: Utils.scaled(10); + } + + SpinBox { + id: spinboxBolaAlpha + z: parent.z + 1 + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: Utils.scaled(450) + from: 0 + value: bolaAlpha*100 + to: 10000 + stepSize: 10 + property int decimals: 1 + property real realValue: value / 100 + + validator: DoubleValidator { + bottom: Math.min(spinboxBolaAlpha.from, spinboxBolaAlpha.to) + top: Math.max(spinboxBolaAlpha.from, spinboxBolaAlpha.to) + } + + textFromValue: function(value, locale) + { + return Number(value / 100).toLocaleString(locale, 'f', spinboxBolaAlpha.decimals) + } + + valueFromText: function(text, locale) + { + return Number.fromLocaleString(locale, text) * 100 + } + } + } + } + + Item { + id: itemButton + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.rightMargin: Utils.scaled(12) + anchors.bottomMargin: Utils.scaled(12) + + Button { + id: cancelBtn + z: parent.z + 1 + text: "Cancel" + anchors.right: saveBtn.left + anchors.bottom: parent.bottom + anchors.rightMargin: Utils.scaled(5) + + onClicked: { + closeOptions(); + } + } + + Button { + id: saveBtn + z: parent.z + 1 + anchors.right: parent.right + anchors.bottom: parent.bottom + text: "Save" + + onClicked: { + saveAdaptationLogic(adaptationLogicModel.get(comboAdaptationSetList.currentIndex).text, comboAdaptationSetList.currentIndex); + saveIcn(switchIcn.checked) + saveIcnPrefix(textInputIcnPrefix.text) + saveHttpPrefix(textInputHttpPrefix.text) + saveIcnSuffix(textInputIcnSuffix.text) + saveHttpSuffix(textInputHttpSuffix.text) + saveSegmentBufferSize(spinboxSegmentBufferSize.value/100) + saveRateAlpha(spinboxRateAlpha.value/100) + saveBufferReservoirThreshold(spinboxBufferReservoirThreshold.value/100) + saveBufferMaxThreshold(spinboxBufferMaxThreshold.value/100) + saveAdaptechFirstThreshold(spinboxAdaptechFirstThreshold.value/100) + saveAdaptechSecondThreshold(spinboxAdaptechSecondThreshold.value/100) + saveAdaptechSwitchUpMargin(spinboxAdaptechSwitchUpMargin.value/100) + saveAdaptechSlackParameter(spinboxAdaptechSlackParameter.value/100) + saveAdaptechAlpha(spinboxAdaptechAlpha.value/100) + saveBufferThreeThresholdFirst(spinboxBufferThreeThresoldFirst.value/100) + saveBufferThreeThresholdSecond(spinboxBufferThreeThresoldSecond.value/100) + saveBufferThreeThresholdThird(spinboxBufferThreeThresoldThird.value/100) + savePandaParamAlpha(spinboxPandaParamAlpha.value/100) + savePandaParamBeta(spinboxPandaParamBeta.value/100) + savePandaParamBMin(spinboxPandaParamBMin.value/100) + savePandaParamK(spinboxPandaParamK.value/100) + savePandaParamW(spinboxPandaParamW.value) + savePandaParamEpsilon(spinboxPandaParamEpsilon.value/100) + saveBolaBufferTarget(spinboxBolaBufferTarget.value/100) + saveBolaAlpha(spinboxBolaAlpha.value/100) + dashPlayer.reloadParameters() + closeOptions(); + } + } + } +} diff --git a/qml/Viper/ProgressBar.qml b/qml/Viper/ProgressBar.qml new file mode 100755 index 00000000..3d0ec228 --- /dev/null +++ b/qml/Viper/ProgressBar.qml @@ -0,0 +1,124 @@ +/****************************************************************************** + Copyright (C) 2013-2016 Wang Bin + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + +import QtQuick 2.0 +import "utils.js" as Utils + +Rectangle { + id: root + color: "#44eeeeee" + radius: Utils.scaled(5) + property alias value: grip.value + property color lineColor: "#880000ee" + property color gripColor: "white" + property real gripSize: Utils.scaled(8) + property real gripTolerance: Utils.scaled(3.0) + property real increment: 0.1 + property bool showGrip: true + property bool tracking: true + signal valueChangedByUi + signal hoverAt(real value) + // dx, dy: only the direction. dx>0 means enter from left or leave to left + signal enter(point pos, point dpos) + signal leave(point pos, point dpos) + + Rectangle { + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + radius: parent.radius + width: grip.x + grip.radius + height: parent.height + color: displayedColor(root.lineColor) + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + + onClicked: { + if (parent.width) { + parent.value = mouse.x / parent.width + valueChangedByUi(parent.value) + } + } + + onMouseXChanged: { + hoverAt(mouseX/parent.width) + } + + onEntered: { + enter(Qt.point(mouseX, mouseY), Qt.point(0, mouseY > height/2 ? 1 : -1)) + hoverAt(mouseX/parent.width) + } + + onExited: { + leave(Qt.point(mouseX, mouseY), Qt.point(0, mouseY > height/2 ? 1 : -1)) + } + } + + Rectangle { + id: grip + property real value: 0 + x: (value * parent.width - width/2) + anchors.verticalCenter: parent.verticalCenter + width: root.gripTolerance * root.gripSize + height: parent.height + radius: width/2 + color: "transparent" + + MouseArea { + id: mouseArea + enabled: root.enabled + anchors.fill: parent + drag { + target: grip + axis: Drag.XAxis + minimumX: -parent.width/2 + maximumX: root.width - parent.width/2 + } + + onPositionChanged: { + if (drag.active) + updatePosition() + } + + onReleased: { + updatePosition() + } + + function updatePosition() + { + value = (grip.x + grip.width/2) / grip.parent.width + valueChangedByUi(value) + } + } + + Rectangle { + anchors.centerIn: parent + width: root.gripSize + height: parent.height + radius: width/2 + color: root.showGrip ? root.gripColor : "transparent" + } + } + + function displayedColor(c) + { + var tint = Qt.rgba(c.r, c.g, c.b, 0.25) + return enabled ? c : Qt.tint(c, tint) + } +} diff --git a/qml/Viper/Slider.qml b/qml/Viper/Slider.qml new file mode 100755 index 00000000..058bfd8e --- /dev/null +++ b/qml/Viper/Slider.qml @@ -0,0 +1,117 @@ +/****************************************************************************** + Copyright (C) 2013-2016 Wang Bin + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + +import QtQuick 2.0 +import "utils.js" as Utils + +Item { + id: root + property alias value: grip.value + property color fillColor: "white" + property color lineColor: "blue" + property real lineWidth: Utils.scaled(6) + property color gripColor: "white" + property real gripSize: Utils.scaled(12) + property real gripTolerance: Utils.scaled(3.0) + property int orientation: Qt.Horizontal + property bool hovered: false //mouseArea.containsMouse || gripMouseArea.containsMouse + property real max: 1 + property real min: 0 + + Rectangle { + anchors.centerIn: parent + height: orientation === Qt.Horizontal ? lineWidth : parent.height + width: orientation === Qt.Horizontal ? parent.width : lineWidth + color: lineColor + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + + onHoveredChanged: { + if (mouseX > 65535) //qt5.6 touch screen release finger becomes very large e.g. 0x7fffffff + return + hovered = mouseArea.containsMouse + } + + onClicked: { + var newValue = min + (mouse.x / parent.width)*(max-min) + if (orientation === Qt.Horizontal) { + newValue = min + (mouse.x / parent.width)*(max-min) + } else { + newValue = min + (mouse.y / parent.height)*(max-min) + } + var increment = 1.0/width + if (Math.abs(newValue - parent.value) > parent.increment*(max-min)) { + if (newValue > parent.value) + parent.value = Math.min(max, parent.value + parent.increment*(max-min)) + else + parent.value = Math.max(min, parent.value - parent.increment*(max-min)) + } + } + } + + Item { + id: grip + property real value: 0.5 + x: orientation === Qt.Horizontal ? ((value-min)/(max-min) * parent.width - width/2) : (parent.width - width)/2 + y: orientation === Qt.Horizontal ? (parent.height - height)/2 : ((value-min)/(max-min) * parent.height - height/2) + width: root.gripTolerance * root.gripSize + height: width + readonly property real radius: width/2 + + MouseArea { + id: gripMouseArea + anchors.fill: parent + hoverEnabled: true + onHoveredChanged: { + if (mouseX > 65535) //qt5.6 touch screen release finger becomes very large e.g. 0x7fffffff + return + hovered = gripMouseArea.containsMouse + } + + drag { + target: grip + axis: orientation === Qt.Horizontal ? Drag.XAxis : Drag.YAxis + minimumX: orientation === Qt.Horizontal ? -parent.radius : 0 + maximumX: orientation === Qt.Horizontal ? root.width - parent.radius : 0 + minimumY: orientation === Qt.Horizontal ? 0 : -parent.radius + maximumY: orientation === Qt.Horizontal ? 0 : root.height - parent.radius + } + + onPositionChanged: { + if (drag.active) + updatePosition() + } + onReleased: updatePosition() + function updatePosition() { + if (orientation === Qt.Horizontal) + value = min + ((grip.x + grip.radius) / grip.parent.width)*(max-min) + else + value = min + ((grip.y + grip.radius) / grip.parent.height)*(max-min) + } + } + + Rectangle { + anchors.centerIn: parent + width: root.gripSize + height: width + radius: width/2 + color: root.gripColor + } + } +} diff --git a/qml/Viper/VideoCodec.qml b/qml/Viper/VideoCodec.qml new file mode 100755 index 00000000..d07c97ec --- /dev/null +++ b/qml/Viper/VideoCodec.qml @@ -0,0 +1,150 @@ +/****************************************************************************** + QtAV: Multimedia framework based on Qt and FFmpeg + Copyright (C) 2012-2016 Wang Bin +* This file is part of QtAV (from 2013) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + +import QtQuick 2.0 +import "utils.js" as Utils + +Page { + id: root + title: qsTr("Video Codec") + height: titleHeight + detail.height + listView.height + copyMode.height + Utils.kSpacing*4 + signal zeroCopyChanged(bool value) + + QtObject { + id: d + property Item selectedItem + property string detail: qsTr("Takes effect on the next play") + } + + Column { + anchors.fill: content + spacing: Utils.kSpacing + + Text { + id: detail + text: d.detail + color: "white" + height: contentHeight + 1.6*Utils.kItemHeight + width: parent.width + font.pixelSize: Utils.kFontSize + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + ListView { + id: listView + contentWidth: parent.width - Utils.scaled(20) + height: Utils.kItemHeight + + anchors { + //topMargin: Utils.kMargin + horizontalCenter: parent.horizontalCenter + } + + onContentWidthChanged: { + anchors.leftMargin = Math.max(10, (parent.width - contentWidth)/2) + anchors.rightMargin = anchors.leftMargin + width = parent.width - 2*anchors.leftMargin + } + + orientation: ListView.Horizontal + spacing: Utils.scaled(6) + focus: true + + delegate: contentDelegate + model: codecMode + } + + ListModel { + id: codecMode + ListElement { name: "FFmpeg"; hardware: false; zcopy: false; description: "FFmpeg/Libav" } + } + + Component { + id: contentDelegate + DelegateItem { + id: delegateItem + text: name + //width: Utils.kItemWidth + height: Utils.kItemHeight + color: "#aa000000" + selectedColor: "#aa0000cc" + border.color: "white" + + onClicked: { + if (d.selectedItem == delegateItem) + return + if (d.selectedItem) + d.selectedItem.state = "baseState" + d.selectedItem = delegateItem + } + + onStateChanged: { + if (state != "selected") + return + d.detail = description + " " + (hardware ? qsTr("hardware decoding") : qsTr("software decoding")) + if (name === "FFmpeg") + { + copyMode.visible = false + } else { + copyMode.visible = zcopy + d.detail += "\n" + qsTr("Zero Copy support") + ":" + zcopy + } + PlayerConfig.decoderPriorityNames = [ name ] + } + } + } + Button { + id: copyMode + text: qsTr("Zero copy") + checked: PlayerConfig.zeroCopy + checkable: true + width: parent.width + height: Utils.kItemHeight + onCheckedChanged: PlayerConfig.zeroCopy = checked + } + } + Component.onCompleted: { + if (Qt.platform.os == "windows") + { + codecMode.append({ name: "DXVA", hardware: true, zcopy: true, description: "DirectX Video Acceleration (Windows)\nUse OpenGLES(ANGLE) + D3D to support 0-copy" }) + codecMode.append({ name: "D3D11", hardware: true, zcopy: true, description: "D3D11 Video Acceleration\n0-copy is supported under OpenGLES(ANGLE)" }) + codecMode.append({ name: "CUDA", hardware: true, zcopy: true, description: "NVIDIA CUDA (Windows, Linux).\nH264 10bit support."}) + } else if (Qt.platform.os == "winrt" || Qt.platform.os == "winphone") { + codecMode.append({ name: "D3D11", hardware: true, zcopy: true, description: "D3D11 Video Acceleration" }) + } else if (Qt.platform.os == "osx") { + codecMode.append({ name: "VDA", hardware: true, zcopy: true, description: "VDA (OSX)" }) + codecMode.append({ name: "VideoToolbox", hardware: true, zcopy: true, description: "VideoToolbox (OSX)" }) + } else if (Qt.platform.os == "ios") { + codecMode.append({ name: "VideoToolbox", hardware: true, zcopy: true, description: "VideoToolbox (iOS)" }) + } else if(Qt.platform.os == "android") { + codecMode.append({ name: "MediaCodec", hardware: true, zcopy: false, description: "Android 5.0 MediaCodec (H.264)" }) + } else if (Qt.platform.os == "linux") { + codecMode.append({ name: "VAAPI", hardware: true, zcopy: true, description: "VA-API (Linux) " }) + codecMode.append({ name: "CUDA", hardware: true, zcopy: true, description: "NVIDIA CUDA (Windows, Linux)"}) + } + for (var i = 0; i < codecMode.count; ++i) + { + if (codecMode.get(i).name === PlayerConfig.decoderPriorityNames[0]) { + listView.currentIndex = i; + d.selectedItem = listView.currentItem + listView.currentItem.state = "selected" + break + } + } + } +} diff --git a/qml/Viper/main.qml b/qml/Viper/main.qml new file mode 100755 index 00000000..bd90103a --- /dev/null +++ b/qml/Viper/main.qml @@ -0,0 +1,945 @@ +/****************************************************************************** + QtAV: Multimedia framework based on Qt and FFmpeg + Copyright (C) 2012-2016 Wang Bin +* This file is part of QtAV (from 2013) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Dialogs 1.2 +import QtAV 1.7 +import QtQuick.Window 2.1 +import QtQml 2.2 +import "utils.js" as Utils + +Rectangle { + id: root + layer.enabled: false + objectName: "root" + width: Screen.width + height: Screen.height + color: "black" + signal requestFullScreen + signal requestNormalSize + property bool play: false + property bool pause: false + property bool stop: true + property bool buffering: false + property string lastPlayed: "" + property string adaptationLogic: "" + property string icnPrefix: "" + property string httpPrefix: "" + property string icnSuffix: "" + property string httpSuffix: "" + property real alpha: 0 + property real segmentBufferSize: 0 + property bool icn: false + property real rateAlpha:0 + property real bufferReservoirThreshold: 0 + property real bufferMaxThreshold: 0 + property real adaptechFirstThreshold: 0 + property real adaptechSecondThreshold: 0 + property real adaptechSwitchUpMargin: 0 + property real adaptechSlackParameter: 0 + property real adaptechAlpha: 0 + property real bufferThreeThresholdFirst: 0 + property real bufferThreeThresholdSecond: 0 + property real bufferThreeThresholdThird: 0 + property real pandaParamAlpha: 0 + property real pandaParamBeta: 0 + property real pandaParamBMin: 0 + property real pandaParamK: 0 + property real pandaParamW: 0 + property real pandaParamEpsilon: 0 + property real bolaBufferTarget: 0 + property real bolaAlpha: 0 + property int screenWidth: Screen.width + property int screenHeight: Screen.height + property int windowWidth: Window.width + property int pixDens: Math.ceil(Screen.pixelDensity) + property int itemWidth: 25 * pixDens + property int itemHeight: 10 * pixDens + property int scaledMargin: 2 * pixDens + property int fontSize: 5 * pixDens + property string platformName: Qt.platform.os + property bool isPlaying: false + property string graphStatus: "stop" + property bool hasMpd: false + property bool canBuffer: false + property alias keyboardFocus: myKeyboard.focus + property bool repeat: false + property bool graph: false + property bool enabledFullScreen: false + property bool autotune: false + property int lifetime: 0 + property int retransmissions: 0 + property real beta: 0 + property real drop: 0 + property real betaWifi: 0 + property real dropWifi: 0 + property int delayWifi: 0 + property real betaLte: 0 + property real dropLte: 0 + property int delayLte: 0 + property int batchingParameter: 0 + property int rateEstimator: 0 + + function init(argv) + { + console.log("init>>>>>screen density logical: " + Screen.logicalPixelDensity + " pixel: " + Screen.pixelDensity); + } + + function initGraph(initGraphValue) + { + graph = initGraphValue + } + + function initRepeat(initRepeatValue) + { + repeat = initRepeatValue + } + + function initFullScreen(initFullScreen) + { + enabledFullScreen = initFullScreen + } + + function setPlay() + { + play = true + pause = false + stop = false + buffering = false + } + + function setLastPlayed(initLastPlayed) + { + lastPlayed = initLastPlayed + } + + function setAdaptationLogic(initAdaptationLogic) + { + adaptationLogic = initAdaptationLogic + } + + function setIcn(initIcn) + { + icn = initIcn + } + + function setPause() + { + play = true + pause = true + stop = false + buffering = false + } + + function startGraph() + { + graphPanel.startTimer() + } + + function stopGraph() + { + graphPanel.stopTimer() + } + + function pauseGraph() + { + graphPanel.pauseTimer() + } + + + function setStop() + { + control.setStopState() + } + + function setBuffering() + { + canBuffer = true + } + + function unSetBuffering() + { + canBuffer = false + } + + VideoOutput2 { + id: videoOut + focus: false + opengl: true + fillMode: VideoOutput.PreserveAspectFit + anchors.fill: parent + source: player + orientation: 0 + property real zoom: 1 + } + + AVPlayer { + id: player + objectName: "player" + autoPlay: true + videoCodecPriority: PlayerConfig.decoderPriorityNames + onPositionChanged: { + } + + videoCapture { + + } + + onSourceChanged: { + videoOut.zoom = 1 + videoOut.regionOfInterest = Qt.rect(0, 0, 0, 0) + } + + onPlaying: { + control.setPlayingState() + } + onSeekFinished: { + } + + onInternalAudioTracksChanged: { + } + + onExternalAudioTracksChanged: { + } + + onInternalSubtitleTracksChanged: { + } + + onStopped: { + console.log("stopped ") + dashPlayer.onStopped(player.duration); + } + + onPaused: { + console.log("else segment paused") + } + + onError: { + if (error !== MediaPlayer.NoError) { + msg.error(errorString) + } + } + + onVolumeChanged: { + } + + onStatusChanged: { + } + + onBufferProgressChanged: { + } + } + + Subtitle { + id: subtitle + player: player + enabled: PlayerConfig.subtitleEnabled + autoLoad: PlayerConfig.subtitleAutoLoad + engines: PlayerConfig.subtitleEngines + delay: PlayerConfig.subtitleDelay + fontFile: PlayerConfig.assFontFile + fontFileForced: PlayerConfig.assFontFileForced + fontsDir: PlayerConfig.assFontsDir + + onContentChanged: { //already enabled + } + + onLoaded: { + msg.info(qsTr("Subtitle") + ": " + path.substring(path.lastIndexOf("/") + 1)) + console.log(msg.text) + } + onSupportedSuffixesChanged: { + + } + + onEngineChanged: { // assume a engine canRender is only used as a renderer + subtitleItem.visible = canRender + subtitleLabel.visible = !canRender + } + onEnabledChanged: { + subtitleItem.visible = enabled + subtitleLabel.visible = enabled + } + } + + Text { + id: msg + objectName: "msg" + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Utils.scaled(20) + style: Text.Outline + styleColor: "green" + color: "white" + anchors.top: root.top + width: root.width + height: root.height / 4 + onTextChanged: { + msg_timer.stop() + visible = true + msg_timer.start() + } + Timer { + id: msg_timer + interval: 2000 + onTriggered: msg.visible = false + } + function error(txt) + { + styleColor = "red" + text = txt + } + function info(txt) + { + styleColor = "green" + text = txt + } + } + + Item { + id: myKeyboard + anchors.fill: parent + focus: true + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Escape: + break; + case Qt.Key_M: + break + case Qt.Key_Right: + break + case Qt.Key_Left: + break + case Qt.Key_Up: + break + case Qt.Key_Down: + break + case Qt.Key_Space: + break + case Qt.Key_Plus: + break; + case Qt.Key_Minus: + break; + case Qt.Key_F: + break + case Qt.Key_R: + break; + case Qt.Key_T: + break; + case Qt.Key_C: + break + case Qt.Key_A: + break + case Qt.Key_O: + break; + case Qt.Key_N: + break + case Qt.Key_B: + break; + case Qt.Key_G: + break; + case Qt.Key_Q: + if (event.modifiers & Qt.ControlModifier) + Qt.quit() + break; + } + } + } + DropArea { + anchors.fill: root + onEntered: { + if (!drag.hasUrls) + return; + console.log(drag.urls) + } + } + + Text { + + text: "Buffering..." + id: bufferingText + font.pointSize: 3*windowWidth*0.01; + color: "white" + font.family : "Helvetica" + opacity: canBuffer ? 1 : 0; + anchors.right: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + + GraphPanel { + + id: graphPanel + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.left: parent.right + opacity: { + if (graph) + opacity = 0.9 + else + opacity = 0 + } + + anchors.leftMargin: parent.width/2 + anchors.bottomMargin: control.height + + } + + ControlPanel { + id: control + objectName: "control" + + anchors { + left: parent.left + bottom: parent.bottom + right: parent.right + leftMargin: Utils.scaled(12) + rightMargin: Utils.scaled(12) + } + Behavior on opacity {NumberAnimation {duration: 300} } + + onStartGraph: graphPanel.startTimer() + onStopGraph:graphPanel.stopTimer() + onPauseGraph:graphPanel.pauseTimer() + + onDownloadMPD: mpdList.downloadMpd() + onOpenMpd: { + lastPlayed = dashPlayer.getLastPlayed() + icn = dashPlayer.getIcn() + adaptationLogic = dashPlayer.getAdaptationLogic() + openMpd.enabled = true; + openMpd.opacity = 0.9 + enabled = false + } + + onOpenOptions: { + lastPlayed = dashPlayer.getLastPlayed() + icn = dashPlayer.getIcn() + adaptationLogic = dashPlayer.getAdaptationLogic() + icnPrefix = dashPlayer.getIcnPrefix() + httpPrefix = dashPlayer.getHttpPrefix() + icnSuffix = dashPlayer.getIcnSuffix() + httpSuffix = dashPlayer.getHttpSuffix() + segmentBufferSize = dashPlayer.getSegmentBufferSize() + rateAlpha = dashPlayer.getRateAlpha() + bufferReservoirThreshold = dashPlayer.getBufferReservoirThreshold() + bufferMaxThreshold = dashPlayer.getBufferMaxThreshold() + adaptechFirstThreshold = dashPlayer.getAdaptechFirstThreshold() + adaptechSecondThreshold = dashPlayer.getAdaptechSecondThreshold() + adaptechSwitchUpMargin = dashPlayer.getAdaptechSwitchUpMargin() + adaptechSlackParameter = dashPlayer.getAdaptechSlackParameter() + adaptechAlpha = dashPlayer.getAdaptechAlpha() + bufferThreeThresholdFirst = dashPlayer.getBufferThreeThresholdFirst() + bufferThreeThresholdSecond = dashPlayer.getBufferThreeThresholdSecond() + bufferThreeThresholdThird = dashPlayer.getBufferThreeThresholdThird() + pandaParamAlpha = dashPlayer.getPandaParamAlpha() + pandaParamBeta = dashPlayer.getPandaParamBeta() + pandaParamBMin = dashPlayer.getPandaParamBMin() + pandaParamK = dashPlayer.getPandaParamK() + pandaParamW = dashPlayer.getPandaParamW() + pandaParamEpsilon = dashPlayer.getPandaParamEpsilon() + bolaBufferTarget = dashPlayer.getBolaBufferTarget() + bolaAlpha = dashPlayer.getBolaAlpha() + options.enabled = true + options.opacity = 0.9 + enabled = false + } + + onOpenOptionConnections: { + autotune = dashPlayer.getAutotune() + lifetime = dashPlayer.getLifetime() + retransmissions = dashPlayer.getRetransmissions() + alpha = dashPlayer.getAlpha() + console.log("alpha " + alpha) + beta = dashPlayer.getBeta() + drop = dashPlayer.getDrop() + betaWifi = dashPlayer.getBetaWifi() + dropWifi = dashPlayer.getDropWifi() + delayWifi = dashPlayer.getDelayWifi() + betaLte = dashPlayer.getBetaLte() + dropLte = dashPlayer.getDropLte() + delayLte = dashPlayer.getDelayLte() + batchingParameter = dashPlayer.getBatchingParameter() + rateEstimator = dashPlayer.getRateEstimator() + optionConnections.enabled = true + optionConnections.opacity = 0.9 + enabled = false + } + + onOpenGraph: { + graph = true + dashPlayer.setGraph(true) + graphPanel.opacity = 0.9 + } + + onHideGraph: { + graph = false + dashPlayer.setGraph(false) + graphPanel.opacity = 0 + } + + onRepeatVideo: { + repeat = true + dashPlayer.setRepeat(true) + } + + onDonotRepeatVideo: { + repeat = false + dashPlayer.setRepeat(false) + } + + onResizeWindowFullScreen: { + var xxx = Utils.scaled(Screen.width) + var yyy = Utils.scaled(Screen.height) + root.width = Utils.scaled(Window.width) + root.height = Utils.scaled(Window.height) + } + + onResizeWindow: { + root.width = Utils.scaled(1024) + root.height = Utils.scaled(768) + } + + onSaveFullScreen: { + enabledFullScreen = true + dashPlayer.setFullScreen(true) + } + + onSaveExitFullScreen: { + enabledFullScreen = false + dashPlayer.setFullScreen(false) + } + + onTogglePause: { + + } + onOpenFile: {} + //IF_QT53 + onOpenUrl: {} + MouseArea { + id: ma2 + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + + onEntered: { + if(player.playbackState === MediaPlayer.PlayingState) + { + control.opacity = 1.0; + } + else + { + control.opacity = 1.0; + } + } + + onExited: { + if(player.playbackState === MediaPlayer.PlayingState) + { + control.opacity = 0.0; + } + } + } + } + + OpenMpd { + id: openMpd + enabled: false + objectName: "openMpd" + + + anchors.centerIn: root + + onCloseOpenMpd: { + control.uncheckOpenBtn(); + control.enabled = true; + enabled = false; + opacity = 0 + } + + onSaveAndPlayMpd: { + dashPlayer.setLastPlayed(newOpenMpd) + lastPlayed = newOpenMpd + console.log("SAVE AND PLAY THE MPD QUEEN\n") + console.log(adaptationLogic) + dashPlayer.downloadMPD(newOpenMpd, adaptationLogic, icn) + } + } + + Options { + id: options + enabled: false + objectName: "openOption" + + anchors { + left: parent.left + bottom: parent.bottom + right: parent.right + top: parent.top + topMargin: Utils.scaled(12) + bottomMargin: control.height + Utils.scaled(12) + leftMargin: Utils.scaled(12) + rightMargin: Utils.scaled(12) + + } + + onCloseOptions: { + control.uncheckOptionsBtn() + options.enabled = false + control.enabled = true + enabled = false; + opacity = 0 + } + + onSaveAdaptationLogic: { + dashPlayer.setAdaptationLogic(selectedAdaptationLogic) + adaptationLogic = selectedAdaptationLogic + } + + onSaveIcn: { + dashPlayer.setIcn(selectedIcn) + icn = selectedIcn + } + + onSaveIcnPrefix: { + dashPlayer.setIcnPrefix(selectedIcnPrefix) + icnPrefix = selectedIcnPrefix + } + + onSaveHttpPrefix: { + dashPlayer.setHttpPrefix(selectedHttpPrefix) + httpPrefix = selectedHttpPrefix + } + onSaveIcnSuffix: { + dashPlayer.setIcnSuffix(selectedIcnSuffix) + icnSuffix = selectedIcnSuffix + } + + onSaveHttpSuffix: { + dashPlayer.setHttpSuffix(selectedHttpSuffix) + httpSuffix = selectedHttpSuffix + } + + onSaveSegmentBufferSize: { + dashPlayer.setSegmentBufferSize(selectedSegmentBufferSize) + segmentBufferSize = selectedSegmentBufferSize + } + + onSaveRateAlpha: { + dashPlayer.setRateAlpha(selectedRateAlpha) + rateAlpha = selectedRateAlpha + } + + onSaveBufferReservoirThreshold: { + dashPlayer.setBufferReservoirThreshold(selectedBufferReservoirThreshold) + bufferReservoirThreshold = selectedBufferReservoirThreshold + } + + onSaveBufferMaxThreshold: { + dashPlayer.setBufferMaxThreshold(selectedBufferMaxThreshold) + bufferMaxThreshold = selectedBufferMaxThreshold + } + + onSaveAdaptechFirstThreshold: { + dashPlayer.setAdaptechFirstThreshold(selectedAdaptechFirstThreshold) + adaptechFirstThreshold = selectedAdaptechFirstThreshold + } + + onSaveAdaptechSecondThreshold: { + dashPlayer.setAdaptechSecondThreshold(selectedAdaptechSecondThreshold) + adaptechSecondThreshold = selectedAdaptechSecondThreshold + } + + onSaveAdaptechSwitchUpMargin: { + dashPlayer.setAdaptechSwitchUpMargin(selectedAdaptechSwitchUpMargin) + adaptechSwitchUpMargin = selectedAdaptechSwitchUpMargin + } + + onSaveAdaptechSlackParameter: { + dashPlayer.setAdaptechSlackParameter(selectedAdaptechSlackParameter) + adaptechSlackParameter = selectedAdaptechSlackParameter + } + + onSaveAdaptechAlpha: { + dashPlayer.setAdaptechAlpha(selectedAdaptechAlpha) + adaptechAlpha = selectedAdaptechAlpha + } + + onSaveBufferThreeThresholdFirst: { + dashPlayer.setBufferThreeThresholdFirst(selectedBufferThreeThresholdFirst) + bufferThreeThresholdFirst = selectedBufferThreeThresholdFirst + } + + onSaveBufferThreeThresholdSecond: { + dashPlayer.setBufferThreeThresholdSecond(selectedBufferThreeThresholdSecond) + bufferThreeThresholdSecond = selectedBufferThreeThresholdSecond + } + + onSaveBufferThreeThresholdThird: { + dashPlayer.setBufferThreeThresholdThird(selectedBufferThreeThresholdThird) + bufferThreeThresholdThird = selectedBufferThreeThresholdThird + } + + onSavePandaParamAlpha: { + dashPlayer.setPandaParamAlpha(selectedPandaParamAlpha) + pandaParamAlpha = selectedPandaParamAlpha + } + + onSavePandaParamBeta: { + dashPlayer.setPandaParamBeta(selectedPandaParamBeta) + pandaParamBeta = selectedPandaParamBeta + } + + onSavePandaParamBMin: { + dashPlayer.setPandaParamBMin(selectedPandaParamBMin) + pandaParamBMin = selectedPandaParamBMin + } + + onSavePandaParamK: { + dashPlayer.setPandaParamK(selectedPandaParamK) + pandaParamK = selectedPandaParamK + } + + onSavePandaParamW: { + dashPlayer.setPandaParamW(selectedPandaParamW) + pandaParamW = selectedPandaParamW + } + + onSavePandaParamEpsilon: { + dashPlayer.setPandaParamEpsilon(selectedPandaParamEpsilon) + pandaParamEpsilon = selectedPandaParamEpsilon + } + + onSaveBolaBufferTarget: { + dashPlayer.setBolaBufferTarget(selectedBolaBufferTarget) + bolaBufferTarget = selectedBolaBufferTarget + } + + onSaveBolaAlpha: { + dashPlayer.setBolaAlpha(selectedBolaAlpha) + bolaAlpha = selectedBolaAlpha + } + + onReloadBufferBasedConf: { + bufferReservoirThreshold = dashPlayer.getBufferReservoirThreshold() + bufferMaxThreshold = dashPlayer.getBufferMaxThreshold() + } + + onReloadBufferRateBasedConf:{ + adaptechFirstThreshold = dashPlayer.getAdaptechFirstThreshold() + adaptechSecondThreshold = dashPlayer.getAdaptechSecondThreshold() + adaptechSwitchUpMargin = dashPlayer.getAdaptechSwitchUpMargin() + adaptechSlackParameter = dashPlayer.getAdaptechSlackParameter() + adaptechAlpha = dashPlayer.getAdaptechAlpha() + } + + onReloadBufferThreeThresholdConf: { + bufferThreeThresholdFirst = dashPlayer.getBufferThreeThresholdFirst() + bufferThreeThresholdSecond = dashPlayer.getBufferThreeThresholdSecond() + bufferThreeThresholdThird = dashPlayer.getBufferThreeThresholdThird() + } + + onReloadPandaConf: { + pandaParamAlpha = dashPlayer.getPandaParamAlpha() + pandaParamBeta = dashPlayer.getPandaParamBeta() + pandaParamBMin = dashPlayer.getPandaParamBMin() + pandaParamK = dashPlayer.getPandaParamK() + pandaParamW = dashPlayer.getPandaParamW() + pandaParamEpsilon = dashPlayer.getPandaParamEpsilon() + + } + + onReloadBolaConf: { + bolaBufferTarget = dashPlayer.getBolaBufferTarget() + + } + } + + OptionConnections { + id: optionConnections + enabled: false + objectName: "openOption" + + anchors { + left: parent.left + bottom: parent.bottom + right: parent.right + top: parent.top + topMargin: Utils.scaled(12) + bottomMargin: control.height + Utils.scaled(12) + leftMargin: Utils.scaled(12) + rightMargin: Utils.scaled(12) + } + + onCloseOptionConnections: { + control.uncheckOptionConnectionsBtn() + optionConnections.enabled = false + control.enabled = true + enabled = false; + opacity = 0 + } + + onSaveAutotune: { + dashPlayer.setAutotune(selectedAutotune) + autotune = selectedAutotune + } + + onSaveLifetime: { + dashPlayer.setLifetime(selectedLifetime) + lifetime = selectedLifetime + } + + onSaveRetransmissions: { + dashPlayer.setRetransmissions(selectedRetransmissions) + retransmissions = selectedRetransmissions + } + + onSaveAlpha: { + dashPlayer.setAlpha(selectedAlpha) + alpha = selectedAlpha + } + + onSaveBeta: { + dashPlayer.setBeta(selectedBeta) + beta = selectedBeta + } + + onSaveDrop: { + dashPlayer.setDrop(selectedDrop) + drop = selectedDrop + } + + onSaveBetaWifi: { + dashPlayer.setBetaWifi(selectedBetaWifi) + betaWifi = selectedBetaWifi + } + + onSaveDropWifi: { + dashPlayer.setDropWifi(selectedDropWifi) + dropWifi = selectedDropWifi + } + + onSaveDelayWifi: { + dashPlayer.setDelayWifi(selectedDelayWifi) + delayWifi = selectedDelayWifi + } + + onSaveBetaLte: { + dashPlayer.setBetaLte(selectedBetaLte) + betaLte = selectedBetaLte + } + + onSaveDropLte: { + dashPlayer.setDropLte(selectedDropLte) + dropLte = selectedDropLte + } + + onSaveDelayLte: { + dashPlayer.setDelayLte(selectedDelayLte) + delayLte = selectedDelayLte + } + + onSaveBatchingParameter: { + dashPlayer.setBatchingParameter(selectedBatchingParameter) + batchingParameter = selectedBatchingParameter + } + + onSaveRateEstimator: { + dashPlayer.setRateEstimator(selectedRateEstimator) + rateAlpha = selectedRateEstimator + } + } + + //IF_QT53 + Dialog { + id: urlDialog + standardButtons: StandardButton.Open | StandardButton.Cancel + title: qsTr("Open a URL") + Rectangle { + color: "black" + anchors.top: parent.top + height: Utils.kItemHeight + width: parent.width + TextInput { + id: urlEdit + color: "orange" + font.pixelSize: Utils.kFontSize + anchors.fill: parent + } + } + onAccepted: player.source = urlEdit.displayText + } + + //ENDIF_QT53 + FileDialog { + id: fileDialog + title: "Please choose a media file" + selectMultiple: true + folder: PlayerConfig.lastFile + onAccepted: { + var sub, av + for (var i = 0; i < fileUrls.length; ++i) + { + var s = fileUrls[i].toString() + if (s.endsWith(".srt") + || s.endsWith(".ass") + || s.endsWith(".ssa") + || s.endsWith(".sub") + || s.endsWith(".idx") //vob + || s.endsWith(".mpl2") + || s.endsWith(".smi") + || s.endsWith(".sami") + || s.endsWith(".sup") + || s.endsWith(".txt")) + sub = fileUrls[i] + else + av = fileUrls[i] + } + if (sub) + { + subtitle.autoLoad = false + subtitle.file = sub + } else + { + subtitle.autoLoad = PlayerConfig.subtitleAutoLoad + subtitle.file = "" + } + if (av) + { + player.source = av + PlayerConfig.lastFile = av + } + } + } + Connections { + target: Qt.application + onStateChanged: { //since 5.1 + if (Qt.platform.os === "winrt" || Qt.platform.os === "winphone") //paused by system + return + // winrt is handled by system + switch (Qt.application.state) { + case Qt.ApplicationSuspended: + case Qt.ApplicationHidden: + player.pause() + break + default: + break + } + } + } + + +} diff --git a/qml/Viper/utils.js b/qml/Viper/utils.js new file mode 100755 index 00000000..ad00957c --- /dev/null +++ b/qml/Viper/utils.js @@ -0,0 +1,100 @@ +/****************************************************************************** + QtAV: Multimedia framework based on Qt and FFmpeg + Copyright (C) 2012-2016 Wang Bin +* This file is part of QtAV (from 2013) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +******************************************************************************/ + +var kItemWidth = scaled(60) +var kItemHeight = scaled(30) +var kMargin = scaled(8) +var kFontSize = scaled(16) +var kSpacing = scaled(4) + +// "/xxx" will be resolved as qrc:///xxx. while "xxx" is "qrc:///QMLDIR/xxx +var resprefix = Qt.resolvedUrl(" ").substring(0, 4) == "qrc:" ? "/" : "" + +function resurl(s) { //why called twice if in qrc? + return resprefix + s +} + +String.prototype.startsWith = function(s) { + return this.indexOf(s) === 0; +}; + +String.prototype.endsWith = function(suffix) { + return this.indexOf(suffix, this.length - suffix.length) !== -1; +}; + +function fileName(path) { + return path.substring(path.lastIndexOf("/") + 1) +} + +function msec2string(t) { + t = Math.floor(t/1000) + var ss = t%60 + t = (t-ss)/60 + var mm = t%60 + var hh = (t-mm)/60 + if (ss < 10) + ss = "0" + ss + if (mm < 10) + mm = "0" + mm + if (hh < 10) + hh = "0" + hh + return hh + ":" + mm +":" + ss +} + +function scaled(x) { + //console.log("scaleRatio: " + scaleRatio + "; " + x + ">>>" + x*scaleRatio); + return x * scaleRatio; +} + + +function htmlEscaped(s) { + if (!s) { + return ''; + } + var escaped = ''; + var namedHtml = { + '38': '&', + '60': '<', + '62': '>', + '34': '"', + '160': ' ', + '162': '¢', + '163': '£', + '164': '¤', + '169': '©', + '174': '®', + }; + var wasNewLine = 0; + for (var i = 0, il = s.length; i < il; ++i) { + var c = s.charCodeAt(i); + var es = namedHtml[c]; + if (typeof es !== 'undefined') { + wasNewLine = 0; + escaped += es; + } else { + if (c === 13 || c === 10) { + if (wasNewLine == 0) + escaped += '
'; + wasNewLine++; + } else { + wasNewLine = 0; + escaped += String.fromCharCode(c); + } + } + } + return escaped; +} diff --git a/qml/images/Slider_bar.png b/qml/images/Slider_bar.png new file mode 100755 index 00000000..84be50fa Binary files /dev/null and b/qml/images/Slider_bar.png differ diff --git a/qml/images/Slider_handle.png b/qml/images/Slider_handle.png new file mode 100755 index 00000000..aebecdf1 Binary files /dev/null and b/qml/images/Slider_handle.png differ diff --git a/qml/images/Triangle-.png b/qml/images/Triangle-.png new file mode 100755 index 00000000..c09b2ce5 Binary files /dev/null and b/qml/images/Triangle-.png differ diff --git a/qml/images/b.jpeg b/qml/images/b.jpeg new file mode 100755 index 00000000..858adf3c Binary files /dev/null and b/qml/images/b.jpeg differ diff --git a/qml/images/b.png b/qml/images/b.png new file mode 100755 index 00000000..8e8dd2a0 Binary files /dev/null and b/qml/images/b.png differ diff --git a/qml/images/buffering.png b/qml/images/buffering.png new file mode 100755 index 00000000..d7763cec Binary files /dev/null and b/qml/images/buffering.png differ diff --git a/qml/images/fullscreen-selected.png b/qml/images/fullscreen-selected.png new file mode 100755 index 00000000..566aeb29 Binary files /dev/null and b/qml/images/fullscreen-selected.png differ diff --git a/qml/images/fullscreen.png b/qml/images/fullscreen.png new file mode 100755 index 00000000..adea2e81 Binary files /dev/null and b/qml/images/fullscreen.png differ diff --git a/qml/images/fullscreen.svg b/qml/images/fullscreen.svg new file mode 100755 index 00000000..83b020eb --- /dev/null +++ b/qml/images/fullscreen.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/qml/images/graph-selected.png b/qml/images/graph-selected.png new file mode 100755 index 00000000..a493540c Binary files /dev/null and b/qml/images/graph-selected.png differ diff --git a/qml/images/graph.png b/qml/images/graph.png new file mode 100755 index 00000000..accfb26f Binary files /dev/null and b/qml/images/graph.png differ diff --git a/qml/images/open-selected.png b/qml/images/open-selected.png new file mode 100755 index 00000000..b9a0e96c Binary files /dev/null and b/qml/images/open-selected.png differ diff --git a/qml/images/open.png b/qml/images/open.png new file mode 100755 index 00000000..11fa91b8 Binary files /dev/null and b/qml/images/open.png differ diff --git a/qml/images/option-connections-selected.png b/qml/images/option-connections-selected.png new file mode 100644 index 00000000..3bb4fac8 Binary files /dev/null and b/qml/images/option-connections-selected.png differ diff --git a/qml/images/option-connections.png b/qml/images/option-connections.png new file mode 100644 index 00000000..c6edcd1a Binary files /dev/null and b/qml/images/option-connections.png differ diff --git a/qml/images/options-selected.png b/qml/images/options-selected.png new file mode 100755 index 00000000..b11785de Binary files /dev/null and b/qml/images/options-selected.png differ diff --git a/qml/images/options.png b/qml/images/options.png new file mode 100755 index 00000000..fe0d1c79 Binary files /dev/null and b/qml/images/options.png differ diff --git a/qml/images/pause.svg b/qml/images/pause.svg new file mode 100755 index 00000000..bfd98887 --- /dev/null +++ b/qml/images/pause.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/qml/images/play.svg b/qml/images/play.svg new file mode 100755 index 00000000..dc66841c --- /dev/null +++ b/qml/images/play.svg @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/qml/images/qt-logo.png b/qml/images/qt-logo.png new file mode 100755 index 00000000..ecbff0ca Binary files /dev/null and b/qml/images/qt-logo.png differ diff --git a/qml/images/repeat-selected.png b/qml/images/repeat-selected.png new file mode 100755 index 00000000..752e78ac Binary files /dev/null and b/qml/images/repeat-selected.png differ diff --git a/qml/images/repeat.png b/qml/images/repeat.png new file mode 100755 index 00000000..02fe6404 Binary files /dev/null and b/qml/images/repeat.png differ diff --git a/qml/images/stop.svg b/qml/images/stop.svg new file mode 100755 index 00000000..b6139441 --- /dev/null +++ b/qml/images/stop.svg @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/viper.pro b/viper.pro new file mode 100644 index 00000000..217e5586 --- /dev/null +++ b/viper.pro @@ -0,0 +1,316 @@ +# Copyright (c) 2017 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +TARGET = viper +VERSION = $$QTAV_VERSION +QT += av svg qml quick sql core gui opengl multimedia charts +android { + QT += androidextras +} + +CONFIG -= release +CONFIG += debug +CONFIG += c++11 + + +INCLUDEPATH += /usr/include/libdash +INCLUDEPATH += /usr/include/libxml2 +QMAKE_CXXFLAGS += -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DANDROID_STL=c++_static #-DICNICPDOWNLOAD +QMAKE_CXXFLAGS += -std=c++11 -g -fpermissive +# Add more folders to ship with the application, here +folder_01.source = qml/Viper +folder_01.target = qml +LIBS += -licnet + + +RESOURCES += \ + viper.qrc + +# The .cpp file which was generated for your project. Feel free to hack it. +SOURCES += \ + Adaptation/AbstractAdaptationLogic.cpp \ + Adaptation/AdaptationLogicFactory.cpp \ + Adaptation/AlwaysLowestLogic.cpp \ + Adaptation/BufferBasedAdaptation.cpp \ + Adaptation/BufferBasedAdaptationWithRateBased.cpp \ + Adaptation/BufferBasedThreeThresholdAdaptation.cpp \ + Adaptation/Panda.cpp \ + Adaptation/Bola.cpp \ + Adaptation/RateBasedAdaptation.cpp \ + Input/DASHManager.cpp \ + Input/DASHReceiver.cpp \ + Input/MediaObject.cpp \ + MPD/AbstractRepresentationStream.cpp \ + MPD/AdaptationSetHelper.cpp \ + MPD/AdaptationSetStream.cpp \ + MPD/BaseUrlResolver.cpp \ + MPD/RepresentationStreamFactory.cpp \ + MPD/SegmentListStream.cpp \ + MPD/SegmentTemplateStream.cpp \ + MPD/SingleMediaSegmentStream.cpp \ + MPD/TimeResolver.cpp \ + Portable/MultiThreading.cpp \ + Managers/MultimediaManager.cpp \ + Managers/MultimediaStream.cpp \ + UI/DASHPlayer.cpp \ + UI/DASHPlayerNoGUI.cpp \ + UI/GraphDataSource.cpp \ + Input/ICNConnectionConsumerApi.cpp \ + Websocket/communication-protocol.cpp \ + Websocket/connection-pool.cpp \ + Websocket/query.cpp \ + Websocket/tcp-server.cpp \ + Websocket/websocket-server.cpp \ + Websocket/WebSocketService.cpp \ + UI/ViperGui.cpp \ + Common/ViperBuffer.cpp \ + Common/QtQuick2ApplicationViewer.cpp + + +lupdate_only{ +SOURCES = qml/Viper/*.qml qml/Viper/*.js +} +# Installation path +target.path = $$[QT_INSTALL_BINS] + + +desktopfile.files = $$PWD/../../qtc_packaging/debian_generic/Viper.desktop +desktopfile.path = /usr/share/applications + +COMMON = $$PWD/Common +INCLUDEPATH *= $$COMMON $$COMMON/.. +isEmpty(PROJECTROOT): PROJECTROOT = $$PWD + +mac: RC_FILE = $$COMMON/Viper.icns +QMAKE_INFO_PLIST = $$COMMON/Info.plist +videos.files = videos +videos.path = / +#QMAKE_BUNDLE_DATA += videos +defineTest(genRC) { + RC_ICONS = $$COMMON/Cisco.icns + QMAKE_TARGET_COMPANY = "Cisco Systems" + QMAKE_TARGET_DESCRIPTION = "ICN Dash Player" + QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2016-2017 Cisco Systems" + QMAKE_TARGET_PRODUCT = "Viper $$1" + export(RC_ICONS) + export(QMAKE_TARGET_COMPANY) + export(QMAKE_TARGET_DESCRIPTION) + export(QMAKE_TARGET_COPYRIGHT) + export(QMAKE_TARGET_PRODUCT) + return(true) +} +genRC($$TARGET) +#SystemParametersInfo +!winrt:*msvc*: LIBS += -lUser32 +DEFINES += BUILD_QOPT_LIB +HEADERS *= \ + $$COMMON/Common.h \ + $$COMMON/Config.h \ + $$COMMON/QOptions.h \ + $$COMMON/CommonExport.h \ + Adaptation/AbstractAdaptationLogic.h \ + Adaptation/AdaptationLogicFactory.h \ + Adaptation/AlwaysLowestLogic.h \ + Adaptation/BufferBasedAdaptation.h \ + Adaptation/BufferBasedAdaptationWithRateBased.h \ + Adaptation/BufferBasedThreeThresholdAdaptation.h \ + Adaptation/IAdaptationLogic.h \ + Adaptation/Panda.h \ + Adaptation/Bola.h \ + Adaptation/RateBasedAdaptation.h \ + Buffer/Buffer.h \ + Buffer/IBufferObserver.h \ + Input/DASHManager.h \ + Input/DASHReceiver.h \ + Input/IDASHManagerObserver.h \ + Input/IDASHReceiverObserver.h \ + Input/IDataReceiver.h \ + Input/MediaObject.h \ + MPD/AbstractRepresentationStream.h \ + MPD/AdaptationSetHelper.h \ + MPD/AdaptationSetStream.h \ + MPD/BaseUrlResolver.h \ + MPD/IRepresentationStream.h \ + MPD/RepresentationStreamFactory.h \ + MPD/SegmentListStream.h \ + MPD/SegmentTemplateStream.h \ + MPD/SingleMediaSegmentStream.h \ + MPD/TimeResolver.h \ + Portable/MultiThreading.h \ + Portable/Networking.h \ + Managers/IMultimediaManagerBase.h \ + Managers/IMultimediaManagerObserver.h \ + Managers/IStreamObserver.h \ + Managers/MultimediaManager.h \ + Managers/MultimediaStream.h \ + UI/DASHPlayer.h \ + UI/DASHPlayerNoGUI.h \ + UI/IDASHPlayerGuiObserver.h \ + UI/IDASHPlayerNoGuiObserver.h \ + debug.h \ + UI/GraphDataSource.h \ + Input/ICNConnectionConsumerApi.h \ + Input/IICNConnection.h \ + Websocket/communication-protocol.h \ + Websocket/connection-pool.h \ + Websocket/json.h \ + Websocket/query.h \ + Websocket/tcp-server.h \ + Websocket/websocket-server.h \ + Websocket/WebSocketService.h \ + websocketpp/base64/base64.hpp \ + websocketpp/common/asio.hpp \ + websocketpp/common/asio_ssl.hpp \ + websocketpp/common/chrono.hpp \ + websocketpp/common/connection_hdl.hpp \ + websocketpp/common/cpp11.hpp \ + websocketpp/common/functional.hpp \ + websocketpp/common/md5.hpp \ + websocketpp/common/memory.hpp \ + websocketpp/common/network.hpp \ + websocketpp/common/platforms.hpp \ + websocketpp/common/random.hpp \ + websocketpp/common/regex.hpp \ + websocketpp/common/stdint.hpp \ + websocketpp/common/system_error.hpp \ + websocketpp/common/thread.hpp \ + websocketpp/common/time.hpp \ + websocketpp/common/type_traits.hpp \ + websocketpp/concurrency/basic.hpp \ + websocketpp/concurrency/none.hpp \ + websocketpp/config/asio.hpp \ + websocketpp/config/asio_client.hpp \ + websocketpp/config/asio_no_tls.hpp \ + websocketpp/config/asio_no_tls_client.hpp \ + websocketpp/config/boost_config.hpp \ + websocketpp/config/core.hpp \ + websocketpp/config/core_client.hpp \ + websocketpp/config/debug.hpp \ + websocketpp/config/debug_asio.hpp \ + websocketpp/config/debug_asio_no_tls.hpp \ + websocketpp/config/minimal_client.hpp \ + websocketpp/config/minimal_server.hpp \ + websocketpp/extensions/permessage_deflate/disabled.hpp \ + websocketpp/extensions/permessage_deflate/enabled.hpp \ + websocketpp/extensions/extension.hpp \ + websocketpp/http/impl/parser.hpp \ + websocketpp/http/impl/request.hpp \ + websocketpp/http/impl/response.hpp \ + websocketpp/http/constants.hpp \ + websocketpp/http/parser.hpp \ + websocketpp/http/request.hpp \ + websocketpp/http/response.hpp \ + websocketpp/impl/connection_impl.hpp \ + websocketpp/impl/endpoint_impl.hpp \ + websocketpp/impl/utilities_impl.hpp \ + websocketpp/logger/basic.hpp \ + websocketpp/logger/levels.hpp \ + websocketpp/logger/stub.hpp \ + websocketpp/logger/syslog.hpp \ + websocketpp/message_buffer/alloc.hpp \ + websocketpp/message_buffer/message.hpp \ + websocketpp/message_buffer/pool.hpp \ + websocketpp/processors/base.hpp \ + websocketpp/processors/hybi00.hpp \ + websocketpp/processors/hybi07.hpp \ + websocketpp/processors/hybi08.hpp \ + websocketpp/processors/hybi13.hpp \ + websocketpp/processors/processor.hpp \ + websocketpp/random/none.hpp \ + websocketpp/random/random_device.hpp \ + websocketpp/roles/client_endpoint.hpp \ + websocketpp/roles/server_endpoint.hpp \ + websocketpp/sha1/sha1.hpp \ + websocketpp/transport/asio/security/base.hpp \ + websocketpp/transport/asio/security/none.hpp \ + websocketpp/transport/asio/security/tls.hpp \ + websocketpp/transport/asio/base.hpp \ + websocketpp/transport/asio/connection.hpp \ + websocketpp/transport/asio/endpoint.hpp \ + websocketpp/transport/base/connection.hpp \ + websocketpp/transport/base/endpoint.hpp \ + websocketpp/transport/debug/base.hpp \ + websocketpp/transport/debug/connection.hpp \ + websocketpp/transport/debug/endpoint.hpp \ + websocketpp/transport/iostream/base.hpp \ + websocketpp/transport/iostream/connection.hpp \ + websocketpp/transport/iostream/endpoint.hpp \ + websocketpp/transport/stub/base.hpp \ + websocketpp/transport/stub/connection.hpp \ + websocketpp/transport/stub/endpoint.hpp \ + websocketpp/client.hpp \ + websocketpp/close.hpp \ + websocketpp/connection.hpp \ + websocketpp/connection_base.hpp \ + websocketpp/endpoint.hpp \ + websocketpp/endpoint_base.hpp \ + websocketpp/error.hpp \ + websocketpp/frame.hpp \ + websocketpp/server.hpp \ + websocketpp/uri.hpp \ + websocketpp/utf8_validator.hpp \ + websocketpp/utilities.hpp \ + websocketpp/version.hpp \ + UI/ViperGui.h \ + Common/ViperBuffer.h \ + Common/QtQuick2ApplicationViewer.h + + + +SOURCES *= \ + $$COMMON/Common.cpp \ + $$COMMON/Config.cpp \ + $$COMMON/QOptions.cpp + +unix:!macx:!android { + INCLUDEPATH += /usr/local/include + INCLUDEPATH += /usr/local/include/libdash + + LIBS += -L/usr/local/lib -ldash -lboost_system -licnet -lavcodec -lavutil -lavformat +} + +macx:!ios { +#SOURCE is ok + INCLUDEPATH += /usr/local/include + INCLUDEPATH += /usr/local/include/libdash + LIBS += -L"/usr/local/lib" -framework CoreServices -ldash -lavformat -lavutil -lavcodec -lboost_system -lboost_regex -lswscale -licnet -lssl -lcrypto +} +SOURCES *= main.cpp +android { + DISTFILES += \ + android/src/org/player/viper/ViperActivity.java \ + android/gradle/wrapper/gradle-wrapper.jar \ + android/AndroidManifest.xml \ + android/res/values/libs.xml \ + android/build.gradle \ + android/gradle/wrapper/gradle-wrapper.properties \ + android/gradlew \ + android/gradlew.bat + + ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + + LIBS += -lconsumer-producer -ldash -lavcodec -lavutil -lavformat -lboost_system # -lccnx_api_control -lccnx_api_notify -lccnx_api_portal -lccnx_common -lccnx_transport_rta #-lparc -lcrypto #-llongbow-textplain -llongbow-ansiterm -llongbow +#user can put fonts in android/assets/fonts +} + + +DISTFILES += \ + qml/images/Triangle-.png \ + Common/Viper.icns \ + android/res/drawable-hdpi/icon.png \ + android/res/drawable-ldpi/icon.png \ + android/res/drawable-mdpi/icon.png \ + android/gradle.properties \ + android/src/org/qtav/qmlplayer/ViperActivity.java \ + android/src/org/player/viper/ViperActivity.java + diff --git a/viper.qrc b/viper.qrc new file mode 100644 index 00000000..31b73a2d --- /dev/null +++ b/viper.qrc @@ -0,0 +1,35 @@ + + + qml/Viper/main.qml + qml/Viper/Button.qml + qml/Viper/ProgressBar.qml + qml/Viper/Slider.qml + qml/Viper/utils.js + qml/Viper/ControlPanel.qml + qml/Viper/VideoCodec.qml + qml/Viper/DelegateItem.qml + qml/images/qt-logo.png + qml/images/Slider_bar.png + qml/images/Slider_handle.png + qml/Viper/GraphPanel.qml + qml/images/fullscreen.svg + qml/images/pause.svg + qml/images/play.svg + qml/images/stop.svg + qml/images/repeat.png + qml/images/repeat-selected.png + qml/images/fullscreen.png + qml/images/fullscreen-selected.png + qml/images/open-selected.png + qml/images/open.png + qml/Viper/OpenMpd.qml + qml/Viper/Options.qml + qml/images/options-selected.png + qml/images/options.png + qml/images/graph-selected.png + qml/images/graph.png + qml/Viper/OptionConnections.qml + qml/images/option-connections-selected.png + qml/images/option-connections.png + + diff --git a/websocketpp/base64/base64.hpp b/websocketpp/base64/base64.hpp new file mode 100644 index 00000000..ff1561d1 --- /dev/null +++ b/websocketpp/base64/base64.hpp @@ -0,0 +1,178 @@ +/* + ****** + base64.hpp is a repackaging of the base64.cpp and base64.h files into a + single header suitable for use as a header only library. This conversion was + done by Peter Thorson (webmaster@zaphoyd.com) in 2012. All modifications to + the code are redistributed under the same license as the original, which is + listed below. + ****** + + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#ifndef _BASE64_HPP_ +#define _BASE64_HPP_ + +#include + +namespace websocketpp { + +static std::string const base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +/// Test whether a character is a valid base64 character +/** + * @param c The character to test + * @return true if c is a valid base64 character + */ +static inline bool is_base64(unsigned char c) { + return (c == 43 || // + + (c >= 47 && c <= 57) || // /-9 + (c >= 65 && c <= 90) || // A-Z + (c >= 97 && c <= 122)); // a-z +} + +/// Encode a char buffer into a base64 string +/** + * @param input The input data + * @param len The length of input in bytes + * @return A base64 encoded string representing input + */ +inline std::string base64_encode(unsigned char const * input, size_t len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (len--) { + char_array_3[i++] = *(input++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) { + ret += base64_chars[char_array_4[i]]; + } + i = 0; + } + } + + if (i) { + for(j = i; j < 3; j++) { + char_array_3[j] = '\0'; + } + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) { + ret += base64_chars[char_array_4[j]]; + } + + while((i++ < 3)) { + ret += '='; + } + } + + return ret; +} + +/// Encode a string into a base64 string +/** + * @param input The input data + * @return A base64 encoded string representing input + */ +inline std::string base64_encode(std::string const & input) { + return base64_encode( + reinterpret_cast(input.data()), + input.size() + ); +} + +/// Decode a base64 encoded string into a string of raw bytes +/** + * @param input The base64 encoded input data + * @return A string representing the decoded raw bytes + */ +inline std::string base64_decode(std::string const & input) { + size_t in_len = input.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( input[in_] != '=') && is_base64(input[in_])) { + char_array_4[i++] = input[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) { + char_array_4[i] = static_cast(base64_chars.find(char_array_4[i])); + } + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) { + ret += char_array_3[i]; + } + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = static_cast(base64_chars.find(char_array_4[j])); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) { + ret += static_cast(char_array_3[j]); + } + } + + return ret; +} + +} // namespace websocketpp + +#endif // _BASE64_HPP_ diff --git a/websocketpp/client.hpp b/websocketpp/client.hpp new file mode 100644 index 00000000..8782d7e4 --- /dev/null +++ b/websocketpp/client.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CLIENT_HPP +#define WEBSOCKETPP_CLIENT_HPP + +#include + +#endif //WEBSOCKETPP_CLIENT_HPP diff --git a/websocketpp/close.hpp b/websocketpp/close.hpp new file mode 100644 index 00000000..ded77657 --- /dev/null +++ b/websocketpp/close.hpp @@ -0,0 +1,342 @@ + +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CLOSE_HPP +#define WEBSOCKETPP_CLOSE_HPP + +/** \file + * A package of types and methods for manipulating WebSocket close codes. + */ + +#include +#include +#include +#include + +#include + +namespace websocketpp { +/// A package of types and methods for manipulating WebSocket close codes. +namespace close { +/// A package of types and methods for manipulating WebSocket close status' +namespace status { + /// The type of a close code value. + typedef uint16_t value; + + /// A blank value for internal use. + static value const blank = 0; + + /// Close the connection without a WebSocket close handshake. + /** + * This special value requests that the WebSocket connection be closed + * without performing the WebSocket closing handshake. This does not comply + * with RFC6455, but should be safe to do if necessary. This could be useful + * for clients that need to disconnect quickly and cannot afford the + * complete handshake. + */ + static value const omit_handshake = 1; + + /// Close the connection with a forced TCP drop. + /** + * This special value requests that the WebSocket connection be closed by + * forcibly dropping the TCP connection. This will leave the other side of + * the connection with a broken connection and some expensive timeouts. this + * should not be done except in extreme cases or in cases of malicious + * remote endpoints. + */ + static value const force_tcp_drop = 2; + + /// Normal closure, meaning that the purpose for which the connection was + /// established has been fulfilled. + static value const normal = 1000; + + /// The endpoint was "going away", such as a server going down or a browser + /// navigating away from a page. + static value const going_away = 1001; + + /// A protocol error occurred. + static value const protocol_error = 1002; + + /// The connection was terminated because an endpoint received a type of + /// data it cannot accept. + /** + * (e.g., an endpoint that understands only text data MAY send this if it + * receives a binary message). + */ + static value const unsupported_data = 1003; + + /// A dummy value to indicate that no status code was received. + /** + * This value is illegal on the wire. + */ + static value const no_status = 1005; + + /// A dummy value to indicate that the connection was closed abnormally. + /** + * In such a case there was no close frame to extract a value from. This + * value is illegal on the wire. + */ + static value const abnormal_close = 1006; + + /// An endpoint received message data inconsistent with its type. + /** + * For example: Invalid UTF8 bytes in a text message. + */ + static value const invalid_payload = 1007; + + /// An endpoint received a message that violated its policy. + /** + * This is a generic status code that can be returned when there is no other + * more suitable status code (e.g., 1003 or 1009) or if there is a need to + * hide specific details about the policy. + */ + static value const policy_violation = 1008; + + /// An endpoint received a message too large to process. + static value const message_too_big = 1009; + + /// A client expected the server to accept a required extension request + /** + * The list of extensions that are needed SHOULD appear in the /reason/ part + * of the Close frame. Note that this status code is not used by the server, + * because it can fail the WebSocket handshake instead. + */ + static value const extension_required = 1010; + + /// An endpoint encountered an unexpected condition that prevented it from + /// fulfilling the request. + static value const internal_endpoint_error = 1011; + + /// Indicates that the service is restarted. A client may reconnect and if + /// if it chooses to do so, should reconnect using a randomized delay of + /// 5-30s + static value const service_restart = 1012; + + /// Indicates that the service is experiencing overload. A client should + /// only connect to a different IP (when there are multiple for the target) + /// or reconnect to the same IP upon user action. + static value const try_again_later = 1013; + + /// An endpoint failed to perform a TLS handshake + /** + * Designated for use in applications expecting a status code to indicate + * that the connection was closed due to a failure to perform a TLS + * handshake (e.g., the server certificate can't be verified). This value is + * illegal on the wire. + */ + static value const tls_handshake = 1015; + + /// A generic subprotocol error + /** + * Indicates that a subprotocol error occurred. Typically this involves + * receiving a message that is not formatted as a valid message for the + * subprotocol in use. + */ + static value const subprotocol_error = 3000; + + /// A invalid subprotocol data + /** + * Indicates that data was received that violated the specification of the + * subprotocol in use. + */ + static value const invalid_subprotocol_data = 3001; + + /// First value in range reserved for future protocol use + static value const rsv_start = 1016; + /// Last value in range reserved for future protocol use + static value const rsv_end = 2999; + + /// Test whether a close code is in a reserved range + /** + * @param [in] code The code to test + * @return Whether or not code is reserved + */ + inline bool reserved(value code) { + return ((code >= rsv_start && code <= rsv_end) || + code == 1004 || code == 1014); + } + + /// First value in range that is always invalid on the wire + static value const invalid_low = 999; + /// Last value in range that is always invalid on the wire + static value const invalid_high = 5000; + + /// Test whether a close code is invalid on the wire + /** + * @param [in] code The code to test + * @return Whether or not code is invalid on the wire + */ + inline bool invalid(value code) { + return (code <= invalid_low || code >= invalid_high || + code == no_status || code == abnormal_close || + code == tls_handshake); + } + + /// Determine if the code represents an unrecoverable error + /** + * There is a class of errors for which once they are discovered normal + * WebSocket functionality can no longer occur. This function determines + * if a given code is one of these values. This information is used to + * determine if the system has the capability of waiting for a close + * acknowledgement or if it should drop the TCP connection immediately + * after sending its close frame. + * + * @param [in] code The value to test. + * @return True if the code represents an unrecoverable error + */ + inline bool terminal(value code) { + return (code == protocol_error || code == invalid_payload || + code == policy_violation || code == message_too_big || + code == internal_endpoint_error); + } + + /// Return a human readable interpretation of a WebSocket close code + /** + * See https://tools.ietf.org/html/rfc6455#section-7.4 for more details. + * + * @since 0.3.0 + * + * @param [in] code The code to look up. + * @return A human readable interpretation of the code. + */ + inline std::string get_string(value code) { + switch (code) { + case normal: + return "Normal close"; + case going_away: + return "Going away"; + case protocol_error: + return "Protocol error"; + case unsupported_data: + return "Unsupported data"; + case no_status: + return "No status set"; + case abnormal_close: + return "Abnormal close"; + case invalid_payload: + return "Invalid payload"; + case policy_violation: + return "Policy violoation"; + case message_too_big: + return "Message too big"; + case extension_required: + return "Extension required"; + case internal_endpoint_error: + return "Internal endpoint error"; + case tls_handshake: + return "TLS handshake failure"; + case subprotocol_error: + return "Generic subprotocol error"; + case invalid_subprotocol_data: + return "Invalid subprotocol data"; + default: + return "Unknown"; + } + } +} // namespace status + +/// Type used to convert close statuses between integer and wire representations +union code_converter { + uint16_t i; + char c[2]; +}; + +/// Extract a close code value from a close payload +/** + * If there is no close value (ie string is empty) status::no_status is + * returned. If a code couldn't be extracted (usually do to a short or + * otherwise mangled payload) status::protocol_error is returned and the ec + * value is flagged as an error. Note that this case is different than the case + * where protocol error is received over the wire. + * + * If the value is in an invalid or reserved range ec is set accordingly. + * + * @param [in] payload Close frame payload value received over the wire. + * @param [out] ec Set to indicate what error occurred, if any. + * @return The extracted value + */ +inline status::value extract_code(std::string const & payload, lib::error_code + & ec) +{ + ec = lib::error_code(); + + if (payload.size() == 0) { + return status::no_status; + } else if (payload.size() == 1) { + ec = make_error_code(error::bad_close_code); + return status::protocol_error; + } + + code_converter val; + + val.c[0] = payload[0]; + val.c[1] = payload[1]; + + status::value code(ntohs(val.i)); + + if (status::invalid(code)) { + ec = make_error_code(error::invalid_close_code); + } + + if (status::reserved(code)) { + ec = make_error_code(error::reserved_close_code); + } + + return code; +} + +/// Extract the reason string from a close payload +/** + * The string should be a valid UTF8 message. error::invalid_utf8 will be set if + * the function extracts a reason that is not valid UTF8. + * + * @param [in] payload The payload string to extract a reason from. + * @param [out] ec Set to indicate what error occurred, if any. + * @return The reason string. + */ +inline std::string extract_reason(std::string const & payload, lib::error_code + & ec) +{ + std::string reason; + ec = lib::error_code(); + + if (payload.size() > 2) { + reason.append(payload.begin()+2,payload.end()); + } + + if (!websocketpp::utf8_validator::validate(reason)) { + ec = make_error_code(error::invalid_utf8); + } + + return reason; +} + +} // namespace close +} // namespace websocketpp + +#endif // WEBSOCKETPP_CLOSE_HPP diff --git a/websocketpp/common/asio.hpp b/websocketpp/common/asio.hpp new file mode 100644 index 00000000..ca483593 --- /dev/null +++ b/websocketpp/common/asio.hpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_ASIO_HPP +#define WEBSOCKETPP_COMMON_ASIO_HPP + +// This file goes to some length to preserve compatibility with versions of +// boost older than 1.49 (where the first modern steady_timer timer based on +// boost/std chrono was introduced. +// +// For the versions older than 1.49, the deadline_timer is used instead. this +// brings in dependencies on boost date_time and it has a different interface +// that is normalized by the `lib::asio::is_neg` and `lib::asio::milliseconds` +// wrappers provided by this file. +// +// The primary reason for this continued support is that boost 1.48 is the +// default and not easily changeable version of boost supplied by the package +// manager of popular Linux distributions like Ubuntu 12.04 LTS. Once the need +// for this has passed this should be cleaned up and simplified. + +#ifdef ASIO_STANDALONE + #include + + #if (ASIO_VERSION/100000) == 1 && ((ASIO_VERSION/100)%1000) < 8 + static_assert(false, "The minimum version of standalone Asio is 1.8.0"); + #endif + + #include + #include + #include +#else + #include + + // See note above about boost <1.49 compatibility. If we are running on + // boost > 1.48 pull in the steady timer and chrono library + #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 48 + #include + #include + #endif + + #include + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef ASIO_STANDALONE + namespace asio { + using namespace ::asio; + // Here we assume that we will be using std::error_code with standalone + // Asio. This is probably a good assumption, but it is possible in rare + // cases that local Asio versions would be used. + using std::errc; + + // See note above about boost <1.49 compatibility. Because we require + // a standalone Asio version of 1.8+ we are guaranteed to have + // steady_timer available. By convention we require the chrono library + // (either boost or std) for use with standalone Asio. + template + bool is_neg(T duration) { + return duration.count() < 0; + } + inline lib::chrono::milliseconds milliseconds(long duration) { + return lib::chrono::milliseconds(duration); + } + } // namespace asio + +#else + namespace asio { + using namespace boost::asio; + + // See note above about boost <1.49 compatibility + #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 48 + // Using boost::asio >=1.49 so we use chrono and steady_timer + template + bool is_neg(T duration) { + return duration.count() < 0; + } + inline lib::chrono::milliseconds milliseconds(long duration) { + return lib::chrono::milliseconds(duration); + } + #else + // Using boost::asio <1.49 we pretend a deadline timer is a steady + // timer and wrap the negative detection and duration conversion + // appropriately. + typedef boost::asio::deadline_timer steady_timer; + + template + bool is_neg(T duration) { + return duration.is_negative(); + } + inline boost::posix_time::time_duration milliseconds(long duration) { + return boost::posix_time::milliseconds(duration); + } + #endif + + using boost::system::error_code; + namespace errc = boost::system::errc; + } // namespace asio +#endif + + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_ASIO_HPP diff --git a/websocketpp/common/asio_ssl.hpp b/websocketpp/common/asio_ssl.hpp new file mode 100644 index 00000000..69892832 --- /dev/null +++ b/websocketpp/common/asio_ssl.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_ASIO_SSL_HPP +#define WEBSOCKETPP_COMMON_ASIO_SSL_HPP + +// NOTE: This file must be included before common/asio.hpp + +#ifdef ASIO_STANDALONE + #include +#else + #include +#endif + +#endif // WEBSOCKETPP_COMMON_ASIO_SSL_HPP diff --git a/websocketpp/common/chrono.hpp b/websocketpp/common/chrono.hpp new file mode 100644 index 00000000..975ee043 --- /dev/null +++ b/websocketpp/common/chrono.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_CHRONO_HPP +#define WEBSOCKETPP_COMMON_CHRONO_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 functional header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_ + #ifndef _WEBSOCKETPP_CPP11_CHRONO_ + #define _WEBSOCKETPP_CPP11_CHRONO_ + #endif +#endif + +// If we're on Visual Studio 2012 or higher and haven't explicitly disabled +// the use of C++11 chrono header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1700 && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_ + #ifndef _WEBSOCKETPP_CPP11_CHRONO_ + #define _WEBSOCKETPP_CPP11_CHRONO_ + #endif +#endif + +#ifdef _WEBSOCKETPP_CPP11_CHRONO_ + #include +#else + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_CHRONO_ + namespace chrono = std::chrono; +#else + namespace chrono = boost::chrono; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_CHRONO_HPP diff --git a/websocketpp/common/connection_hdl.hpp b/websocketpp/common/connection_hdl.hpp new file mode 100644 index 00000000..1044c88e --- /dev/null +++ b/websocketpp/common/connection_hdl.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_CONNECTION_HDL_HPP +#define WEBSOCKETPP_COMMON_CONNECTION_HDL_HPP + +#include + +namespace websocketpp { + +/// A handle to uniquely identify a connection. +/** + * This type uniquely identifies a connection. It is implemented as a weak + * pointer to the connection in question. This provides uniqueness across + * multiple endpoints and ensures that IDs never conflict or run out. + * + * It is safe to make copies of this handle, store those copies in containers, + * and use them from other threads. + * + * This handle can be upgraded to a full shared_ptr using + * `endpoint::get_con_from_hdl()` from within a handler fired by the connection + * that owns the handler. + */ +typedef lib::weak_ptr connection_hdl; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_CONNECTION_HDL_HPP diff --git a/websocketpp/common/cpp11.hpp b/websocketpp/common/cpp11.hpp new file mode 100644 index 00000000..492a3b8f --- /dev/null +++ b/websocketpp/common/cpp11.hpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_CPP11_HPP +#define WEBSOCKETPP_COMMON_CPP11_HPP + +/** + * This header sets up some constants based on the state of C++11 support + */ + +// Hide clang feature detection from other compilers +#ifndef __has_feature // Optional of course. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif +#ifndef __has_extension + #define __has_extension __has_feature // Compatibility with pre-3.0 compilers. +#endif + +// The code below attempts to use information provided by the build system or +// user supplied defines to selectively enable C++11 language and library +// features. In most cases features that are targeted individually may also be +// selectively disabled via an associated _WEBSOCKETPP_NOXXX_ define. + +#if defined(_WEBSOCKETPP_CPP11_STL_) || __cplusplus >= 201103L || defined(_WEBSOCKETPP_CPP11_STRICT_) + // This check tests for blanket c++11 coverage. It can be activated in one + // of three ways. Either the compiler itself reports that it is a full + // C++11 compiler via the __cplusplus macro or the user/build system + // supplies one of the two preprocessor defines below: + + // This is defined to allow other WebSocket++ common headers to enable + // C++11 features when they are detected by this file rather than + // duplicating the above logic in every common header. + #define _WEBSOCKETPP_CPP11_INTERNAL_ + + // _WEBSOCKETPP_CPP11_STRICT_ + // + // This define reports to WebSocket++ that 100% of the language and library + // features of C++11 are available. Using this define on a non-C++11 + // compiler will result in problems. + + // _WEBSOCKETPP_CPP11_STL_ + // + // This define enables *most* C++11 options that were implemented early on + // by compilers. It is typically used for compilers that have many, but not + // all C++11 features. It should be safe to use on GCC 4.7-4.8 and perhaps + // earlier. + #ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_ + #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept + #endif + #ifndef _WEBSOCKETPP_CONSTEXPR_TOKEN_ + #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr + #endif + #ifndef _WEBSOCKETPP_INITIALIZER_LISTS_ + #define _WEBSOCKETPP_INITIALIZER_LISTS_ + #endif + #ifndef _WEBSOCKETPP_NULLPTR_TOKEN_ + #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr + #endif + #ifndef _WEBSOCKETPP_MOVE_SEMANTICS_ + #define _WEBSOCKETPP_MOVE_SEMANTICS_ + #endif + #ifndef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + #define _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + #endif + + #ifndef __GNUC__ + // GCC as of version 4.9 (latest) does not support std::put_time yet. + // so ignore it + #define _WEBSOCKETPP_PUTTIME_ + #endif +#else + // In the absence of a blanket define, try to use compiler versions or + // feature testing macros to selectively enable what we can. + + // Test for noexcept + #ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_ + #ifdef _WEBSOCKETPP_NOEXCEPT_ + // build system says we have noexcept + #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept + #else + #if __has_feature(cxx_noexcept) + // clang feature detect says we have noexcept + #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept + #elif defined(_MSC_VER) && _MSC_VER >= 1900 + // Visual Studio 2015+ has noexcept + #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept + #else + // assume we don't have noexcept + #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ + #endif + #endif + #endif + + // Test for constexpr + #ifndef _WEBSOCKETPP_CONSTEXPR_TOKEN_ + #ifdef _WEBSOCKETPP_CONSTEXPR_ + // build system says we have constexpr + #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr + #else + #if __has_feature(cxx_constexpr) + // clang feature detect says we have constexpr + #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr + #elif defined(_MSC_VER) && _MSC_VER >= 1900 + // Visual Studio 2015+ has constexpr + #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr + #else + // assume we don't have constexpr + #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ + #endif + #endif + #endif + + // Enable initializer lists on clang when available. + #if __has_feature(cxx_generalized_initializers) && !defined(_WEBSOCKETPP_INITIALIZER_LISTS_) + #define _WEBSOCKETPP_INITIALIZER_LISTS_ + #endif + + // Test for nullptr + #ifndef _WEBSOCKETPP_NULLPTR_TOKEN_ + #ifdef _WEBSOCKETPP_NULLPTR_ + // build system says we have nullptr + #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr + #else + #if __has_feature(cxx_nullptr) + // clang feature detect says we have nullptr + #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr + #elif defined(_MSC_VER) &&_MSC_VER >= 1600 + // Visual Studio version that has nullptr + #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr + #else + // assume we don't have nullptr + #define _WEBSOCKETPP_NULLPTR_TOKEN_ 0 + #endif + #endif + #endif +#endif + +#endif // WEBSOCKETPP_COMMON_CPP11_HPP diff --git a/websocketpp/common/functional.hpp b/websocketpp/common/functional.hpp new file mode 100644 index 00000000..d332dd15 --- /dev/null +++ b/websocketpp/common/functional.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_FUNCTIONAL_HPP +#define WEBSOCKETPP_COMMON_FUNCTIONAL_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 functional header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_ + #ifndef _WEBSOCKETPP_CPP11_FUNCTIONAL_ + #define _WEBSOCKETPP_CPP11_FUNCTIONAL_ + #endif +#endif + +// If we're on Visual Studio 2010 or higher and haven't explicitly disabled +// the use of C++11 functional header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_ + #ifndef _WEBSOCKETPP_CPP11_FUNCTIONAL_ + #define _WEBSOCKETPP_CPP11_FUNCTIONAL_ + #endif +#endif + + + +#ifdef _WEBSOCKETPP_CPP11_FUNCTIONAL_ + #include +#else + #include + #include + #include +#endif + + + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_FUNCTIONAL_ + using std::function; + using std::bind; + using std::ref; + namespace placeholders = std::placeholders; + + // There are some cases where a C++11 compiler balks at using std::ref + // but a C++03 compiler using boost function requires boost::ref. As such + // lib::ref is not useful in these cases. Instead this macro allows the use + // of boost::ref in the case of a boost compile or no reference wrapper at + // all in the case of a C++11 compile + #define _WEBSOCKETPP_REF(x) x + + template + void clear_function(T & x) { + x = nullptr; + } +#else + using boost::function; + using boost::bind; + using boost::ref; + namespace placeholders { + /// \todo this feels hacky, is there a better way? + using ::_1; + using ::_2; + using ::_3; + } + + // See above definition for more details on what this is and why it exists + #define _WEBSOCKETPP_REF(x) boost::ref(x) + + template + void clear_function(T & x) { + x.clear(); + } +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_FUNCTIONAL_HPP diff --git a/websocketpp/common/md5.hpp b/websocketpp/common/md5.hpp new file mode 100644 index 00000000..279725f4 --- /dev/null +++ b/websocketpp/common/md5.hpp @@ -0,0 +1,448 @@ +/* + md5.hpp is a reformulation of the md5.h and md5.c code from + http://www.opensource.apple.com/source/cups/cups-59/cups/md5.c to allow it to + function as a component of a header only library. This conversion was done by + Peter Thorson (webmaster@zaphoyd.com) in 2012 for the WebSocket++ project. The + changes are released under the same license as the original (listed below) +*/ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef WEBSOCKETPP_COMMON_MD5_HPP +#define WEBSOCKETPP_COMMON_MD5_HPP + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +#include +#include +#include + +namespace websocketpp { +/// Provides MD5 hashing functionality +namespace md5 { + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +/* Initialize the algorithm. */ +inline void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +inline void md5_append(md5_state_t *pms, md5_byte_t const * data, size_t nbytes); + +/* Finish the message and return the digest. */ +inline void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#undef ZSW_MD5_BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define ZSW_MD5_BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define ZSW_MD5_BYTE_ORDER 0 +#endif + +#define ZSW_MD5_T_MASK ((md5_word_t)~0) +#define ZSW_MD5_T1 /* 0xd76aa478 */ (ZSW_MD5_T_MASK ^ 0x28955b87) +#define ZSW_MD5_T2 /* 0xe8c7b756 */ (ZSW_MD5_T_MASK ^ 0x173848a9) +#define ZSW_MD5_T3 0x242070db +#define ZSW_MD5_T4 /* 0xc1bdceee */ (ZSW_MD5_T_MASK ^ 0x3e423111) +#define ZSW_MD5_T5 /* 0xf57c0faf */ (ZSW_MD5_T_MASK ^ 0x0a83f050) +#define ZSW_MD5_T6 0x4787c62a +#define ZSW_MD5_T7 /* 0xa8304613 */ (ZSW_MD5_T_MASK ^ 0x57cfb9ec) +#define ZSW_MD5_T8 /* 0xfd469501 */ (ZSW_MD5_T_MASK ^ 0x02b96afe) +#define ZSW_MD5_T9 0x698098d8 +#define ZSW_MD5_T10 /* 0x8b44f7af */ (ZSW_MD5_T_MASK ^ 0x74bb0850) +#define ZSW_MD5_T11 /* 0xffff5bb1 */ (ZSW_MD5_T_MASK ^ 0x0000a44e) +#define ZSW_MD5_T12 /* 0x895cd7be */ (ZSW_MD5_T_MASK ^ 0x76a32841) +#define ZSW_MD5_T13 0x6b901122 +#define ZSW_MD5_T14 /* 0xfd987193 */ (ZSW_MD5_T_MASK ^ 0x02678e6c) +#define ZSW_MD5_T15 /* 0xa679438e */ (ZSW_MD5_T_MASK ^ 0x5986bc71) +#define ZSW_MD5_T16 0x49b40821 +#define ZSW_MD5_T17 /* 0xf61e2562 */ (ZSW_MD5_T_MASK ^ 0x09e1da9d) +#define ZSW_MD5_T18 /* 0xc040b340 */ (ZSW_MD5_T_MASK ^ 0x3fbf4cbf) +#define ZSW_MD5_T19 0x265e5a51 +#define ZSW_MD5_T20 /* 0xe9b6c7aa */ (ZSW_MD5_T_MASK ^ 0x16493855) +#define ZSW_MD5_T21 /* 0xd62f105d */ (ZSW_MD5_T_MASK ^ 0x29d0efa2) +#define ZSW_MD5_T22 0x02441453 +#define ZSW_MD5_T23 /* 0xd8a1e681 */ (ZSW_MD5_T_MASK ^ 0x275e197e) +#define ZSW_MD5_T24 /* 0xe7d3fbc8 */ (ZSW_MD5_T_MASK ^ 0x182c0437) +#define ZSW_MD5_T25 0x21e1cde6 +#define ZSW_MD5_T26 /* 0xc33707d6 */ (ZSW_MD5_T_MASK ^ 0x3cc8f829) +#define ZSW_MD5_T27 /* 0xf4d50d87 */ (ZSW_MD5_T_MASK ^ 0x0b2af278) +#define ZSW_MD5_T28 0x455a14ed +#define ZSW_MD5_T29 /* 0xa9e3e905 */ (ZSW_MD5_T_MASK ^ 0x561c16fa) +#define ZSW_MD5_T30 /* 0xfcefa3f8 */ (ZSW_MD5_T_MASK ^ 0x03105c07) +#define ZSW_MD5_T31 0x676f02d9 +#define ZSW_MD5_T32 /* 0x8d2a4c8a */ (ZSW_MD5_T_MASK ^ 0x72d5b375) +#define ZSW_MD5_T33 /* 0xfffa3942 */ (ZSW_MD5_T_MASK ^ 0x0005c6bd) +#define ZSW_MD5_T34 /* 0x8771f681 */ (ZSW_MD5_T_MASK ^ 0x788e097e) +#define ZSW_MD5_T35 0x6d9d6122 +#define ZSW_MD5_T36 /* 0xfde5380c */ (ZSW_MD5_T_MASK ^ 0x021ac7f3) +#define ZSW_MD5_T37 /* 0xa4beea44 */ (ZSW_MD5_T_MASK ^ 0x5b4115bb) +#define ZSW_MD5_T38 0x4bdecfa9 +#define ZSW_MD5_T39 /* 0xf6bb4b60 */ (ZSW_MD5_T_MASK ^ 0x0944b49f) +#define ZSW_MD5_T40 /* 0xbebfbc70 */ (ZSW_MD5_T_MASK ^ 0x4140438f) +#define ZSW_MD5_T41 0x289b7ec6 +#define ZSW_MD5_T42 /* 0xeaa127fa */ (ZSW_MD5_T_MASK ^ 0x155ed805) +#define ZSW_MD5_T43 /* 0xd4ef3085 */ (ZSW_MD5_T_MASK ^ 0x2b10cf7a) +#define ZSW_MD5_T44 0x04881d05 +#define ZSW_MD5_T45 /* 0xd9d4d039 */ (ZSW_MD5_T_MASK ^ 0x262b2fc6) +#define ZSW_MD5_T46 /* 0xe6db99e5 */ (ZSW_MD5_T_MASK ^ 0x1924661a) +#define ZSW_MD5_T47 0x1fa27cf8 +#define ZSW_MD5_T48 /* 0xc4ac5665 */ (ZSW_MD5_T_MASK ^ 0x3b53a99a) +#define ZSW_MD5_T49 /* 0xf4292244 */ (ZSW_MD5_T_MASK ^ 0x0bd6ddbb) +#define ZSW_MD5_T50 0x432aff97 +#define ZSW_MD5_T51 /* 0xab9423a7 */ (ZSW_MD5_T_MASK ^ 0x546bdc58) +#define ZSW_MD5_T52 /* 0xfc93a039 */ (ZSW_MD5_T_MASK ^ 0x036c5fc6) +#define ZSW_MD5_T53 0x655b59c3 +#define ZSW_MD5_T54 /* 0x8f0ccc92 */ (ZSW_MD5_T_MASK ^ 0x70f3336d) +#define ZSW_MD5_T55 /* 0xffeff47d */ (ZSW_MD5_T_MASK ^ 0x00100b82) +#define ZSW_MD5_T56 /* 0x85845dd1 */ (ZSW_MD5_T_MASK ^ 0x7a7ba22e) +#define ZSW_MD5_T57 0x6fa87e4f +#define ZSW_MD5_T58 /* 0xfe2ce6e0 */ (ZSW_MD5_T_MASK ^ 0x01d3191f) +#define ZSW_MD5_T59 /* 0xa3014314 */ (ZSW_MD5_T_MASK ^ 0x5cfebceb) +#define ZSW_MD5_T60 0x4e0811a1 +#define ZSW_MD5_T61 /* 0xf7537e82 */ (ZSW_MD5_T_MASK ^ 0x08ac817d) +#define ZSW_MD5_T62 /* 0xbd3af235 */ (ZSW_MD5_T_MASK ^ 0x42c50dca) +#define ZSW_MD5_T63 0x2ad7d2bb +#define ZSW_MD5_T64 /* 0xeb86d391 */ (ZSW_MD5_T_MASK ^ 0x14792c6e) + +static void md5_process(md5_state_t *pms, md5_byte_t const * data /*[64]*/) { + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if ZSW_MD5_BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + md5_word_t const * X; +#endif + + { +#if ZSW_MD5_BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static int const w = 1; + + if (*((md5_byte_t const *)&w)) /* dynamic little-endian */ +#endif +#if ZSW_MD5_BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (md5_byte_t const *)0) & 3)) { + /* data are properly aligned */ + X = (md5_word_t const *)data; + } else { + /* not aligned */ + std::memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if ZSW_MD5_BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if ZSW_MD5_BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if ZSW_MD5_BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ZSW_MD5_ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define ZSW_MD5_F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + ZSW_MD5_F(b,c,d) + X[k] + Ti;\ + a = ZSW_MD5_ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, ZSW_MD5_T1); + SET(d, a, b, c, 1, 12, ZSW_MD5_T2); + SET(c, d, a, b, 2, 17, ZSW_MD5_T3); + SET(b, c, d, a, 3, 22, ZSW_MD5_T4); + SET(a, b, c, d, 4, 7, ZSW_MD5_T5); + SET(d, a, b, c, 5, 12, ZSW_MD5_T6); + SET(c, d, a, b, 6, 17, ZSW_MD5_T7); + SET(b, c, d, a, 7, 22, ZSW_MD5_T8); + SET(a, b, c, d, 8, 7, ZSW_MD5_T9); + SET(d, a, b, c, 9, 12, ZSW_MD5_T10); + SET(c, d, a, b, 10, 17, ZSW_MD5_T11); + SET(b, c, d, a, 11, 22, ZSW_MD5_T12); + SET(a, b, c, d, 12, 7, ZSW_MD5_T13); + SET(d, a, b, c, 13, 12, ZSW_MD5_T14); + SET(c, d, a, b, 14, 17, ZSW_MD5_T15); + SET(b, c, d, a, 15, 22, ZSW_MD5_T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define ZSW_MD5_G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + ZSW_MD5_G(b,c,d) + X[k] + Ti;\ + a = ZSW_MD5_ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, ZSW_MD5_T17); + SET(d, a, b, c, 6, 9, ZSW_MD5_T18); + SET(c, d, a, b, 11, 14, ZSW_MD5_T19); + SET(b, c, d, a, 0, 20, ZSW_MD5_T20); + SET(a, b, c, d, 5, 5, ZSW_MD5_T21); + SET(d, a, b, c, 10, 9, ZSW_MD5_T22); + SET(c, d, a, b, 15, 14, ZSW_MD5_T23); + SET(b, c, d, a, 4, 20, ZSW_MD5_T24); + SET(a, b, c, d, 9, 5, ZSW_MD5_T25); + SET(d, a, b, c, 14, 9, ZSW_MD5_T26); + SET(c, d, a, b, 3, 14, ZSW_MD5_T27); + SET(b, c, d, a, 8, 20, ZSW_MD5_T28); + SET(a, b, c, d, 13, 5, ZSW_MD5_T29); + SET(d, a, b, c, 2, 9, ZSW_MD5_T30); + SET(c, d, a, b, 7, 14, ZSW_MD5_T31); + SET(b, c, d, a, 12, 20, ZSW_MD5_T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define ZSW_MD5_H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + ZSW_MD5_H(b,c,d) + X[k] + Ti;\ + a = ZSW_MD5_ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, ZSW_MD5_T33); + SET(d, a, b, c, 8, 11, ZSW_MD5_T34); + SET(c, d, a, b, 11, 16, ZSW_MD5_T35); + SET(b, c, d, a, 14, 23, ZSW_MD5_T36); + SET(a, b, c, d, 1, 4, ZSW_MD5_T37); + SET(d, a, b, c, 4, 11, ZSW_MD5_T38); + SET(c, d, a, b, 7, 16, ZSW_MD5_T39); + SET(b, c, d, a, 10, 23, ZSW_MD5_T40); + SET(a, b, c, d, 13, 4, ZSW_MD5_T41); + SET(d, a, b, c, 0, 11, ZSW_MD5_T42); + SET(c, d, a, b, 3, 16, ZSW_MD5_T43); + SET(b, c, d, a, 6, 23, ZSW_MD5_T44); + SET(a, b, c, d, 9, 4, ZSW_MD5_T45); + SET(d, a, b, c, 12, 11, ZSW_MD5_T46); + SET(c, d, a, b, 15, 16, ZSW_MD5_T47); + SET(b, c, d, a, 2, 23, ZSW_MD5_T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define ZSW_MD5_I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + ZSW_MD5_I(b,c,d) + X[k] + Ti;\ + a = ZSW_MD5_ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, ZSW_MD5_T49); + SET(d, a, b, c, 7, 10, ZSW_MD5_T50); + SET(c, d, a, b, 14, 15, ZSW_MD5_T51); + SET(b, c, d, a, 5, 21, ZSW_MD5_T52); + SET(a, b, c, d, 12, 6, ZSW_MD5_T53); + SET(d, a, b, c, 3, 10, ZSW_MD5_T54); + SET(c, d, a, b, 10, 15, ZSW_MD5_T55); + SET(b, c, d, a, 1, 21, ZSW_MD5_T56); + SET(a, b, c, d, 8, 6, ZSW_MD5_T57); + SET(d, a, b, c, 15, 10, ZSW_MD5_T58); + SET(c, d, a, b, 6, 15, ZSW_MD5_T59); + SET(b, c, d, a, 13, 21, ZSW_MD5_T60); + SET(a, b, c, d, 4, 6, ZSW_MD5_T61); + SET(d, a, b, c, 11, 10, ZSW_MD5_T62); + SET(c, d, a, b, 2, 15, ZSW_MD5_T63); + SET(b, c, d, a, 9, 21, ZSW_MD5_T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void md5_init(md5_state_t *pms) { + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ ZSW_MD5_T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ ZSW_MD5_T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void md5_append(md5_state_t *pms, md5_byte_t const * data, size_t nbytes) { + md5_byte_t const * p = data; + size_t left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : static_cast(nbytes)); + + std::memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + std::memcpy(pms->buf, p, left); +} + +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { + static md5_byte_t const pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} + +// some convenience c++ functions +inline std::string md5_hash_string(std::string const & s) { + char digest[16]; + + md5_state_t state; + + md5_init(&state); + md5_append(&state, (md5_byte_t const *)s.c_str(), s.size()); + md5_finish(&state, (md5_byte_t *)digest); + + std::string ret; + ret.resize(16); + std::copy(digest,digest+16,ret.begin()); + + return ret; +} + +const char hexval[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + +inline std::string md5_hash_hex(std::string const & input) { + std::string hash = md5_hash_string(input); + std::string hex; + + for (size_t i = 0; i < hash.size(); i++) { + hex.push_back(hexval[((hash[i] >> 4) & 0xF)]); + hex.push_back(hexval[(hash[i]) & 0x0F]); + } + + return hex; +} + +} // md5 +} // websocketpp + +#endif // WEBSOCKETPP_COMMON_MD5_HPP diff --git a/websocketpp/common/memory.hpp b/websocketpp/common/memory.hpp new file mode 100644 index 00000000..581aa559 --- /dev/null +++ b/websocketpp/common/memory.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_MEMORY_HPP +#define WEBSOCKETPP_COMMON_MEMORY_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 memory header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_ + #ifndef _WEBSOCKETPP_CPP11_MEMORY_ + #define _WEBSOCKETPP_CPP11_MEMORY_ + #endif +#endif + +// If we're on Visual Studio 2010 or higher and haven't explicitly disabled +// the use of C++11 functional header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_ + #ifndef _WEBSOCKETPP_CPP11_MEMORY_ + #define _WEBSOCKETPP_CPP11_MEMORY_ + #endif +#endif + + + +#ifdef _WEBSOCKETPP_CPP11_MEMORY_ + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_MEMORY_ + using std::shared_ptr; + using std::weak_ptr; + using std::auto_ptr; + using std::enable_shared_from_this; + using std::static_pointer_cast; + using std::make_shared; + using std::unique_ptr; + + typedef std::unique_ptr unique_ptr_uchar_array; +#else + using boost::shared_ptr; + using boost::weak_ptr; + using std::auto_ptr; + using boost::enable_shared_from_this; + using boost::static_pointer_cast; + using boost::make_shared; + + typedef boost::scoped_array unique_ptr_uchar_array; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_MEMORY_HPP diff --git a/websocketpp/common/network.hpp b/websocketpp/common/network.hpp new file mode 100644 index 00000000..26a4cb9e --- /dev/null +++ b/websocketpp/common/network.hpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_NETWORK_HPP +#define WEBSOCKETPP_COMMON_NETWORK_HPP + +// For ntohs and htons +#if defined(_WIN32) + #include +#else + //#include + #include +#endif + +#include + +namespace websocketpp { +namespace lib { +namespace net { + +inline bool is_little_endian() { + short int val = 0x1; + char *ptr = reinterpret_cast(&val); + return (ptr[0] == 1); +} + +#define TYP_INIT 0 +#define TYP_SMLE 1 +#define TYP_BIGE 2 + +/// Convert 64 bit value to network byte order +/** + * This method is prefixed to avoid conflicts with operating system level + * macros for this functionality. + * + * TODO: figure out if it would be beneficial to use operating system level + * macros for this. + * + * @param src The integer in host byte order + * @return src converted to network byte order + */ +inline uint64_t _htonll(uint64_t src) { + static int typ = TYP_INIT; + unsigned char c; + union { + uint64_t ull; + unsigned char c[8]; + } x; + if (typ == TYP_INIT) { + x.ull = 0x01; + typ = (x.c[7] == 0x01ULL) ? TYP_BIGE : TYP_SMLE; + } + if (typ == TYP_BIGE) + return src; + x.ull = src; + c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c; + c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c; + c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c; + c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c; + return x.ull; +} + +/// Convert 64 bit value to host byte order +/** + * This method is prefixed to avoid conflicts with operating system level + * macros for this functionality. + * + * TODO: figure out if it would be beneficial to use operating system level + * macros for this. + * + * @param src The integer in network byte order + * @return src converted to host byte order + */ +inline uint64_t _ntohll(uint64_t src) { + return _htonll(src); +} + +} // net +} // lib +} // websocketpp + +#endif // WEBSOCKETPP_COMMON_NETWORK_HPP diff --git a/websocketpp/common/platforms.hpp b/websocketpp/common/platforms.hpp new file mode 100644 index 00000000..87798577 --- /dev/null +++ b/websocketpp/common/platforms.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_PLATFORMS_HPP +#define WEBSOCKETPP_COMMON_PLATFORMS_HPP + +/** + * This header contains any platform specific preprocessor adjustments that + * don't fit somewhere else better. + */ + +#if defined(_WIN32) && !defined(NOMINMAX) + // don't define min and max macros that conflict with std::min and std::max + #define NOMINMAX +#endif + +// Bump up the variadic parameter max for Visual Studio 2012 +#if defined(_MSC_VER) && _MSC_VER == 1700 + #define _VARIADIC_MAX 8 +#endif + +#endif // WEBSOCKETPP_COMMON_PLATFORMS_HPP diff --git a/websocketpp/common/random.hpp b/websocketpp/common/random.hpp new file mode 100644 index 00000000..ddf99694 --- /dev/null +++ b/websocketpp/common/random.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP +#define WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 random header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_RANDOM_DEVICE_ + #ifndef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ + #define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ + #endif +#endif + + +// If we're on Visual Studio 2010 or higher and haven't explicitly disabled +// the use of C++11 random header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_ + #ifndef _WEBSOCKETPP_CPP11_MEMORY_ + #define _WEBSOCKETPP_CPP11_MEMORY_ + #endif +#endif + + + +#ifdef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ + #include +#else + #include + + #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 46 + #include + #include + #elif (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) >= 43 + #include + #else + // TODO: static_assert(false, "Could not find a suitable random_device") + #endif +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ + using std::random_device; + using std::uniform_int_distribution; +#else + using boost::random::random_device; + using boost::random::uniform_int_distribution; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP diff --git a/websocketpp/common/regex.hpp b/websocketpp/common/regex.hpp new file mode 100644 index 00000000..326635de --- /dev/null +++ b/websocketpp/common/regex.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_REGEX_HPP +#define WEBSOCKETPP_COMMON_REGEX_HPP + +#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_REGEX_ + #ifndef _WEBSOCKETPP_CPP11_REGEX_ + #define _WEBSOCKETPP_CPP11_REGEX_ + #endif +#endif + +#ifdef _WEBSOCKETPP_CPP11_REGEX_ + #include +#else + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_REGEX_ + using std::cmatch; + using std::regex; + using std::regex_match; +#else + using boost::cmatch; + using boost::regex; + using boost::regex_match; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_REGEX_HPP diff --git a/websocketpp/common/stdint.hpp b/websocketpp/common/stdint.hpp new file mode 100644 index 00000000..ec48ea75 --- /dev/null +++ b/websocketpp/common/stdint.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_STDINT_HPP +#define WEBSOCKETPP_COMMON_STDINT_HPP + +#ifndef __STDC_LIMIT_MACROS + #define __STDC_LIMIT_MACROS 1 +#endif + +#if defined (_WIN32) && defined (_MSC_VER) && (_MSC_VER < 1600) + #include + + using boost::int8_t; + using boost::int_least8_t; + using boost::int_fast8_t; + using boost::uint8_t; + using boost::uint_least8_t; + using boost::uint_fast8_t; + + using boost::int16_t; + using boost::int_least16_t; + using boost::int_fast16_t; + using boost::uint16_t; + using boost::uint_least16_t; + using boost::uint_fast16_t; + + using boost::int32_t; + using boost::int_least32_t; + using boost::int_fast32_t; + using boost::uint32_t; + using boost::uint_least32_t; + using boost::uint_fast32_t; + + #ifndef BOOST_NO_INT64_T + using boost::int64_t; + using boost::int_least64_t; + using boost::int_fast64_t; + using boost::uint64_t; + using boost::uint_least64_t; + using boost::uint_fast64_t; + #endif + using boost::intmax_t; + using boost::uintmax_t; +#else + #include +#endif + +#endif // WEBSOCKETPP_COMMON_STDINT_HPP diff --git a/websocketpp/common/system_error.hpp b/websocketpp/common/system_error.hpp new file mode 100644 index 00000000..e5aea254 --- /dev/null +++ b/websocketpp/common/system_error.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP +#define WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP + + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 system_error header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_ + #ifndef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + #define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + #endif +#endif + +// If we're on Visual Studio 2010 or higher and haven't explicitly disabled +// the use of C++11 system_error header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_ + #ifndef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + #define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + #endif +#endif + + + +#ifdef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + #include +#else + #include + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ + using std::errc; + using std::error_code; + using std::error_category; + using std::error_condition; + using std::system_error; + #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ namespace std { + #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ } +#else + namespace errc = boost::system::errc; + using boost::system::error_code; + using boost::system::error_category; + using boost::system::error_condition; + using boost::system::system_error; + #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ namespace boost { namespace system { + #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ }} +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP diff --git a/websocketpp/common/thread.hpp b/websocketpp/common/thread.hpp new file mode 100644 index 00000000..09f6b3c5 --- /dev/null +++ b/websocketpp/common/thread.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_THREAD_HPP +#define WEBSOCKETPP_COMMON_THREAD_HPP + +#include + +// If we autodetect C++11 and haven't been explicitly instructed to not use +// C++11 threads, then set the defines that instructs the rest of this header +// to use C++11 and +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_THREAD_ + // MinGW by default does not support C++11 thread/mutex so even if the + // internal check for C++11 passes, ignore it if we are on MinGW + #if (!defined(__MINGW32__) && !defined(__MINGW64__)) + #ifndef _WEBSOCKETPP_CPP11_THREAD_ + #define _WEBSOCKETPP_CPP11_THREAD_ + #endif + #endif +#endif + +// If we're on Visual Studio 2013 or higher and haven't explicitly disabled +// the use of C++11 thread header then prefer it to boost. +#if defined(_MSC_VER) && _MSC_VER >= 1800 && !defined _WEBSOCKETPP_NO_CPP11_THREAD_ + #ifndef _WEBSOCKETPP_CPP11_THREAD_ + #define _WEBSOCKETPP_CPP11_THREAD_ + #endif +#endif + +#ifdef _WEBSOCKETPP_CPP11_THREAD_ + #include + #include + #include +#else + #include + #include + #include +#endif + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_THREAD_ + using std::mutex; + using std::lock_guard; + using std::thread; + using std::unique_lock; + using std::condition_variable; +#else + using boost::mutex; + using boost::lock_guard; + using boost::thread; + using boost::unique_lock; + using boost::condition_variable; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_THREAD_HPP diff --git a/websocketpp/common/time.hpp b/websocketpp/common/time.hpp new file mode 100644 index 00000000..571688e1 --- /dev/null +++ b/websocketpp/common/time.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_TIME_HPP +#define WEBSOCKETPP_COMMON_TIME_HPP + +#include + +namespace websocketpp { +namespace lib { + +// Code in this header was inspired by the following article and includes some +// code from the related project g2log. The g2log code is public domain licensed +// http://kjellkod.wordpress.com/2013/01/22/exploring-c11-part-2-localtime-and-time-again/ + +/// Thread safe cross platform localtime +inline std::tm localtime(std::time_t const & time) { + std::tm tm_snapshot; +#if (defined(__MINGW32__) || defined(__MINGW64__)) + memcpy(&tm_snapshot, ::localtime(&time), sizeof(std::tm)); +#elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) + localtime_s(&tm_snapshot, &time); +#else + localtime_r(&time, &tm_snapshot); // POSIX +#endif + return tm_snapshot; +} + +} // lib +} // websocketpp + +#endif // WEBSOCKETPP_COMMON_TIME_HPP diff --git a/websocketpp/common/type_traits.hpp b/websocketpp/common/type_traits.hpp new file mode 100644 index 00000000..c89b82b3 --- /dev/null +++ b/websocketpp/common/type_traits.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP +#define WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP + +#include + +// If we've determined that we're in full C++11 mode and the user hasn't +// explicitly disabled the use of C++11 functional header, then prefer it to +// boost. +#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_TYPE_TRAITS_ + #ifndef _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + #define _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + #endif +#endif + + +#ifdef _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + #include +#else + #include +#endif + + + +namespace websocketpp { +namespace lib { + +#ifdef _WEBSOCKETPP_CPP11_TYPE_TRAITS_ + using std::aligned_storage; + using std::is_same; +#else + using boost::aligned_storage; + using boost::is_same; +#endif + +} // namespace lib +} // namespace websocketpp + +#endif // WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP diff --git a/websocketpp/concurrency/basic.hpp b/websocketpp/concurrency/basic.hpp new file mode 100644 index 00000000..1943ad77 --- /dev/null +++ b/websocketpp/concurrency/basic.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONCURRENCY_BASIC_HPP +#define WEBSOCKETPP_CONCURRENCY_BASIC_HPP + +#include + +namespace websocketpp { +namespace concurrency { + +/// Concurrency policy that uses std::mutex / boost::mutex +class basic { +public: + typedef lib::mutex mutex_type; + typedef lib::lock_guard scoped_lock_type; +}; + +} // namespace concurrency +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONCURRENCY_BASIC_HPP diff --git a/websocketpp/concurrency/none.hpp b/websocketpp/concurrency/none.hpp new file mode 100644 index 00000000..da9aa411 --- /dev/null +++ b/websocketpp/concurrency/none.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONCURRENCY_NONE_HPP +#define WEBSOCKETPP_CONCURRENCY_NONE_HPP + +namespace websocketpp { + +/// Concurrency handling support +namespace concurrency { + +/// Implementation for no-op locking primitives +namespace none_impl { +/// A fake mutex implementation that does nothing +class fake_mutex { +public: + fake_mutex() {} + ~fake_mutex() {} +}; + +/// A fake lock guard implementation that does nothing +class fake_lock_guard { +public: + explicit fake_lock_guard(fake_mutex) {} + ~fake_lock_guard() {} +}; +} // namespace none_impl + +/// Stub concurrency policy that implements the interface using no-ops. +/** + * This policy documents the concurrency policy interface using no-ops. It can + * be used as a reference or base for building a new concurrency policy. It can + * also be used as is to disable all locking for endpoints used in purely single + * threaded programs. + */ +class none { +public: + /// The type of a mutex primitive + /** + * std::mutex is an example. + */ + typedef none_impl::fake_mutex mutex_type; + + /// The type of a scoped/RAII lock primitive. + /** + * The scoped lock constructor should take a mutex_type as a parameter, + * acquire that lock, and release it in its destructor. std::lock_guard is + * an example. + */ + typedef none_impl::fake_lock_guard scoped_lock_type; +}; + +} // namespace concurrency +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONCURRENCY_ASYNC_HPP diff --git a/websocketpp/config/asio.hpp b/websocketpp/config/asio.hpp new file mode 100644 index 00000000..d28d0fb8 --- /dev/null +++ b/websocketpp/config/asio.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_TLS_HPP +#define WEBSOCKETPP_CONFIG_ASIO_TLS_HPP + +#include +#include +#include + +// Pull in non-tls config +#include + +// Define TLS config +namespace websocketpp { +namespace config { + +/// Server config with asio transport and TLS enabled +struct asio_tls : public core { + typedef asio_tls type; + typedef core base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::tls_socket::endpoint socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_TLS_HPP diff --git a/websocketpp/config/asio_client.hpp b/websocketpp/config/asio_client.hpp new file mode 100644 index 00000000..1cb594d0 --- /dev/null +++ b/websocketpp/config/asio_client.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_TLS_CLIENT_HPP +#define WEBSOCKETPP_CONFIG_ASIO_TLS_CLIENT_HPP + +#include +#include +#include + +// Pull in non-tls config +#include + +// Define TLS config +namespace websocketpp { +namespace config { + +/// Client config with asio transport and TLS enabled +struct asio_tls_client : public core_client { + typedef asio_tls_client type; + typedef core_client base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::tls_socket::endpoint socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_TLS_CLIENT_HPP diff --git a/websocketpp/config/asio_no_tls.hpp b/websocketpp/config/asio_no_tls.hpp new file mode 100644 index 00000000..6c1357fb --- /dev/null +++ b/websocketpp/config/asio_no_tls.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_HPP +#define WEBSOCKETPP_CONFIG_ASIO_HPP + +#include +#include + +namespace websocketpp { +namespace config { + +/// Server config with asio transport and TLS disabled +struct asio : public core { + typedef asio type; + typedef core base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::basic_socket::endpoint + socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_HPP diff --git a/websocketpp/config/asio_no_tls_client.hpp b/websocketpp/config/asio_no_tls_client.hpp new file mode 100644 index 00000000..6e3f7ba0 --- /dev/null +++ b/websocketpp/config/asio_no_tls_client.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_CLIENT_HPP +#define WEBSOCKETPP_CONFIG_ASIO_CLIENT_HPP + +#include +#include + +namespace websocketpp { +namespace config { + +/// Client config with asio transport and TLS disabled +struct asio_client : public core_client { + typedef asio_client type; + typedef core_client base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::basic_socket::endpoint + socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_CLIENT_HPP diff --git a/websocketpp/config/boost_config.hpp b/websocketpp/config/boost_config.hpp new file mode 100644 index 00000000..57671ccd --- /dev/null +++ b/websocketpp/config/boost_config.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + // This header defines WebSocket++ macros for C++11 compatibility based on the + // Boost.Config library. This will correctly configure most target platforms + // simply by including this header before any other WebSocket++ header. + +#ifndef WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP +#define WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP + +#include + +// _WEBSOCKETPP_CPP11_MEMORY_ and _WEBSOCKETPP_CPP11_FUNCTIONAL_ presently +// only work if either both or neither is defined. +#if !defined BOOST_NO_CXX11_SMART_PTR && !defined BOOST_NO_CXX11_HDR_FUNCTIONAL + #define _WEBSOCKETPP_CPP11_MEMORY_ + #define _WEBSOCKETPP_CPP11_FUNCTIONAL_ +#endif + +#ifdef BOOST_ASIO_HAS_STD_CHRONO + #define _WEBSOCKETPP_CPP11_CHRONO_ +#endif + +#ifndef BOOST_NO_CXX11_HDR_RANDOM + #define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_ +#endif + +#ifndef BOOST_NO_CXX11_HDR_REGEX + #define _WEBSOCKETPP_CPP11_REGEX_ +#endif + +#ifndef BOOST_NO_CXX11_HDR_SYSTEM_ERROR + #define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_ +#endif + +#ifndef BOOST_NO_CXX11_HDR_THREAD + #define _WEBSOCKETPP_CPP11_THREAD_ +#endif + +#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST + #define _WEBSOCKETPP_INITIALIZER_LISTS_ +#endif + +#define _WEBSOCKETPP_NOEXCEPT_TOKEN_ BOOST_NOEXCEPT +#define _WEBSOCKETPP_CONSTEXPR_TOKEN_ BOOST_CONSTEXPR +// TODO: nullptr support + +#endif // WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP diff --git a/websocketpp/config/core.hpp b/websocketpp/config/core.hpp new file mode 100644 index 00000000..a95b4021 --- /dev/null +++ b/websocketpp/config/core.hpp @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_CORE_HPP +#define WEBSOCKETPP_CONFIG_CORE_HPP + +// Non-Policy common stuff +#include +#include +#include + +// Concurrency +#include + +// Transport +#include + +// HTTP +#include +#include + +// Messages +#include +#include + +// Loggers +#include + +// RNG +#include + +// User stub base classes +#include +#include + +// Extensions +#include + +namespace websocketpp { +namespace config { + +/// Server config with iostream transport +struct core { + typedef core type; + + // Concurrency policy + typedef websocketpp::concurrency::basic concurrency_type; + + // HTTP Parser Policies + typedef http::parser::request request_type; + typedef http::parser::response response_type; + + // Message Policies + typedef message_buffer::message + message_type; + typedef message_buffer::alloc::con_msg_manager + con_msg_manager_type; + typedef message_buffer::alloc::endpoint_msg_manager + endpoint_msg_manager_type; + + /// Logging policies + typedef websocketpp::log::basic elog_type; + typedef websocketpp::log::basic alog_type; + + /// RNG policies + typedef websocketpp::random::none::int_generator rng_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + struct transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::elog_type elog_type; + typedef type::alog_type alog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + /// Default timer values (in ms) + + /// Length of time to wait for socket pre-initialization + /** + * Exactly what this includes depends on the socket policy in use + */ + static const long timeout_socket_pre_init = 5000; + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + + /// Length of time to wait for socket post-initialization + /** + * Exactly what this includes depends on the socket policy in use. + * Often this means the TLS handshake + */ + static const long timeout_socket_post_init = 5000; + + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + + /// Length of time to wait for TCP connect + static const long timeout_connect = 5000; + + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; + }; + + /// Transport Endpoint Component + typedef websocketpp::transport::iostream::endpoint + transport_type; + + /// User overridable Endpoint base class + typedef websocketpp::endpoint_base endpoint_base; + /// User overridable Connection base class + typedef websocketpp::connection_base connection_base; + + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + + /// WebSocket Protocol version to use as a client + /** + * What version of the WebSocket Protocol to use for outgoing client + * connections. Setting this to a value other than 13 (RFC6455) is not + * recommended. + */ + static const int client_version = 13; // RFC6455 + + /// Default static error logging channels + /** + * Which error logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level errors + */ + static const websocketpp::log::level elog_level = + websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel; + + /// Default static access logging channels + /** + * Which access logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level access messages + */ + static const websocketpp::log::level alog_level = + websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel; + + /// + static const size_t connection_read_buffer_size = 16384; + + /// Drop connections immediately on protocol error. + /** + * Drop connections on protocol error rather than sending a close frame. + * Off by default. This may result in legit messages near the error being + * dropped as well. It may free up resources otherwise spent dealing with + * misbehaving clients. + */ + static const bool drop_on_protocol_error = false; + + /// Suppresses the return of detailed connection close information + /** + * Silence close suppresses the return of detailed connection close + * information during the closing handshake. This information is useful + * for debugging and presenting useful errors to end users but may be + * undesirable for security reasons in some production environments. + * Close reasons could be used by an attacker to confirm that the endpoint + * is out of resources or be used to identify the WebSocket implementation + * in use. + * + * Note: this will suppress *all* close codes, including those explicitly + * sent by local applications. + */ + static const bool silent_close = false; + + /// Default maximum message size + /** + * Default value for the processor's maximum message size. Maximum message size + * determines the point at which the library will fail a connection with the + * message_too_big protocol error. + * + * The default is 32MB + * + * @since 0.3.0 + */ + static const size_t max_message_size = 32000000; + + /// Default maximum http body size + /** + * Default value for the http parser's maximum body size. Maximum body size + * determines the point at which the library will abort reading an HTTP + * connection with the 413/request entity too large error. + * + * The default is 32MB + * + * @since 0.5.0 + */ + static const size_t max_http_body_size = 32000000; + + /// Global flag for enabling/disabling extensions + static const bool enable_extensions = true; + + /// Extension specific settings: + + /// permessage_compress extension + struct permessage_deflate_config { + typedef core::request_type request_type; + + /// If the remote endpoint requests that we reset the compression + /// context after each message should we honor the request? + static const bool allow_disabling_context_takeover = true; + + /// If the remote endpoint requests that we reduce the size of the + /// LZ77 sliding window size this is the lowest value that will be + /// allowed. Values range from 8 to 15. A value of 8 means we will + /// allow any possible window size. A value of 15 means do not allow + /// negotiation of the window size (ie require the default). + static const uint8_t minimum_outgoing_window_bits = 8; + }; + + typedef websocketpp::extensions::permessage_deflate::disabled + permessage_deflate_type; + + /// Autonegotiate permessage-deflate + /** + * Automatically enables the permessage-deflate extension. + * + * For clients this results in a permessage-deflate extension request being + * sent with every request rather than requiring it to be requested manually + * + * For servers this results in accepting the first set of extension settings + * requested by the client that we understand being used. The alternative is + * requiring the extension to be manually negotiated in `validate`. With + * auto-negotiate on, you may still override the auto-negotiate manually if + * needed. + */ + //static const bool autonegotiate_compression = false; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_CORE_HPP diff --git a/websocketpp/config/core_client.hpp b/websocketpp/config/core_client.hpp new file mode 100644 index 00000000..dadf8a4e --- /dev/null +++ b/websocketpp/config/core_client.hpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_CORE_CLIENT_HPP +#define WEBSOCKETPP_CONFIG_CORE_CLIENT_HPP + +// Non-Policy common stuff +#include +#include +#include + +// Concurrency +#ifndef _WEBSOCKETPP_NO_THREADING_ +#include +#else +#include +#endif + +// Transport +#include + +// HTTP +#include +#include + +// Messages +#include +#include + +// Loggers +#include + +// RNG +#include + +// User stub base classes +#include +#include + +// Extensions +#include + +namespace websocketpp { +namespace config { + +/// Client config with iostream transport +struct core_client { + typedef core_client type; + + // Concurrency policy +#ifndef _WEBSOCKETPP_NO_THREADING_ + typedef websocketpp::concurrency::basic concurrency_type; +#else + typedef websocketpp::concurrency::none concurrency_type; +#endif + + // HTTP Parser Policies + typedef http::parser::request request_type; + typedef http::parser::response response_type; + + // Message Policies + typedef message_buffer::message + message_type; + typedef message_buffer::alloc::con_msg_manager + con_msg_manager_type; + typedef message_buffer::alloc::endpoint_msg_manager + endpoint_msg_manager_type; + + /// Logging policies + typedef websocketpp::log::basic elog_type; + typedef websocketpp::log::basic alog_type; + + /// RNG policies + typedef websocketpp::random::random_device::int_generator rng_type; + + /// Controls compile time enabling/disabling of thread syncronization code + /// Disabling can provide a minor performance improvement to single threaded + /// applications + static bool const enable_multithreading = true; + + struct transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::elog_type elog_type; + typedef type::alog_type alog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + /// Default timer values (in ms) + + /// Length of time to wait for socket pre-initialization + /** + * Exactly what this includes depends on the socket policy in use + */ + static const long timeout_socket_pre_init = 5000; + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + + /// Length of time to wait for socket post-initialization + /** + * Exactly what this includes depends on the socket policy in use. + * Often this means the TLS handshake + */ + static const long timeout_socket_post_init = 5000; + + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + + /// Length of time to wait for TCP connect + static const long timeout_connect = 5000; + + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; + }; + + /// Transport Endpoint Component + typedef websocketpp::transport::iostream::endpoint + transport_type; + + /// User overridable Endpoint base class + typedef websocketpp::endpoint_base endpoint_base; + /// User overridable Connection base class + typedef websocketpp::connection_base connection_base; + + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + + /// WebSocket Protocol version to use as a client + /** + * What version of the WebSocket Protocol to use for outgoing client + * connections. Setting this to a value other than 13 (RFC6455) is not + * recommended. + */ + static const int client_version = 13; // RFC6455 + + /// Default static error logging channels + /** + * Which error logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level errors + */ + static const websocketpp::log::level elog_level = + websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel; + + /// Default static access logging channels + /** + * Which access logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level access messages + */ + static const websocketpp::log::level alog_level = + websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel; + + /// + static const size_t connection_read_buffer_size = 16384; + + /// Drop connections immediately on protocol error. + /** + * Drop connections on protocol error rather than sending a close frame. + * Off by default. This may result in legit messages near the error being + * dropped as well. It may free up resources otherwise spent dealing with + * misbehaving clients. + */ + static const bool drop_on_protocol_error = false; + + /// Suppresses the return of detailed connection close information + /** + * Silence close suppresses the return of detailed connection close + * information during the closing handshake. This information is useful + * for debugging and presenting useful errors to end users but may be + * undesirable for security reasons in some production environments. + * Close reasons could be used by an attacker to confirm that the endpoint + * is out of resources or be used to identify the WebSocket implementation + * in use. + * + * Note: this will suppress *all* close codes, including those explicitly + * sent by local applications. + */ + static const bool silent_close = false; + + /// Default maximum message size + /** + * Default value for the processor's maximum message size. Maximum message size + * determines the point at which the library will fail a connection with the + * message_too_big protocol error. + * + * The default is 32MB + * + * @since 0.3.0 + */ + static const size_t max_message_size = 32000000; + + /// Default maximum http body size + /** + * Default value for the http parser's maximum body size. Maximum body size + * determines the point at which the library will abort reading an HTTP + * connection with the 413/request entity too large error. + * + * The default is 32MB + * + * @since 0.5.0 + */ + static const size_t max_http_body_size = 32000000; + + /// Global flag for enabling/disabling extensions + static const bool enable_extensions = true; + + /// Extension specific settings: + + /// permessage_deflate extension + struct permessage_deflate_config { + typedef core_client::request_type request_type; + + /// If the remote endpoint requests that we reset the compression + /// context after each message should we honor the request? + static const bool allow_disabling_context_takeover = true; + + /// If the remote endpoint requests that we reduce the size of the + /// LZ77 sliding window size this is the lowest value that will be + /// allowed. Values range from 8 to 15. A value of 8 means we will + /// allow any possible window size. A value of 15 means do not allow + /// negotiation of the window size (ie require the default). + static const uint8_t minimum_outgoing_window_bits = 8; + }; + + typedef websocketpp::extensions::permessage_deflate::disabled + permessage_deflate_type; + + /// Autonegotiate permessage-compress + /** + * Automatically enables the permessage-compress extension. + * + * For clients this results in a permessage-compress extension request being + * sent with every request rather than requiring it to be requested manually + * + * For servers this results in accepting the first set of extension settings + * requested by the client that we understand being used. The alternative is + * requiring the extension to be manually negotiated in `validate`. With + * auto-negotiate on, you may still override the auto-negotiate manually if + * needed. + */ + //static const bool autonegotiate_compression = false; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_CORE_CLIENT_HPP diff --git a/websocketpp/config/debug.hpp b/websocketpp/config/debug.hpp new file mode 100644 index 00000000..223f72fb --- /dev/null +++ b/websocketpp/config/debug.hpp @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_DEBUG_HPP +#define WEBSOCKETPP_CONFIG_DEBUG_HPP + + + +// Non-Policy common stuff +#include +#include + +// Concurrency +#include + +// Transport +#include + +// HTTP +#include +#include + +// Messages +#include +#include + +// Loggers +#include + +// RNG +#include + +// User stub base classes +#include +#include + +// Extensions +#include + +namespace websocketpp { +namespace config { + +/// Client/Server debug config with iostream transport +struct debug_core { + typedef debug_core type; + + // Concurrency policy + typedef websocketpp::concurrency::basic concurrency_type; + + // HTTP Parser Policies + typedef http::parser::request request_type; + typedef http::parser::response response_type; + + // Message Policies + typedef message_buffer::message + message_type; + typedef message_buffer::alloc::con_msg_manager + con_msg_manager_type; + typedef message_buffer::alloc::endpoint_msg_manager + endpoint_msg_manager_type; + + /// Logging policies + typedef websocketpp::log::basic elog_type; + typedef websocketpp::log::basic alog_type; + + /// RNG policies + typedef websocketpp::random::none::int_generator rng_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + struct transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::elog_type elog_type; + typedef type::alog_type alog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + /// Default timer values (in ms) + + /// Length of time to wait for socket pre-initialization + /** + * Exactly what this includes depends on the socket policy in use + */ + static const long timeout_socket_pre_init = 5000; + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + + /// Length of time to wait for socket post-initialization + /** + * Exactly what this includes depends on the socket policy in use. + * Often this means the TLS handshake + */ + static const long timeout_socket_post_init = 5000; + + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + + /// Length of time to wait for TCP connect + static const long timeout_connect = 5000; + + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; + }; + + /// Transport Endpoint Component + typedef websocketpp::transport::iostream::endpoint + transport_type; + + /// User overridable Endpoint base class + typedef websocketpp::endpoint_base endpoint_base; + /// User overridable Connection base class + typedef websocketpp::connection_base connection_base; + + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + + /// WebSocket Protocol version to use as a client + /** + * What version of the WebSocket Protocol to use for outgoing client + * connections. Setting this to a value other than 13 (RFC6455) is not + * recommended. + */ + static const int client_version = 13; // RFC6455 + + /// Default static error logging channels + /** + * Which error logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level errors + */ + static const websocketpp::log::level elog_level = + websocketpp::log::elevel::all; + + /// Default static access logging channels + /** + * Which access logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level access messages + */ + static const websocketpp::log::level alog_level = + websocketpp::log::alevel::all; + + /// + static const size_t connection_read_buffer_size = 16384; + + /// Drop connections immediately on protocol error. + /** + * Drop connections on protocol error rather than sending a close frame. + * Off by default. This may result in legit messages near the error being + * dropped as well. It may free up resources otherwise spent dealing with + * misbehaving clients. + */ + static const bool drop_on_protocol_error = false; + + /// Suppresses the return of detailed connection close information + /** + * Silence close suppresses the return of detailed connection close + * information during the closing handshake. This information is useful + * for debugging and presenting useful errors to end users but may be + * undesirable for security reasons in some production environments. + * Close reasons could be used by an attacker to confirm that the endpoint + * is out of resources or be used to identify the WebSocket implementation + * in use. + * + * Note: this will suppress *all* close codes, including those explicitly + * sent by local applications. + */ + static const bool silent_close = false; + + /// Default maximum message size + /** + * Default value for the processor's maximum message size. Maximum message size + * determines the point at which the library will fail a connection with the + * message_too_big protocol error. + * + * The default is 32MB + * + * @since 0.3.0 + */ + static const size_t max_message_size = 32000000; + + /// Default maximum http body size + /** + * Default value for the http parser's maximum body size. Maximum body size + * determines the point at which the library will abort reading an HTTP + * connection with the 413/request entity too large error. + * + * The default is 32MB + * + * @since 0.5.0 + */ + static const size_t max_http_body_size = 32000000; + + /// Global flag for enabling/disabling extensions + static const bool enable_extensions = true; + + /// Extension specific settings: + + /// permessage_compress extension + struct permessage_deflate_config { + typedef type::request_type request_type; + + /// If the remote endpoint requests that we reset the compression + /// context after each message should we honor the request? + static const bool allow_disabling_context_takeover = true; + + /// If the remote endpoint requests that we reduce the size of the + /// LZ77 sliding window size this is the lowest value that will be + /// allowed. Values range from 8 to 15. A value of 8 means we will + /// allow any possible window size. A value of 15 means do not allow + /// negotiation of the window size (ie require the default). + static const uint8_t minimum_outgoing_window_bits = 8; + }; + + typedef websocketpp::extensions::permessage_deflate::disabled + permessage_deflate_type; + + /// Autonegotiate permessage-deflate + /** + * Automatically enables the permessage-deflate extension. + * + * For clients this results in a permessage-deflate extension request being + * sent with every request rather than requiring it to be requested manually + * + * For servers this results in accepting the first set of extension settings + * requested by the client that we understand being used. The alternative is + * requiring the extension to be manually negotiated in `validate`. With + * auto-negotiate on, you may still override the auto-negotiate manually if + * needed. + */ + //static const bool autonegotiate_compression = false; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_CORE_HPP diff --git a/websocketpp/config/debug_asio.hpp b/websocketpp/config/debug_asio.hpp new file mode 100644 index 00000000..a57c736c --- /dev/null +++ b/websocketpp/config/debug_asio.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP +#define WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP + +#include +#include +#include + +// Pull in non-tls config +#include + +// Define TLS config +namespace websocketpp { +namespace config { + +/// Client/Server debug config with asio transport and TLS enabled +struct debug_asio_tls : public debug_core { + typedef debug_asio_tls type; + typedef debug_core base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::tls_socket::endpoint socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP diff --git a/websocketpp/config/debug_asio_no_tls.hpp b/websocketpp/config/debug_asio_no_tls.hpp new file mode 100644 index 00000000..b3dc83b4 --- /dev/null +++ b/websocketpp/config/debug_asio_no_tls.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP +#define WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP + +#include +#include + +namespace websocketpp { +namespace config { + +/// Client/Server debug config with asio transport and TLS disabled +struct debug_asio : public debug_core { + typedef debug_asio type; + typedef debug_core base; + + typedef base::concurrency_type concurrency_type; + + typedef base::request_type request_type; + typedef base::response_type response_type; + + typedef base::message_type message_type; + typedef base::con_msg_manager_type con_msg_manager_type; + typedef base::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef base::alog_type alog_type; + typedef base::elog_type elog_type; + + typedef base::rng_type rng_type; + + struct transport_config : public base::transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + typedef websocketpp::transport::asio::basic_socket::endpoint + socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP diff --git a/websocketpp/config/minimal_client.hpp b/websocketpp/config/minimal_client.hpp new file mode 100644 index 00000000..72528cde --- /dev/null +++ b/websocketpp/config/minimal_client.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_MINIMAL_CLIENT_HPP +#define WEBSOCKETPP_CONFIG_MINIMAL_CLIENT_HPP + +#include + +namespace websocketpp { +namespace config { + +/// Client config with minimal dependencies +/** + * This config strips out as many dependencies as possible. It is suitable for + * use as a base class for custom configs that want to implement or choose their + * own policies for components that even the core config includes. + * + * NOTE: this config stubs out enough that it cannot be used directly. You must + * supply at least a transport policy and a cryptographically secure random + * number generation policy for a config based on `minimal_client` to do + * anything useful. + * + * Present dependency list for minimal_server config: + * + * C++98 STL: + * + * + * + * + * + * + * C++11 STL or Boost + * + * + * + * + * Operating System: + * or + * or (for ntohl.. could potentially bundle this) + * + * @since 0.4.0-dev + */ +typedef minimal_server minimal_client; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_MINIMAL_CLIENT_HPP diff --git a/websocketpp/config/minimal_server.hpp b/websocketpp/config/minimal_server.hpp new file mode 100644 index 00000000..dd1aedb9 --- /dev/null +++ b/websocketpp/config/minimal_server.hpp @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_MINIMAL_HPP +#define WEBSOCKETPP_CONFIG_MINIMAL_HPP + +// Non-Policy common stuff +#include +#include +#include + +// Concurrency +#include + +// Transport +#include + +// HTTP +#include +#include + +// Messages +#include +#include + +// Loggers +#include + +// RNG +#include + +// User stub base classes +#include +#include + +// Extensions +#include + +namespace websocketpp { +namespace config { + +/// Server config with minimal dependencies +/** + * This config strips out as many dependencies as possible. It is suitable for + * use as a base class for custom configs that want to implement or choose their + * own policies for components that even the core config includes. + * + * NOTE: this config stubs out enough that it cannot be used directly. You must + * supply at least a transport policy for a config based on `minimal_server` to + * do anything useful. + * + * Present dependency list for minimal_server config: + * + * C++98 STL: + * + * + * + * + * + * + * C++11 STL or Boost + * + * + * + * + * Operating System: + * or + * or (for ntohl.. could potentially bundle this) + * + * @since 0.4.0-dev + */ +struct minimal_server { + typedef minimal_server type; + + // Concurrency policy + typedef websocketpp::concurrency::none concurrency_type; + + // HTTP Parser Policies + typedef http::parser::request request_type; + typedef http::parser::response response_type; + + // Message Policies + typedef message_buffer::message + message_type; + typedef message_buffer::alloc::con_msg_manager + con_msg_manager_type; + typedef message_buffer::alloc::endpoint_msg_manager + endpoint_msg_manager_type; + + /// Logging policies + typedef websocketpp::log::stub elog_type; + typedef websocketpp::log::stub alog_type; + + /// RNG policies + typedef websocketpp::random::none::int_generator rng_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + struct transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::elog_type elog_type; + typedef type::alog_type alog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + + /// Controls compile time enabling/disabling of thread syncronization + /// code Disabling can provide a minor performance improvement to single + /// threaded applications + static bool const enable_multithreading = true; + + /// Default timer values (in ms) + + /// Length of time to wait for socket pre-initialization + /** + * Exactly what this includes depends on the socket policy in use + */ + static const long timeout_socket_pre_init = 5000; + + /// Length of time to wait before a proxy handshake is aborted + static const long timeout_proxy = 5000; + + /// Length of time to wait for socket post-initialization + /** + * Exactly what this includes depends on the socket policy in use. + * Often this means the TLS handshake + */ + static const long timeout_socket_post_init = 5000; + + /// Length of time to wait for dns resolution + static const long timeout_dns_resolve = 5000; + + /// Length of time to wait for TCP connect + static const long timeout_connect = 5000; + + /// Length of time to wait for socket shutdown + static const long timeout_socket_shutdown = 5000; + }; + + /// Transport Endpoint Component + typedef websocketpp::transport::stub::endpoint + transport_type; + + /// User overridable Endpoint base class + typedef websocketpp::endpoint_base endpoint_base; + /// User overridable Connection base class + typedef websocketpp::connection_base connection_base; + + /// Default timer values (in ms) + + /// Length of time before an opening handshake is aborted + static const long timeout_open_handshake = 5000; + /// Length of time before a closing handshake is aborted + static const long timeout_close_handshake = 5000; + /// Length of time to wait for a pong after a ping + static const long timeout_pong = 5000; + + /// WebSocket Protocol version to use as a client + /** + * What version of the WebSocket Protocol to use for outgoing client + * connections. Setting this to a value other than 13 (RFC6455) is not + * recommended. + */ + static const int client_version = 13; // RFC6455 + + /// Default static error logging channels + /** + * Which error logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level errors + */ + static const websocketpp::log::level elog_level = + websocketpp::log::elevel::none; + + /// Default static access logging channels + /** + * Which access logging channels to enable at compile time. Channels not + * enabled here will be unable to be selected by programs using the library. + * This option gives an optimizing compiler the ability to remove entirely + * code to test whether or not to print out log messages on a certain + * channel + * + * Default is all except for development/debug level access messages + */ + static const websocketpp::log::level alog_level = + websocketpp::log::alevel::none; + + /// + static const size_t connection_read_buffer_size = 16384; + + /// Drop connections immediately on protocol error. + /** + * Drop connections on protocol error rather than sending a close frame. + * Off by default. This may result in legit messages near the error being + * dropped as well. It may free up resources otherwise spent dealing with + * misbehaving clients. + */ + static const bool drop_on_protocol_error = false; + + /// Suppresses the return of detailed connection close information + /** + * Silence close suppresses the return of detailed connection close + * information during the closing handshake. This information is useful + * for debugging and presenting useful errors to end users but may be + * undesirable for security reasons in some production environments. + * Close reasons could be used by an attacker to confirm that the endpoint + * is out of resources or be used to identify the WebSocket implementation + * in use. + * + * Note: this will suppress *all* close codes, including those explicitly + * sent by local applications. + */ + static const bool silent_close = false; + + /// Default maximum message size + /** + * Default value for the processor's maximum message size. Maximum message size + * determines the point at which the library will fail a connection with the + * message_too_big protocol error. + * + * The default is 32MB + * + * @since 0.4.0-alpha1 + */ + static const size_t max_message_size = 32000000; + + /// Default maximum http body size + /** + * Default value for the http parser's maximum body size. Maximum body size + * determines the point at which the library will abort reading an HTTP + * connection with the 413/request entity too large error. + * + * The default is 32MB + * + * @since 0.5.0 + */ + static const size_t max_http_body_size = 32000000; + + /// Global flag for enabling/disabling extensions + static const bool enable_extensions = true; + + /// Extension specific settings: + + /// permessage_compress extension + struct permessage_deflate_config { + typedef core::request_type request_type; + + /// If the remote endpoint requests that we reset the compression + /// context after each message should we honor the request? + static const bool allow_disabling_context_takeover = true; + + /// If the remote endpoint requests that we reduce the size of the + /// LZ77 sliding window size this is the lowest value that will be + /// allowed. Values range from 8 to 15. A value of 8 means we will + /// allow any possible window size. A value of 15 means do not allow + /// negotiation of the window size (ie require the default). + static const uint8_t minimum_outgoing_window_bits = 8; + }; + + typedef websocketpp::extensions::permessage_deflate::disabled + permessage_deflate_type; + + /// Autonegotiate permessage-deflate + /** + * Automatically enables the permessage-deflate extension. + * + * For clients this results in a permessage-deflate extension request being + * sent with every request rather than requiring it to be requested manually + * + * For servers this results in accepting the first set of extension settings + * requested by the client that we understand being used. The alternative is + * requiring the extension to be manually negotiated in `validate`. With + * auto-negotiate on, you may still override the auto-negotiate manually if + * needed. + */ + //static const bool autonegotiate_compression = false; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_MINIMAL_HPP diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp new file mode 100644 index 00000000..3bbbbb31 --- /dev/null +++ b/websocketpp/connection.hpp @@ -0,0 +1,1651 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONNECTION_HPP +#define WEBSOCKETPP_CONNECTION_HPP + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace websocketpp { + +/// The type and function signature of an open handler +/** + * The open handler is called once for every successful WebSocket connection + * attempt. Either the fail handler or the open handler will be called for each + * WebSocket connection attempt. HTTP Connections that did not attempt to + * upgrade the connection to the WebSocket protocol will trigger the http + * handler instead of fail/open. + */ +typedef lib::function open_handler; + +/// The type and function signature of a close handler +/** + * The close handler is called once for every successfully established + * connection after it is no longer capable of sending or receiving new messages + * + * The close handler will be called exactly once for every connection for which + * the open handler was called. + */ +typedef lib::function close_handler; + +/// The type and function signature of a fail handler +/** + * The fail handler is called once for every unsuccessful WebSocket connection + * attempt. Either the fail handler or the open handler will be called for each + * WebSocket connection attempt. HTTP Connections that did not attempt to + * upgrade the connection to the WebSocket protocol will trigger the http + * handler instead of fail/open. + */ +typedef lib::function fail_handler; + +/// The type and function signature of an interrupt handler +/** + * The interrupt handler is called when a connection receives an interrupt + * request from the application. Interrupts allow the application to trigger a + * handler to be run in the absense of a WebSocket level handler trigger (like + * a new message). + * + * This is typically used by another application thread to schedule some tasks + * that can only be run from within the handler chain for thread safety reasons. + */ +typedef lib::function interrupt_handler; + +/// The type and function signature of a ping handler +/** + * The ping handler is called when the connection receives a WebSocket ping + * control frame. The string argument contains the ping payload. The payload is + * a binary string up to 126 bytes in length. The ping handler returns a bool, + * true if a pong response should be sent, false if the pong response should be + * suppressed. + */ +typedef lib::function ping_handler; + +/// The type and function signature of a pong handler +/** + * The pong handler is called when the connection receives a WebSocket pong + * control frame. The string argument contains the pong payload. The payload is + * a binary string up to 126 bytes in length. + */ +typedef lib::function pong_handler; + +/// The type and function signature of a pong timeout handler +/** + * The pong timeout handler is called when a ping goes unanswered by a pong for + * longer than the locally specified timeout period. + */ +typedef lib::function pong_timeout_handler; + +/// The type and function signature of a validate handler +/** + * The validate handler is called after a WebSocket handshake has been received + * and processed but before it has been accepted. This gives the application a + * chance to implement connection details specific policies for accepting + * connections and the ability to negotiate extensions and subprotocols. + * + * The validate handler return value indicates whether or not the connection + * should be accepted. Additional methods may be called during the function to + * set response headers, set HTTP return/error codes, etc. + */ +typedef lib::function validate_handler; + +/// The type and function signature of a http handler +/** + * The http handler is called when an HTTP connection is made that does not + * attempt to upgrade the connection to the WebSocket protocol. This allows + * WebSocket++ servers to respond to these requests with regular HTTP responses. + * + * This can be used to deliver error pages & dashboards and to deliver static + * files such as the base HTML & JavaScript for an otherwise single page + * WebSocket application. + * + * Note: WebSocket++ is designed to be a high performance WebSocket server. It + * is not tuned to provide a full featured, high performance, HTTP web server + * solution. The HTTP handler is appropriate only for low volume HTTP traffic. + * If you expect to serve high volumes of HTTP traffic a dedicated HTTP web + * server is strongly recommended. + * + * The default HTTP handler will return a 426 Upgrade Required error. Custom + * handlers may override the response status code to deliver any type of + * response. + */ +typedef lib::function http_handler; + +// +typedef lib::function read_handler; +typedef lib::function write_frame_handler; + +// constants related to the default WebSocket protocol versions available +#ifdef _WEBSOCKETPP_INITIALIZER_LISTS_ // simplified C++11 version + /// Container that stores the list of protocol versions supported + /** + * @todo Move this to configs to allow compile/runtime disabling or enabling + * of protocol versions + */ + static std::vector const versions_supported = {0,7,8,13}; +#else + /// Helper array to get around lack of initializer lists pre C++11 + static int const helper[] = {0,7,8,13}; + /// Container that stores the list of protocol versions supported + /** + * @todo Move this to configs to allow compile/runtime disabling or enabling + * of protocol versions + */ + static std::vector const versions_supported(helper,helper+4); +#endif + +namespace session { +namespace state { + // externally visible session state (states based on the RFC) + enum value { + connecting = 0, + open = 1, + closing = 2, + closed = 3 + }; +} // namespace state + + +namespace fail { +namespace status { + enum value { + GOOD = 0, // no failure yet! + SYSTEM = 1, // system call returned error, check that code + WEBSOCKET = 2, // websocket close codes contain error + UNKNOWN = 3, // No failure information is available + TIMEOUT_TLS = 4, // TLS handshake timed out + TIMEOUT_WS = 5 // WS handshake timed out + }; +} // namespace status +} // namespace fail + +namespace internal_state { + // More granular internal states. These are used for multi-threaded + // connection synchronization and preventing values that are not yet or no + // longer available from being used. + + enum value { + USER_INIT = 0, + TRANSPORT_INIT = 1, + READ_HTTP_REQUEST = 2, + WRITE_HTTP_REQUEST = 3, + READ_HTTP_RESPONSE = 4, + WRITE_HTTP_RESPONSE = 5, + PROCESS_HTTP_REQUEST = 6, + PROCESS_CONNECTION = 7 + }; +} // namespace internal_state + + +namespace http_state { + // states to keep track of the progress of http connections + + enum value { + init = 0, + deferred = 1, + headers_written = 2, + body_written = 3, + closed = 4 + }; +} // namespace http_state + +} // namespace session + +/// Represents an individual WebSocket connection +template +class connection + : public config::transport_type::transport_con_type + , public config::connection_base +{ +public: + /// Type of this connection + typedef connection type; + /// Type of a shared pointer to this connection + typedef lib::shared_ptr ptr; + /// Type of a weak pointer to this connection + typedef lib::weak_ptr weak_ptr; + + /// Type of the concurrency component of this connection + typedef typename config::concurrency_type concurrency_type; + /// Type of the access logging policy + typedef typename config::alog_type alog_type; + /// Type of the error logging policy + typedef typename config::elog_type elog_type; + + /// Type of the transport component of this connection + typedef typename config::transport_type::transport_con_type + transport_con_type; + /// Type of a shared pointer to the transport component of this connection + typedef typename transport_con_type::ptr transport_con_ptr; + + typedef lib::function termination_handler; + + typedef typename concurrency_type::scoped_lock_type scoped_lock_type; + typedef typename concurrency_type::mutex_type mutex_type; + + typedef typename config::request_type request_type; + typedef typename config::response_type response_type; + + typedef typename config::message_type message_type; + typedef typename message_type::ptr message_ptr; + + typedef typename config::con_msg_manager_type con_msg_manager_type; + typedef typename con_msg_manager_type::ptr con_msg_manager_ptr; + + /// Type of RNG + typedef typename config::rng_type rng_type; + + typedef processor::processor processor_type; + typedef lib::shared_ptr processor_ptr; + + // Message handler (needs to know message type) + typedef lib::function message_handler; + + /// Type of a pointer to a transport timer handle + typedef typename transport_con_type::timer_ptr timer_ptr; + + // Misc Convenience Types + typedef session::internal_state::value istate_type; + +private: + enum terminate_status { + failed = 1, + closed, + unknown + }; +public: + + explicit connection(bool p_is_server, std::string const & ua, alog_type& alog, + elog_type& elog, rng_type & rng) + : transport_con_type(p_is_server, alog, elog) + , m_handle_read_frame(lib::bind( + &type::handle_read_frame, + this, + lib::placeholders::_1, + lib::placeholders::_2 + )) + , m_write_frame_handler(lib::bind( + &type::handle_write_frame, + this, + lib::placeholders::_1 + )) + , m_user_agent(ua) + , m_open_handshake_timeout_dur(config::timeout_open_handshake) + , m_close_handshake_timeout_dur(config::timeout_close_handshake) + , m_pong_timeout_dur(config::timeout_pong) + , m_max_message_size(config::max_message_size) + , m_state(session::state::connecting) + , m_internal_state(session::internal_state::USER_INIT) + , m_msg_manager(new con_msg_manager_type()) + , m_send_buffer_size(0) + , m_write_flag(false) + , m_read_flag(true) + , m_is_server(p_is_server) + , m_alog(alog) + , m_elog(elog) + , m_rng(rng) + , m_local_close_code(close::status::abnormal_close) + , m_remote_close_code(close::status::abnormal_close) + , m_is_http(false) + , m_http_state(session::http_state::init) + , m_was_clean(false) + { + m_alog.write(log::alevel::devel,"connection constructor"); + } + + /// Get a shared pointer to this component + ptr get_shared() { + return lib::static_pointer_cast(transport_con_type::get_shared()); + } + + /////////////////////////// + // Set Handler Callbacks // + /////////////////////////// + + /// Set open handler + /** + * The open handler is called after the WebSocket handshake is complete and + * the connection is considered OPEN. + * + * @param h The new open_handler + */ + void set_open_handler(open_handler h) { + m_open_handler = h; + } + + /// Set close handler + /** + * The close handler is called immediately after the connection is closed. + * + * @param h The new close_handler + */ + void set_close_handler(close_handler h) { + m_close_handler = h; + } + + /// Set fail handler + /** + * The fail handler is called whenever the connection fails while the + * handshake is bring processed. + * + * @param h The new fail_handler + */ + void set_fail_handler(fail_handler h) { + m_fail_handler = h; + } + + /// Set ping handler + /** + * The ping handler is called whenever the connection receives a ping + * control frame. The ping payload is included. + * + * The ping handler's return time controls whether or not a pong is + * sent in response to this ping. Returning false will suppress the + * return pong. If no ping handler is set a pong will be sent. + * + * @param h The new ping_handler + */ + void set_ping_handler(ping_handler h) { + m_ping_handler = h; + } + + /// Set pong handler + /** + * The pong handler is called whenever the connection receives a pong + * control frame. The pong payload is included. + * + * @param h The new pong_handler + */ + void set_pong_handler(pong_handler h) { + m_pong_handler = h; + } + + /// Set pong timeout handler + /** + * If the transport component being used supports timers, the pong timeout + * handler is called whenever a pong control frame is not received with the + * configured timeout period after the application sends a ping. + * + * The config setting `timeout_pong` controls the length of the timeout + * period. It is specified in milliseconds. + * + * This can be used to probe the health of the remote endpoint's WebSocket + * implementation. This does not guarantee that the remote application + * itself is still healthy but can be a useful diagnostic. + * + * Note: receipt of this callback doesn't mean the pong will never come. + * This functionality will not suppress delivery of the pong in question + * should it arrive after the timeout. + * + * @param h The new pong_timeout_handler + */ + void set_pong_timeout_handler(pong_timeout_handler h) { + m_pong_timeout_handler = h; + } + + /// Set interrupt handler + /** + * The interrupt handler is called whenever the connection is manually + * interrupted by the application. + * + * @param h The new interrupt_handler + */ + void set_interrupt_handler(interrupt_handler h) { + m_interrupt_handler = h; + } + + /// Set http handler + /** + * The http handler is called after an HTTP request other than a WebSocket + * upgrade request is received. It allows a WebSocket++ server to respond + * to regular HTTP requests on the same port as it processes WebSocket + * connections. This can be useful for hosting error messages, flash + * policy files, status pages, and other simple HTTP responses. It is not + * intended to be used as a primary web server. + * + * @param h The new http_handler + */ + void set_http_handler(http_handler h) { + m_http_handler = h; + } + + /// Set validate handler + /** + * The validate handler is called after a WebSocket handshake has been + * parsed but before a response is returned. It provides the application + * a chance to examine the request and determine whether or not it wants + * to accept the connection. + * + * Returning false from the validate handler will reject the connection. + * If no validate handler is present, all connections will be allowed. + * + * @param h The new validate_handler + */ + void set_validate_handler(validate_handler h) { + m_validate_handler = h; + } + + /// Set message handler + /** + * The message handler is called after a new message has been received. + * + * @param h The new message_handler + */ + void set_message_handler(message_handler h) { + m_message_handler = h; + } + + ////////////////////////////////////////// + // Connection timeouts and other limits // + ////////////////////////////////////////// + + /// Set open handshake timeout + /** + * Sets the length of time the library will wait after an opening handshake + * has been initiated before cancelling it. This can be used to prevent + * excessive wait times for outgoing clients or excessive resource usage + * from broken clients or DoS attacks on servers. + * + * Connections that time out will have their fail handlers called with the + * open_handshake_timeout error code. + * + * The default value is specified via the compile time config value + * 'timeout_open_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the open handshake timeout in ms + */ + void set_open_handshake_timeout(long dur) { + m_open_handshake_timeout_dur = dur; + } + + /// Set close handshake timeout + /** + * Sets the length of time the library will wait after a closing handshake + * has been initiated before cancelling it. This can be used to prevent + * excessive wait times for outgoing clients or excessive resource usage + * from broken clients or DoS attacks on servers. + * + * Connections that time out will have their close handlers called with the + * close_handshake_timeout error code. + * + * The default value is specified via the compile time config value + * 'timeout_close_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the close handshake timeout in ms + */ + void set_close_handshake_timeout(long dur) { + m_close_handshake_timeout_dur = dur; + } + + /// Set pong timeout + /** + * Sets the length of time the library will wait for a pong response to a + * ping. This can be used as a keepalive or to detect broken connections. + * + * Pong responses that time out will have the pong timeout handler called. + * + * The default value is specified via the compile time config value + * 'timeout_pong'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the pong timeout in ms + */ + void set_pong_timeout(long dur) { + m_pong_timeout_dur = dur; + } + + /// Get maximum message size + /** + * Get maximum message size. Maximum message size determines the point at + * which the connection will fail with the message_too_big protocol error. + * + * The default is set by the endpoint that creates the connection. + * + * @since 0.3.0 + */ + size_t get_max_message_size() const { + return m_max_message_size; + } + + /// Set maximum message size + /** + * Set maximum message size. Maximum message size determines the point at + * which the connection will fail with the message_too_big protocol error. + * This value may be changed during the connection. + * + * The default is set by the endpoint that creates the connection. + * + * @since 0.3.0 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_message_size(size_t new_value) { + m_max_message_size = new_value; + if (m_processor) { + m_processor->set_max_message_size(new_value); + } + } + + /// Get maximum HTTP message body size + /** + * Get maximum HTTP message body size. Maximum message body size determines + * the point at which the connection will stop reading an HTTP request whose + * body is too large. + * + * The default is set by the endpoint that creates the connection. + * + * @since 0.5.0 + * + * @return The maximum HTTP message body size + */ + size_t get_max_http_body_size() const { + return m_request.get_max_body_size(); + } + + /// Set maximum HTTP message body size + /** + * Set maximum HTTP message body size. Maximum message body size determines + * the point at which the connection will stop reading an HTTP request whose + * body is too large. + * + * The default is set by the endpoint that creates the connection. + * + * @since 0.5.0 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_http_body_size(size_t new_value) { + m_request.set_max_body_size(new_value); + } + + ////////////////////////////////// + // Uncategorized public methods // + ////////////////////////////////// + + /// Get the size of the outgoing write buffer (in payload bytes) + /** + * Retrieves the number of bytes in the outgoing write buffer that have not + * already been dispatched to the transport layer. This represents the bytes + * that are presently cancelable without uncleanly ending the websocket + * connection + * + * This method invokes the m_write_lock mutex + * + * @return The current number of bytes in the outgoing send buffer. + */ + size_t get_buffered_amount() const; + + /// Get the size of the outgoing write buffer (in payload bytes) + /** + * @deprecated use `get_buffered_amount` instead + */ + size_t buffered_amount() const { + return get_buffered_amount(); + } + + //////////////////// + // Action Methods // + //////////////////// + + /// Create a message and then add it to the outgoing send queue + /** + * Convenience method to send a message given a payload string and + * optionally an opcode. Default opcode is utf8 text. + * + * This method locks the m_write_lock mutex + * + * @param payload The payload string to generated the message with + * + * @param op The opcode to generated the message with. Default is + * frame::opcode::text + */ + lib::error_code send(std::string const & payload, frame::opcode::value op = + frame::opcode::text); + + /// Send a message (raw array overload) + /** + * Convenience method to send a message given a raw array and optionally an + * opcode. Default opcode is binary. + * + * This method locks the m_write_lock mutex + * + * @param payload A pointer to the array containing the bytes to send. + * + * @param len Length of the array. + * + * @param op The opcode to generated the message with. Default is + * frame::opcode::binary + */ + lib::error_code send(void const * payload, size_t len, frame::opcode::value + op = frame::opcode::binary); + + /// Add a message to the outgoing send queue + /** + * If presented with a prepared message it is added without validation or + * framing. If presented with an unprepared message it is validated, framed, + * and then added + * + * Errors are returned via an exception + * \todo make exception system_error rather than error_code + * + * This method invokes the m_write_lock mutex + * + * @param msg A message_ptr to the message to send. + */ + lib::error_code send(message_ptr msg); + + /// Asyncronously invoke handler::on_inturrupt + /** + * Signals to the connection to asyncronously invoke the on_inturrupt + * callback for this connection's handler once it is safe to do so. + * + * When the on_inturrupt handler callback is called it will be from + * within the transport event loop with all the thread safety features + * guaranteed by the transport to regular handlers + * + * Multiple inturrupt signals can be active at once on the same connection + * + * @return An error code + */ + lib::error_code interrupt(); + + /// Transport inturrupt callback + void handle_interrupt(); + + /// Pause reading of new data + /** + * Signals to the connection to halt reading of new data. While reading is paused, + * the connection will stop reading from its associated socket. In turn this will + * result in TCP based flow control kicking in and slowing data flow from the remote + * endpoint. + * + * This is useful for applications that push new requests to a queue to be processed + * by another thread and need a way to signal when their request queue is full without + * blocking the network processing thread. + * + * Use `resume_reading()` to resume. + * + * If supported by the transport this is done asynchronously. As such reading may not + * stop until the current read operation completes. Typically you can expect to + * receive no more bytes after initiating a read pause than the size of the read + * buffer. + * + * If reading is paused for this connection already nothing is changed. + */ + lib::error_code pause_reading(); + + /// Pause reading callback + void handle_pause_reading(); + + /// Resume reading of new data + /** + * Signals to the connection to resume reading of new data after it was paused by + * `pause_reading()`. + * + * If reading is not paused for this connection already nothing is changed. + */ + lib::error_code resume_reading(); + + /// Resume reading callback + void handle_resume_reading(); + + /// Send a ping + /** + * Initiates a ping with the given payload/ + * + * There is no feedback directly from ping except in cases of immediately + * detectable errors. Feedback will be provided via on_pong or + * on_pong_timeout callbacks. + * + * Ping locks the m_write_lock mutex + * + * @param payload Payload to be used for the ping + */ + void ping(std::string const & payload); + + /// exception free variant of ping + void ping(std::string const & payload, lib::error_code & ec); + + /// Utility method that gets called back when the ping timer expires + void handle_pong_timeout(std::string payload, lib::error_code const & ec); + + /// Send a pong + /** + * Initiates a pong with the given payload. + * + * There is no feedback from a pong once sent. + * + * Pong locks the m_write_lock mutex + * + * @param payload Payload to be used for the pong + */ + void pong(std::string const & payload); + + /// exception free variant of pong + void pong(std::string const & payload, lib::error_code & ec); + + /// Close the connection + /** + * Initiates the close handshake process. + * + * If close returns successfully the connection will be in the closing + * state and no additional messages may be sent. All messages sent prior + * to calling close will be written out before the connection is closed. + * + * If no reason is specified none will be sent. If no code is specified + * then no code will be sent. + * + * The handler's on_close callback will be called once the close handshake + * is complete. + * + * Reasons will be automatically truncated to the maximum length (123 bytes) + * if necessary. + * + * @param code The close code to send + * @param reason The close reason to send + */ + void close(close::status::value const code, std::string const & reason); + + /// exception free variant of close + void close(close::status::value const code, std::string const & reason, + lib::error_code & ec); + + //////////////////////////////////////////////// + // Pass-through access to the uri information // + //////////////////////////////////////////////// + + /// Returns the secure flag from the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return Whether or not the connection URI is flagged secure. + */ + bool get_secure() const; + + /// Returns the host component of the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The host component of the connection URI + */ + std::string const & get_host() const; + + /// Returns the resource component of the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The resource component of the connection URI + */ + std::string const & get_resource() const; + + /// Returns the port component of the connection URI + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The port component of the connection URI + */ + uint16_t get_port() const; + + /// Gets the connection URI + /** + * This should really only be called by internal library methods unless you + * really know what you are doing. + * + * @return A pointer to the connection's URI + */ + uri_ptr get_uri() const; + + /// Sets the connection URI + /** + * This should really only be called by internal library methods unless you + * really know what you are doing. + * + * @param uri The new URI to set + */ + void set_uri(uri_ptr uri); + + ///////////////////////////// + // Subprotocol negotiation // + ///////////////////////////// + + /// Gets the negotated subprotocol + /** + * Retrieves the subprotocol that was negotiated during the handshake. This + * method is valid in the open handler and later. + * + * @return The negotiated subprotocol + */ + std::string const & get_subprotocol() const; + + /// Gets all of the subprotocols requested by the client + /** + * Retrieves the subprotocols that were requested during the handshake. This + * method is valid in the validate handler and later. + * + * @return A vector of the requested subprotocol + */ + std::vector const & get_requested_subprotocols() const; + + /// Adds the given subprotocol string to the request list (exception free) + /** + * Adds a subprotocol to the list to send with the opening handshake. This + * may be called multiple times to request more than one. If the server + * supports one of these, it may choose one. If so, it will return it + * in it's handshake reponse and the value will be available via + * get_subprotocol(). Subprotocol requests should be added in order of + * preference. + * + * @param request The subprotocol to request + * @param ec A reference to an error code that will be filled in the case of + * errors + */ + void add_subprotocol(std::string const & request, lib::error_code & ec); + + /// Adds the given subprotocol string to the request list + /** + * Adds a subprotocol to the list to send with the opening handshake. This + * may be called multiple times to request more than one. If the server + * supports one of these, it may choose one. If so, it will return it + * in it's handshake reponse and the value will be available via + * get_subprotocol(). Subprotocol requests should be added in order of + * preference. + * + * @param request The subprotocol to request + */ + void add_subprotocol(std::string const & request); + + /// Select a subprotocol to use (exception free) + /** + * Indicates which subprotocol should be used for this connection. Valid + * only during the validate handler callback. Subprotocol selected must have + * been requested by the client. Consult get_requested_subprotocols() for a + * list of valid subprotocols. + * + * This member function is valid on server endpoints/connections only + * + * @param value The subprotocol to select + * @param ec A reference to an error code that will be filled in the case of + * errors + */ + void select_subprotocol(std::string const & value, lib::error_code & ec); + + /// Select a subprotocol to use + /** + * Indicates which subprotocol should be used for this connection. Valid + * only during the validate handler callback. Subprotocol selected must have + * been requested by the client. Consult get_requested_subprotocols() for a + * list of valid subprotocols. + * + * This member function is valid on server endpoints/connections only + * + * @param value The subprotocol to select + */ + void select_subprotocol(std::string const & value); + + ///////////////////////////////////////////////////////////// + // Pass-through access to the request and response objects // + ///////////////////////////////////////////////////////////// + + /// Retrieve a request header + /** + * Retrieve the value of a header from the handshake HTTP request. + * + * @param key Name of the header to get + * @return The value of the header + */ + std::string const & get_request_header(std::string const & key) const; + + /// Retrieve a request body + /** + * Retrieve the value of the request body. This value is typically used with + * PUT and POST requests to upload files or other data. Only HTTP + * connections will ever have bodies. WebSocket connection's will always + * have blank bodies. + * + * @return The value of the request body. + */ + std::string const & get_request_body() const; + + /// Retrieve a response header + /** + * Retrieve the value of a header from the handshake HTTP request. + * + * @param key Name of the header to get + * @return The value of the header + */ + std::string const & get_response_header(std::string const & key) const; + + /// Get response HTTP status code + /** + * Gets the response status code + * + * @since 0.7.0 + * + * @return The response status code sent + */ + http::status_code::value get_response_code() const { + return m_response.get_status_code(); + } + + /// Get response HTTP status message + /** + * Gets the response status message + * + * @since 0.7.0 + * + * @return The response status message sent + */ + std::string const & get_response_msg() const { + return m_response.get_status_msg(); + } + + /// Set response status code and message + /** + * Sets the response status code to `code` and looks up the corresponding + * message for standard codes. Non-standard codes will be entered as Unknown + * use set_status(status_code::value,std::string) overload to set both + * values explicitly. + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param code Code to set + * @param msg Message to set + * @see websocketpp::http::response::set_status + */ + void set_status(http::status_code::value code); + + /// Set response status code and message + /** + * Sets the response status code and message to independent custom values. + * use set_status(status_code::value) to set the code and have the standard + * message be automatically set. + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param code Code to set + * @param msg Message to set + * @see websocketpp::http::response::set_status + */ + void set_status(http::status_code::value code, std::string const & msg); + + /// Set response body content + /** + * Set the body content of the HTTP response to the parameter string. Note + * set_body will also set the Content-Length HTTP header to the appropriate + * value. If you want the Content-Length header to be something else set it + * to something else after calling set_body + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param value String data to include as the body content. + * @see websocketpp::http::response::set_body + */ + void set_body(std::string const & value); + + /// Append a header + /** + * If a header with this name already exists the value will be appended to + * the existing header to form a comma separated list of values. Use + * `connection::replace_header` to overwrite existing values. + * + * This member function is valid only from the http() and validate() handler + * callbacks, or to a client connection before connect has been called. + * + * @param key Name of the header to set + * @param val Value to add + * @see replace_header + * @see websocketpp::http::parser::append_header + */ + void append_header(std::string const & key, std::string const & val); + + /// Replace a header + /** + * If a header with this name already exists the old value will be replaced + * Use `connection::append_header` to append to a list of existing values. + * + * This member function is valid only from the http() and validate() handler + * callbacks, or to a client connection before connect has been called. + * + * @param key Name of the header to set + * @param val Value to set + * @see append_header + * @see websocketpp::http::parser::replace_header + */ + void replace_header(std::string const & key, std::string const & val); + + /// Remove a header + /** + * Removes a header from the response. + * + * This member function is valid only from the http() and validate() handler + * callbacks, or to a client connection before connect has been called. + * + * @param key The name of the header to remove + * @see websocketpp::http::parser::remove_header + */ + void remove_header(std::string const & key); + + /// Get request object + /** + * Direct access to request object. This can be used to call methods of the + * request object that are not part of the standard request API that + * connection wraps. + * + * Note use of this method involves using behavior specific to the + * configured HTTP policy. Such behavior may not work with alternate HTTP + * policies. + * + * @since 0.3.0-alpha3 + * + * @return A const reference to the raw request object + */ + request_type const & get_request() const { + return m_request; + } + + /// Get response object + /** + * Direct access to the HTTP response sent or received as a part of the + * opening handshake. This can be used to call methods of the response + * object that are not part of the standard request API that connection + * wraps. + * + * Note use of this method involves using behavior specific to the + * configured HTTP policy. Such behavior may not work with alternate HTTP + * policies. + * + * @since 0.7.0 + * + * @return A const reference to the raw response object + */ + response_type const & get_response() const { + return m_response; + } + + /// Defer HTTP Response until later (Exception free) + /** + * Used in the http handler to defer the HTTP response for this connection + * until later. Handshake timers will be canceled and the connection will be + * left open until `send_http_response` or an equivalent is called. + * + * Warning: deferred connections won't time out and as a result can tie up + * resources. + * + * @since 0.6.0 + * + * @return A status code, zero on success, non-zero otherwise + */ + lib::error_code defer_http_response(); + + /// Send deferred HTTP Response (exception free) + /** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * @since 0.6.0 + * + * @param ec A status code, zero on success, non-zero otherwise + */ + void send_http_response(lib::error_code & ec); + + /// Send deferred HTTP Response + void send_http_response(); + + // TODO HTTPNBIO: write_headers + // function that processes headers + status so far and writes it to the wire + // beginning the HTTP response body state. This method will ignore anything + // in the response body. + + // TODO HTTPNBIO: write_body_message + // queues the specified message_buffer for async writing + + // TODO HTTPNBIO: finish connection + // + + // TODO HTTPNBIO: write_response + // Writes the whole response, headers + body and closes the connection + + + + ///////////////////////////////////////////////////////////// + // Pass-through access to the other connection information // + ///////////////////////////////////////////////////////////// + + /// Get Connection Handle + /** + * The connection handle is a token that can be shared outside the + * WebSocket++ core for the purposes of identifying a connection and + * sending it messages. + * + * @return A handle to the connection + */ + connection_hdl get_handle() const { + return m_connection_hdl; + } + + /// Get whether or not this connection is part of a server or client + /** + * @return whether or not the connection is attached to a server endpoint + */ + bool is_server() const { + return m_is_server; + } + + /// Return the same origin policy origin value from the opening request. + /** + * This value is available after the HTTP request has been fully read and + * may be called from any thread. + * + * @return The connection's origin value from the opening handshake. + */ + std::string const & get_origin() const; + + /// Return the connection state. + /** + * Values can be connecting, open, closing, and closed + * + * @return The connection's current state. + */ + session::state::value get_state() const; + + + /// Get the WebSocket close code sent by this endpoint. + /** + * @return The WebSocket close code sent by this endpoint. + */ + close::status::value get_local_close_code() const { + return m_local_close_code; + } + + /// Get the WebSocket close reason sent by this endpoint. + /** + * @return The WebSocket close reason sent by this endpoint. + */ + std::string const & get_local_close_reason() const { + return m_local_close_reason; + } + + /// Get the WebSocket close code sent by the remote endpoint. + /** + * @return The WebSocket close code sent by the remote endpoint. + */ + close::status::value get_remote_close_code() const { + return m_remote_close_code; + } + + /// Get the WebSocket close reason sent by the remote endpoint. + /** + * @return The WebSocket close reason sent by the remote endpoint. + */ + std::string const & get_remote_close_reason() const { + return m_remote_close_reason; + } + + /// Get the internal error code for a closed/failed connection + /** + * Retrieves a machine readable detailed error code indicating the reason + * that the connection was closed or failed. Valid only after the close or + * fail handler is called. + * + * @return Error code indicating the reason the connection was closed or + * failed + */ + lib::error_code get_ec() const { + return m_ec; + } + + /// Get a message buffer + /** + * Warning: The API related to directly sending message buffers may change + * before the 1.0 release. If you plan to use it, please keep an eye on any + * breaking changes notifications in future release notes. Also if you have + * any feedback about usage and capabilities now is a great time to provide + * it. + * + * Message buffers are used to store message payloads and other message + * metadata. + * + * The size parameter is a hint only. Your final payload does not need to + * match it. There may be some performance benefits if the initial size + * guess is equal to or slightly higher than the final payload size. + * + * @param op The opcode for the new message + * @param size A hint to optimize the initial allocation of payload space. + * @return A new message buffer + */ + message_ptr get_message(websocketpp::frame::opcode::value op, size_t size) + const + { + return m_msg_manager->get_message(op, size); + } + + //////////////////////////////////////////////////////////////////////// + // The remaining public member functions are for internal/policy use // + // only. Do not call from application code unless you understand what // + // you are doing. // + //////////////////////////////////////////////////////////////////////// + + + + void read_handshake(size_t num_bytes); + + void handle_read_handshake(lib::error_code const & ec, + size_t bytes_transferred); + void handle_read_http_response(lib::error_code const & ec, + size_t bytes_transferred); + + + void handle_write_http_response(lib::error_code const & ec); + void handle_send_http_request(lib::error_code const & ec); + + void handle_open_handshake_timeout(lib::error_code const & ec); + void handle_close_handshake_timeout(lib::error_code const & ec); + + void handle_read_frame(lib::error_code const & ec, size_t bytes_transferred); + void read_frame(); + + /// Get array of WebSocket protocol versions that this connection supports. + std::vector const & get_supported_versions() const; + + /// Sets the handler for a terminating connection. Should only be used + /// internally by the endpoint class. + void set_termination_handler(termination_handler new_handler); + + void terminate(lib::error_code const & ec); + void handle_terminate(terminate_status tstat, lib::error_code const & ec); + + /// Checks if there are frames in the send queue and if there are sends one + /** + * \todo unit tests + * + * This method locks the m_write_lock mutex + */ + void write_frame(); + + /// Process the results of a frame write operation and start the next write + /** + * \todo unit tests + * + * This method locks the m_write_lock mutex + * + * @param terminate Whether or not to terminate the connection upon + * completion of this write. + * + * @param ec A status code from the transport layer, zero on success, + * non-zero otherwise. + */ + void handle_write_frame(lib::error_code const & ec); +// protected: + // This set of methods would really like to be protected, but doing so + // requires that the endpoint be able to friend the connection. This is + // allowed with C++11, but not prior versions + + /// Start the connection state machine + void start(); + + /// Set Connection Handle + /** + * The connection handle is a token that can be shared outside the + * WebSocket++ core for the purposes of identifying a connection and + * sending it messages. + * + * @param hdl A connection_hdl that the connection will use to refer + * to itself. + */ + void set_handle(connection_hdl hdl) { + m_connection_hdl = hdl; + transport_con_type::set_handle(hdl); + } +protected: + void handle_transport_init(lib::error_code const & ec); + + /// Set m_processor based on information in m_request. Set m_response + /// status and return an error code indicating status. + lib::error_code initialize_processor(); + + /// Perform WebSocket handshake validation of m_request using m_processor. + /// set m_response and return an error code indicating status. + lib::error_code process_handshake_request(); +private: + + + /// Completes m_response, serializes it, and sends it out on the wire. + void write_http_response(lib::error_code const & ec); + + /// Sends an opening WebSocket connect request + void send_http_request(); + + /// Alternate path for write_http_response in error conditions + void write_http_response_error(lib::error_code const & ec); + + /// Process control message + /** + * + */ + void process_control_frame(message_ptr msg); + + /// Send close acknowledgement + /** + * If no arguments are present no close code/reason will be specified. + * + * Note: the close code/reason values provided here may be overrided by + * other settings (such as silent close). + * + * @param code The close code to send + * @param reason The close reason to send + * @return A status code, zero on success, non-zero otherwise + */ + lib::error_code send_close_ack(close::status::value code = + close::status::blank, std::string const & reason = std::string()); + + /// Send close frame + /** + * If no arguments are present no close code/reason will be specified. + * + * Note: the close code/reason values provided here may be overrided by + * other settings (such as silent close). + * + * The ack flag determines what to do in the case of a blank status and + * whether or not to terminate the TCP connection after sending it. + * + * @param code The close code to send + * @param reason The close reason to send + * @param ack Whether or not this is an acknowledgement close frame + * @return A status code, zero on success, non-zero otherwise + */ + lib::error_code send_close_frame(close::status::value code = + close::status::blank, std::string const & reason = std::string(), bool ack = false, + bool terminal = false); + + /// Get a pointer to a new WebSocket protocol processor for a given version + /** + * @param version Version number of the WebSocket protocol to get a + * processor for. Negative values indicate invalid/unknown versions and will + * always return a null ptr + * + * @return A shared_ptr to a new instance of the appropriate processor or a + * null ptr if there is no installed processor that matches the version + * number. + */ + processor_ptr get_processor(int version) const; + + /// Add a message to the write queue + /** + * Adds a message to the write queue and updates any associated shared state + * + * Must be called while holding m_write_lock + * + * @todo unit tests + * + * @param msg The message to push + */ + void write_push(message_ptr msg); + + /// Pop a message from the write queue + /** + * Removes and returns a message from the write queue and updates any + * associated shared state. + * + * Must be called while holding m_write_lock + * + * @todo unit tests + * + * @return the message_ptr at the front of the queue + */ + message_ptr write_pop(); + + /// Prints information about the incoming connection to the access log + /** + * Prints information about the incoming connection to the access log. + * Includes: connection type, websocket version, remote endpoint, user agent + * path, status code. + */ + void log_open_result(); + + /// Prints information about a connection being closed to the access log + /** + * Includes: local and remote close codes and reasons + */ + void log_close_result(); + + /// Prints information about a connection being failed to the access log + /** + * Includes: error code and message for why it was failed + */ + void log_fail_result(); + + /// Prints information about HTTP connections + /** + * Includes: TODO + */ + void log_http_result(); + + /// Prints information about an arbitrary error code on the specified channel + template + void log_err(log::level l, char const * msg, error_type const & ec) { + std::stringstream s; + s << msg << " error: " << ec << " (" << ec.message() << ")"; + m_elog.write(l, s.str()); + } + + // internal handler functions + read_handler m_handle_read_frame; + write_frame_handler m_write_frame_handler; + + // static settings + std::string const m_user_agent; + + /// Pointer to the connection handle + connection_hdl m_connection_hdl; + + /// Handler objects + open_handler m_open_handler; + close_handler m_close_handler; + fail_handler m_fail_handler; + ping_handler m_ping_handler; + pong_handler m_pong_handler; + pong_timeout_handler m_pong_timeout_handler; + interrupt_handler m_interrupt_handler; + http_handler m_http_handler; + validate_handler m_validate_handler; + message_handler m_message_handler; + + /// constant values + long m_open_handshake_timeout_dur; + long m_close_handshake_timeout_dur; + long m_pong_timeout_dur; + size_t m_max_message_size; + + /// External connection state + /** + * Lock: m_connection_state_lock + */ + session::state::value m_state; + + /// Internal connection state + /** + * Lock: m_connection_state_lock + */ + istate_type m_internal_state; + + mutable mutex_type m_connection_state_lock; + + /// The lock used to protect the message queue + /** + * Serializes access to the write queue as well as shared state within the + * processor. + */ + mutex_type m_write_lock; + + // connection resources + char m_buf[config::connection_read_buffer_size]; + size_t m_buf_cursor; + termination_handler m_termination_handler; + con_msg_manager_ptr m_msg_manager; + timer_ptr m_handshake_timer; + timer_ptr m_ping_timer; + + /// @todo this is not memory efficient. this value is not used after the + /// handshake. + std::string m_handshake_buffer; + + /// Pointer to the processor object for this connection + /** + * The processor provides functionality that is specific to the WebSocket + * protocol version that the client has negotiated. It also contains all of + * the state necessary to encode and decode the incoming and outgoing + * WebSocket byte streams + * + * Use of the prepare_data_frame method requires lock: m_write_lock + */ + processor_ptr m_processor; + + /// Queue of unsent outgoing messages + /** + * Lock: m_write_lock + */ + std::queue m_send_queue; + + /// Size in bytes of the outstanding payloads in the write queue + /** + * Lock: m_write_lock + */ + size_t m_send_buffer_size; + + /// buffer holding the various parts of the current message being writen + /** + * Lock m_write_lock + */ + std::vector m_send_buffer; + + /// a list of pointers to hold on to the messages being written to keep them + /// from going out of scope before the write is complete. + std::vector m_current_msgs; + + /// True if there is currently an outstanding transport write + /** + * Lock m_write_lock + */ + bool m_write_flag; + + /// True if this connection is presently reading new data + bool m_read_flag; + + // connection data + request_type m_request; + response_type m_response; + uri_ptr m_uri; + std::string m_subprotocol; + + // connection data that might not be necessary to keep around for the life + // of the whole connection. + std::vector m_requested_subprotocols; + + bool const m_is_server; + alog_type& m_alog; + elog_type& m_elog; + + rng_type & m_rng; + + // Close state + /// Close code that was sent on the wire by this endpoint + close::status::value m_local_close_code; + + /// Close reason that was sent on the wire by this endpoint + std::string m_local_close_reason; + + /// Close code that was received on the wire from the remote endpoint + close::status::value m_remote_close_code; + + /// Close reason that was received on the wire from the remote endpoint + std::string m_remote_close_reason; + + /// Detailed internal error code + lib::error_code m_ec; + + /// A flag that gets set once it is determined that the connection is an + /// HTTP connection and not a WebSocket one. + bool m_is_http; + + /// A flag that gets set when the completion of an http connection is + /// deferred until later. + session::http_state::value m_http_state; + + bool m_was_clean; + + /// Whether or not this endpoint initiated the closing handshake. + bool m_closed_by_me; + + /// ??? + bool m_failed_by_me; + + /// Whether or not this endpoint initiated the drop of the TCP connection + bool m_dropped_by_me; +}; + +} // namespace websocketpp + +#include + +#endif // WEBSOCKETPP_CONNECTION_HPP diff --git a/websocketpp/connection_base.hpp b/websocketpp/connection_base.hpp new file mode 100644 index 00000000..2e700962 --- /dev/null +++ b/websocketpp/connection_base.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONNECTION_BASE_HPP +#define WEBSOCKETPP_CONNECTION_BASE_HPP + +namespace websocketpp { + +/// Stub for user supplied base class. +class connection_base {}; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONNECTION_BASE_HPP diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp new file mode 100644 index 00000000..65584d8a --- /dev/null +++ b/websocketpp/endpoint.hpp @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_ENDPOINT_HPP +#define WEBSOCKETPP_ENDPOINT_HPP + +#include + +#include +#include + +#include + +namespace websocketpp { + +/// Creates and manages connections associated with a WebSocket endpoint +template +class endpoint : public config::transport_type, public config::endpoint_base { +public: + // Import appropriate types from our helper class + // See endpoint_types for more details. + typedef endpoint type; + + /// Type of the transport component of this endpoint + typedef typename config::transport_type transport_type; + /// Type of the concurrency component of this endpoint + typedef typename config::concurrency_type concurrency_type; + + /// Type of the connections that this endpoint creates + typedef connection connection_type; + /// Shared pointer to connection_type + typedef typename connection_type::ptr connection_ptr; + /// Weak pointer to connection type + typedef typename connection_type::weak_ptr connection_weak_ptr; + + /// Type of the transport component of the connections that this endpoint + /// creates + typedef typename transport_type::transport_con_type transport_con_type; + /// Type of a shared pointer to the transport component of the connections + /// that this endpoint creates. + typedef typename transport_con_type::ptr transport_con_ptr; + + /// Type of message_handler + typedef typename connection_type::message_handler message_handler; + /// Type of message pointers that this endpoint uses + typedef typename connection_type::message_ptr message_ptr; + + /// Type of error logger + typedef typename config::elog_type elog_type; + /// Type of access logger + typedef typename config::alog_type alog_type; + + /// Type of our concurrency policy's scoped lock object + typedef typename concurrency_type::scoped_lock_type scoped_lock_type; + /// Type of our concurrency policy's mutex object + typedef typename concurrency_type::mutex_type mutex_type; + + /// Type of RNG + typedef typename config::rng_type rng_type; + + // TODO: organize these + typedef typename connection_type::termination_handler termination_handler; + + // This would be ideal. Requires C++11 though + //friend connection; + + explicit endpoint(bool p_is_server) + : m_alog(config::alog_level, log::channel_type_hint::access) + , m_elog(config::elog_level, log::channel_type_hint::error) + , m_user_agent(::websocketpp::user_agent) + , m_open_handshake_timeout_dur(config::timeout_open_handshake) + , m_close_handshake_timeout_dur(config::timeout_close_handshake) + , m_pong_timeout_dur(config::timeout_pong) + , m_max_message_size(config::max_message_size) + , m_max_http_body_size(config::max_http_body_size) + , m_is_server(p_is_server) + { + m_alog.set_channels(config::alog_level); + m_elog.set_channels(config::elog_level); + + m_alog.write(log::alevel::devel, "endpoint constructor"); + + transport_type::init_logging(&m_alog, &m_elog); + } + + + /// Destructor + ~endpoint() {} + + #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no copy constructor because endpoints are not copyable + endpoint(endpoint &) = delete; + + // no copy assignment operator because endpoints are not copyable + endpoint & operator=(endpoint const &) = delete; + #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + + #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + endpoint(endpoint && o) + : config::transport_type(std::move(o)) + , config::endpoint_base(std::move(o)) + , m_alog(std::move(o.m_alog)) + , m_elog(std::move(o.m_elog)) + , m_user_agent(std::move(o.m_user_agent)) + , m_open_handler(std::move(o.m_open_handler)) + + , m_close_handler(std::move(o.m_close_handler)) + , m_fail_handler(std::move(o.m_fail_handler)) + , m_ping_handler(std::move(o.m_ping_handler)) + , m_pong_handler(std::move(o.m_pong_handler)) + , m_pong_timeout_handler(std::move(o.m_pong_timeout_handler)) + , m_interrupt_handler(std::move(o.m_interrupt_handler)) + , m_http_handler(std::move(o.m_http_handler)) + , m_validate_handler(std::move(o.m_validate_handler)) + , m_message_handler(std::move(o.m_message_handler)) + + , m_open_handshake_timeout_dur(o.m_open_handshake_timeout_dur) + , m_close_handshake_timeout_dur(o.m_close_handshake_timeout_dur) + , m_pong_timeout_dur(o.m_pong_timeout_dur) + , m_max_message_size(o.m_max_message_size) + , m_max_http_body_size(o.m_max_http_body_size) + + , m_rng(std::move(o.m_rng)) + , m_is_server(o.m_is_server) + {} + + #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no move assignment operator because of const member variables + endpoint & operator=(endpoint &&) = delete; + #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + + #endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + + + /// Returns the user agent string that this endpoint will use + /** + * Returns the user agent string that this endpoint will use when creating + * new connections. + * + * The default value for this version is stored in websocketpp::user_agent + * + * @return The user agent string. + */ + std::string get_user_agent() const { + scoped_lock_type guard(m_mutex); + return m_user_agent; + } + + /// Sets the user agent string that this endpoint will use + /** + * Sets the identifier that this endpoint will use when creating new + * connections. Changing this value will only affect future connections. + * For client endpoints this will be sent as the "User-Agent" header in + * outgoing requests. For server endpoints this will be sent in the "Server" + * response header. + * + * Setting this value to the empty string will suppress the use of the + * Server and User-Agent headers. This is typically done to hide + * implementation details for security purposes. + * + * For best results set this before accepting or opening connections. + * + * The default value for this version is stored in websocketpp::user_agent + * + * This can be overridden on an individual connection basis by setting a + * custom "Server" header during the validate handler or "User-Agent" + * header on a connection before calling connect(). + * + * @param ua The string to set the user agent to. + */ + void set_user_agent(std::string const & ua) { + scoped_lock_type guard(m_mutex); + m_user_agent = ua; + } + + /// Returns whether or not this endpoint is a server. + /** + * @return Whether or not this endpoint is a server + */ + bool is_server() const { + return m_is_server; + } + + /********************************/ + /* Pass-through logging adaptor */ + /********************************/ + + /// Set Access logging channel + /** + * Set the access logger's channel value. The value is a number whose + * interpretation depends on the logging policy in use. + * + * @param channels The channel value(s) to set + */ + void set_access_channels(log::level channels) { + m_alog.set_channels(channels); + } + + /// Clear Access logging channels + /** + * Clear the access logger's channel value. The value is a number whose + * interpretation depends on the logging policy in use. + * + * @param channels The channel value(s) to clear + */ + void clear_access_channels(log::level channels) { + m_alog.clear_channels(channels); + } + + /// Set Error logging channel + /** + * Set the error logger's channel value. The value is a number whose + * interpretation depends on the logging policy in use. + * + * @param channels The channel value(s) to set + */ + void set_error_channels(log::level channels) { + m_elog.set_channels(channels); + } + + /// Clear Error logging channels + /** + * Clear the error logger's channel value. The value is a number whose + * interpretation depends on the logging policy in use. + * + * @param channels The channel value(s) to clear + */ + void clear_error_channels(log::level channels) { + m_elog.clear_channels(channels); + } + + /// Get reference to access logger + /** + * @return A reference to the access logger + */ + alog_type & get_alog() { + return m_alog; + } + + /// Get reference to error logger + /** + * @return A reference to the error logger + */ + elog_type & get_elog() { + return m_elog; + } + + /*************************/ + /* Set Handler functions */ + /*************************/ + + void set_open_handler(open_handler h) { + m_alog.write(log::alevel::devel,"set_open_handler"); + scoped_lock_type guard(m_mutex); + m_open_handler = h; + } + void set_close_handler(close_handler h) { + m_alog.write(log::alevel::devel,"set_close_handler"); + scoped_lock_type guard(m_mutex); + m_close_handler = h; + } + void set_fail_handler(fail_handler h) { + m_alog.write(log::alevel::devel,"set_fail_handler"); + scoped_lock_type guard(m_mutex); + m_fail_handler = h; + } + void set_ping_handler(ping_handler h) { + m_alog.write(log::alevel::devel,"set_ping_handler"); + scoped_lock_type guard(m_mutex); + m_ping_handler = h; + } + void set_pong_handler(pong_handler h) { + m_alog.write(log::alevel::devel,"set_pong_handler"); + scoped_lock_type guard(m_mutex); + m_pong_handler = h; + } + void set_pong_timeout_handler(pong_timeout_handler h) { + m_alog.write(log::alevel::devel,"set_pong_timeout_handler"); + scoped_lock_type guard(m_mutex); + m_pong_timeout_handler = h; + } + void set_interrupt_handler(interrupt_handler h) { + m_alog.write(log::alevel::devel,"set_interrupt_handler"); + scoped_lock_type guard(m_mutex); + m_interrupt_handler = h; + } + void set_http_handler(http_handler h) { + m_alog.write(log::alevel::devel,"set_http_handler"); + scoped_lock_type guard(m_mutex); + m_http_handler = h; + } + void set_validate_handler(validate_handler h) { + m_alog.write(log::alevel::devel,"set_validate_handler"); + scoped_lock_type guard(m_mutex); + m_validate_handler = h; + } + void set_message_handler(message_handler h) { + m_alog.write(log::alevel::devel,"set_message_handler"); + scoped_lock_type guard(m_mutex); + m_message_handler = h; + } + + ////////////////////////////////////////// + // Connection timeouts and other limits // + ////////////////////////////////////////// + + /// Set open handshake timeout + /** + * Sets the length of time the library will wait after an opening handshake + * has been initiated before cancelling it. This can be used to prevent + * excessive wait times for outgoing clients or excessive resource usage + * from broken clients or DoS attacks on servers. + * + * Connections that time out will have their fail handlers called with the + * open_handshake_timeout error code. + * + * The default value is specified via the compile time config value + * 'timeout_open_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the open handshake timeout in ms + */ + void set_open_handshake_timeout(long dur) { + scoped_lock_type guard(m_mutex); + m_open_handshake_timeout_dur = dur; + } + + /// Set close handshake timeout + /** + * Sets the length of time the library will wait after a closing handshake + * has been initiated before cancelling it. This can be used to prevent + * excessive wait times for outgoing clients or excessive resource usage + * from broken clients or DoS attacks on servers. + * + * Connections that time out will have their close handlers called with the + * close_handshake_timeout error code. + * + * The default value is specified via the compile time config value + * 'timeout_close_handshake'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the close handshake timeout in ms + */ + void set_close_handshake_timeout(long dur) { + scoped_lock_type guard(m_mutex); + m_close_handshake_timeout_dur = dur; + } + + /// Set pong timeout + /** + * Sets the length of time the library will wait for a pong response to a + * ping. This can be used as a keepalive or to detect broken connections. + * + * Pong responses that time out will have the pong timeout handler called. + * + * The default value is specified via the compile time config value + * 'timeout_pong'. The default value in the core config + * is 5000ms. A value of 0 will disable the timer entirely. + * + * To be effective, the transport you are using must support timers. See + * the documentation for your transport policy for details about its + * timer support. + * + * @param dur The length of the pong timeout in ms + */ + void set_pong_timeout(long dur) { + scoped_lock_type guard(m_mutex); + m_pong_timeout_dur = dur; + } + + /// Get default maximum message size + /** + * Get the default maximum message size that will be used for new + * connections created by this endpoint. The maximum message size determines + * the point at which the connection will fail a connection with the + * message_too_big protocol error. + * + * The default is set by the max_message_size value from the template config + * + * @since 0.3.0 + */ + size_t get_max_message_size() const { + return m_max_message_size; + } + + /// Set default maximum message size + /** + * Set the default maximum message size that will be used for new + * connections created by this endpoint. Maximum message size determines the + * point at which the connection will fail a connection with the + * message_too_big protocol error. + * + * The default is set by the max_message_size value from the template config + * + * @since 0.3.0 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_message_size(size_t new_value) { + m_max_message_size = new_value; + } + + /// Get maximum HTTP message body size + /** + * Get maximum HTTP message body size. Maximum message body size determines + * the point at which the connection will stop reading an HTTP request whose + * body is too large. + * + * The default is set by the max_http_body_size value from the template + * config + * + * @since 0.5.0 + * + * @return The maximum HTTP message body size + */ + size_t get_max_http_body_size() const { + return m_max_http_body_size; + } + + /// Set maximum HTTP message body size + /** + * Set maximum HTTP message body size. Maximum message body size determines + * the point at which the connection will stop reading an HTTP request whose + * body is too large. + * + * The default is set by the max_http_body_size value from the template + * config + * + * @since 0.5.1 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_http_body_size(size_t new_value) { + m_max_http_body_size = new_value; + } + + /*************************************/ + /* Connection pass through functions */ + /*************************************/ + + /** + * These functions act as adaptors to their counterparts in connection. They + * can produce one additional type of error, the bad_connection error, that + * indicates that the conversion from connection_hdl to connection_ptr + * failed due to the connection not existing anymore. Each method has a + * default and an exception free varient. + */ + + void interrupt(connection_hdl hdl, lib::error_code & ec); + void interrupt(connection_hdl hdl); + + /// Pause reading of new data (exception free) + /** + * Signals to the connection to halt reading of new data. While reading is + * paused, the connection will stop reading from its associated socket. In + * turn this will result in TCP based flow control kicking in and slowing + * data flow from the remote endpoint. + * + * This is useful for applications that push new requests to a queue to be + * processed by another thread and need a way to signal when their request + * queue is full without blocking the network processing thread. + * + * Use `resume_reading()` to resume. + * + * If supported by the transport this is done asynchronously. As such + * reading may not stop until the current read operation completes. + * Typically you can expect to receive no more bytes after initiating a read + * pause than the size of the read buffer. + * + * If reading is paused for this connection already nothing is changed. + */ + void pause_reading(connection_hdl hdl, lib::error_code & ec); + + /// Pause reading of new data + void pause_reading(connection_hdl hdl); + + /// Resume reading of new data (exception free) + /** + * Signals to the connection to resume reading of new data after it was + * paused by `pause_reading()`. + * + * If reading is not paused for this connection already nothing is changed. + */ + void resume_reading(connection_hdl hdl, lib::error_code & ec); + + /// Resume reading of new data + void resume_reading(connection_hdl hdl); + + /// Send deferred HTTP Response + /** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * Exception free variant + * + * @since 0.6.0 + * + * @param hdl The connection to send the response on + * @param ec A status code, zero on success, non-zero otherwise + */ + void send_http_response(connection_hdl hdl, lib::error_code & ec); + + /// Send deferred HTTP Response (exception free) + /** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * Exception variant + * + * @since 0.6.0 + * + * @param hdl The connection to send the response on + */ + void send_http_response(connection_hdl hdl); + + /// Create a message and add it to the outgoing send queue (exception free) + /** + * Convenience method to send a message given a payload string and an opcode + * + * @param [in] hdl The handle identifying the connection to send via. + * @param [in] payload The payload string to generated the message with + * @param [in] op The opcode to generated the message with. + * @param [out] ec A code to fill in for errors + */ + void send(connection_hdl hdl, std::string const & payload, + frame::opcode::value op, lib::error_code & ec); + /// Create a message and add it to the outgoing send queue + /** + * Convenience method to send a message given a payload string and an opcode + * + * @param [in] hdl The handle identifying the connection to send via. + * @param [in] payload The payload string to generated the message with + * @param [in] op The opcode to generated the message with. + * @param [out] ec A code to fill in for errors + */ + void send(connection_hdl hdl, std::string const & payload, + frame::opcode::value op); + + void send(connection_hdl hdl, void const * payload, size_t len, + frame::opcode::value op, lib::error_code & ec); + void send(connection_hdl hdl, void const * payload, size_t len, + frame::opcode::value op); + + void send(connection_hdl hdl, message_ptr msg, lib::error_code & ec); + void send(connection_hdl hdl, message_ptr msg); + + void close(connection_hdl hdl, close::status::value const code, + std::string const & reason, lib::error_code & ec); + void close(connection_hdl hdl, close::status::value const code, + std::string const & reason); + + /// Send a ping to a specific connection + /** + * @since 0.3.0-alpha3 + * + * @param [in] hdl The connection_hdl of the connection to send to. + * @param [in] payload The payload string to send. + * @param [out] ec A reference to an error code to fill in + */ + void ping(connection_hdl hdl, std::string const & payload, + lib::error_code & ec); + /// Send a ping to a specific connection + /** + * Exception variant of `ping` + * + * @since 0.3.0-alpha3 + * + * @param [in] hdl The connection_hdl of the connection to send to. + * @param [in] payload The payload string to send. + */ + void ping(connection_hdl hdl, std::string const & payload); + + /// Send a pong to a specific connection + /** + * @since 0.3.0-alpha3 + * + * @param [in] hdl The connection_hdl of the connection to send to. + * @param [in] payload The payload string to send. + * @param [out] ec A reference to an error code to fill in + */ + void pong(connection_hdl hdl, std::string const & payload, + lib::error_code & ec); + /// Send a pong to a specific connection + /** + * Exception variant of `pong` + * + * @since 0.3.0-alpha3 + * + * @param [in] hdl The connection_hdl of the connection to send to. + * @param [in] payload The payload string to send. + */ + void pong(connection_hdl hdl, std::string const & payload); + + /// Retrieves a connection_ptr from a connection_hdl (exception free) + /** + * Converting a weak pointer to shared_ptr is not thread safe because the + * pointer could be deleted at any time. + * + * NOTE: This method may be called by handler to upgrade its handle to a + * full connection_ptr. That full connection may then be used safely for the + * remainder of the handler body. get_con_from_hdl and the resulting + * connection_ptr are NOT safe to use outside the handler loop. + * + * @param hdl The connection handle to translate + * + * @return the connection_ptr. May be NULL if the handle was invalid. + */ + connection_ptr get_con_from_hdl(connection_hdl hdl, lib::error_code & ec) { + connection_ptr con = lib::static_pointer_cast( + hdl.lock()); + if (!con) { + ec = error::make_error_code(error::bad_connection); + } + return con; + } + + /// Retrieves a connection_ptr from a connection_hdl (exception version) + connection_ptr get_con_from_hdl(connection_hdl hdl) { + lib::error_code ec; + connection_ptr con = this->get_con_from_hdl(hdl,ec); + if (ec) { + throw exception(ec); + } + return con; + } +protected: + connection_ptr create_connection(); + + alog_type m_alog; + elog_type m_elog; +private: + // dynamic settings + std::string m_user_agent; + + open_handler m_open_handler; + close_handler m_close_handler; + fail_handler m_fail_handler; + ping_handler m_ping_handler; + pong_handler m_pong_handler; + pong_timeout_handler m_pong_timeout_handler; + interrupt_handler m_interrupt_handler; + http_handler m_http_handler; + validate_handler m_validate_handler; + message_handler m_message_handler; + + long m_open_handshake_timeout_dur; + long m_close_handshake_timeout_dur; + long m_pong_timeout_dur; + size_t m_max_message_size; + size_t m_max_http_body_size; + + rng_type m_rng; + + // static settings + bool const m_is_server; + + // endpoint state + mutable mutex_type m_mutex; +}; + +} // namespace websocketpp + +#include + +#endif // WEBSOCKETPP_ENDPOINT_HPP diff --git a/websocketpp/endpoint_base.hpp b/websocketpp/endpoint_base.hpp new file mode 100644 index 00000000..1ad1a44d --- /dev/null +++ b/websocketpp/endpoint_base.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_ENDPOINT_BASE_HPP +#define WEBSOCKETPP_ENDPOINT_BASE_HPP + +namespace websocketpp { + +/// Stub for user supplied base class. +class endpoint_base {}; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_ENDPOINT_BASE_HPP diff --git a/websocketpp/error.hpp b/websocketpp/error.hpp new file mode 100644 index 00000000..562fb6ed --- /dev/null +++ b/websocketpp/error.hpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_ERROR_HPP +#define WEBSOCKETPP_ERROR_HPP + +#include +#include +#include + +#include +#include + +namespace websocketpp { + +/// Combination error code / string type for returning two values +typedef std::pair err_str_pair; + +/// Library level error codes +namespace error { +enum value { + /// Catch-all library error + general = 1, + + /// send attempted when endpoint write queue was full + send_queue_full, + + /// Attempted an operation using a payload that was improperly formatted + /// ex: invalid UTF8 encoding on a text message. + payload_violation, + + /// Attempted to open a secure connection with an insecure endpoint + endpoint_not_secure, + + /// Attempted an operation that required an endpoint that is no longer + /// available. This is usually because the endpoint went out of scope + /// before a connection that it created. + endpoint_unavailable, + + /// An invalid uri was supplied + invalid_uri, + + /// The endpoint is out of outgoing message buffers + no_outgoing_buffers, + + /// The endpoint is out of incoming message buffers + no_incoming_buffers, + + /// The connection was in the wrong state for this operation + invalid_state, + + /// Unable to parse close code + bad_close_code, + + /// Close code is in a reserved range + reserved_close_code, + + /// Close code is invalid + invalid_close_code, + + /// Invalid UTF-8 + invalid_utf8, + + /// Invalid subprotocol + invalid_subprotocol, + + /// An operation was attempted on a connection that did not exist or was + /// already deleted. + bad_connection, + + /// Unit testing utility error code + test, + + /// Connection creation attempted failed + con_creation_failed, + + /// Selected subprotocol was not requested by the client + unrequested_subprotocol, + + /// Attempted to use a client specific feature on a server endpoint + client_only, + + /// Attempted to use a server specific feature on a client endpoint + server_only, + + /// HTTP connection ended + http_connection_ended, + + /// WebSocket opening handshake timed out + open_handshake_timeout, + + /// WebSocket close handshake timed out + close_handshake_timeout, + + /// Invalid port in URI + invalid_port, + + /// An async accept operation failed because the underlying transport has been + /// requested to not listen for new connections anymore. + async_accept_not_listening, + + /// The requested operation was canceled + operation_canceled, + + /// Connection rejected + rejected, + + /// Upgrade Required. This happens if an HTTP request is made to a + /// WebSocket++ server that doesn't implement an http handler + upgrade_required, + + /// Invalid WebSocket protocol version + invalid_version, + + /// Unsupported WebSocket protocol version + unsupported_version, + + /// HTTP parse error + http_parse_error, + + /// Extension negotiation failed + extension_neg_failed +}; // enum value + + +class category : public lib::error_category { +public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp"; + } + + std::string message(int value) const { + switch(value) { + case error::general: + return "Generic error"; + case error::send_queue_full: + return "send queue full"; + case error::payload_violation: + return "payload violation"; + case error::endpoint_not_secure: + return "endpoint not secure"; + case error::endpoint_unavailable: + return "endpoint not available"; + case error::invalid_uri: + return "invalid uri"; + case error::no_outgoing_buffers: + return "no outgoing message buffers"; + case error::no_incoming_buffers: + return "no incoming message buffers"; + case error::invalid_state: + return "invalid state"; + case error::bad_close_code: + return "Unable to extract close code"; + case error::invalid_close_code: + return "Extracted close code is in an invalid range"; + case error::reserved_close_code: + return "Extracted close code is in a reserved range"; + case error::invalid_utf8: + return "Invalid UTF-8"; + case error::invalid_subprotocol: + return "Invalid subprotocol"; + case error::bad_connection: + return "Bad Connection"; + case error::test: + return "Test Error"; + case error::con_creation_failed: + return "Connection creation attempt failed"; + case error::unrequested_subprotocol: + return "Selected subprotocol was not requested by the client"; + case error::client_only: + return "Feature not available on server endpoints"; + case error::server_only: + return "Feature not available on client endpoints"; + case error::http_connection_ended: + return "HTTP connection ended"; + case error::open_handshake_timeout: + return "The opening handshake timed out"; + case error::close_handshake_timeout: + return "The closing handshake timed out"; + case error::invalid_port: + return "Invalid URI port"; + case error::async_accept_not_listening: + return "Async Accept not listening"; + case error::operation_canceled: + return "Operation canceled"; + case error::rejected: + return "Connection rejected"; + case error::upgrade_required: + return "Upgrade required"; + case error::invalid_version: + return "Invalid version"; + case error::unsupported_version: + return "Unsupported version"; + case error::http_parse_error: + return "HTTP parse error"; + case error::extension_neg_failed: + return "Extension negotiation failed"; + default: + return "Unknown"; + } + } +}; + +inline const lib::error_category& get_category() { + static category instance; + return instance; +} + +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +namespace websocketpp { + +class exception : public std::exception { +public: + exception(std::string const & msg, lib::error_code ec = make_error_code(error::general)) + : m_msg(msg.empty() ? ec.message() : msg), m_code(ec) + {} + + explicit exception(lib::error_code ec) + : m_msg(ec.message()), m_code(ec) + {} + + ~exception() throw() {} + + virtual char const * what() const throw() { + return m_msg.c_str(); + } + + lib::error_code code() const throw() { + return m_code; + } + + const std::string m_msg; + lib::error_code m_code; +}; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_ERROR_HPP diff --git a/websocketpp/extensions/extension.hpp b/websocketpp/extensions/extension.hpp new file mode 100644 index 00000000..f5fbd9f7 --- /dev/null +++ b/websocketpp/extensions/extension.hpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_EXTENSION_HPP +#define WEBSOCKETPP_EXTENSION_HPP + +#include +#include + +#include +#include + +namespace websocketpp { + +/** + * Some generic information about extensions + * + * Each extension object has an implemented flag. It can be retrieved by calling + * is_implemented(). This compile time flag indicates whether or not the object + * in question actually implements the extension or if it is a placeholder stub + * + * Each extension object also has an enabled flag. It can be retrieved by + * calling is_enabled(). This runtime flag indicates whether or not the + * extension has been negotiated for this connection. + */ +namespace extensions { + +namespace error { +enum value { + /// Catch all + general = 1, + + /// Extension disabled + disabled +}; + +class category : public lib::error_category { +public: + category() {} + + const char *name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.extension"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic extension error"; + case disabled: + return "Use of methods from disabled extension"; + default: + return "Unknown permessage-compress error"; + } + } +}; + +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace extensions +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum + +{ + static const bool value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif // WEBSOCKETPP_EXTENSION_HPP diff --git a/websocketpp/extensions/permessage_deflate/disabled.hpp b/websocketpp/extensions/permessage_deflate/disabled.hpp new file mode 100644 index 00000000..49c0e1dc --- /dev/null +++ b/websocketpp/extensions/permessage_deflate/disabled.hpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP +#define WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP + +#include +#include + +#include +#include + +#include +#include +#include + +namespace websocketpp { +namespace extensions { +namespace permessage_deflate { + +/// Stub class for use when disabling permessage_deflate extension +/** + * This class is a stub that implements the permessage_deflate interface + * with minimal dependencies. It is used to disable permessage_deflate + * functionality at compile time without loading any unnecessary code. + */ +template +class disabled { + typedef std::pair err_str_pair; + +public: + /// Negotiate extension + /** + * The disabled extension always fails the negotiation with a disabled + * error. + * + * @param offer Attribute from client's offer + * @return Status code and value to return to remote endpoint + */ + err_str_pair negotiate(http::attribute_list const &) { + return make_pair(make_error_code(error::disabled),std::string()); + } + + /// Initialize state + /** + * For the disabled extension state initialization is a no-op. + * + * @param is_server True to initialize as a server, false for a client. + * @return A code representing the error that occurred, if any + */ + lib::error_code init(bool) { + return lib::error_code(); + } + + /// Returns true if the extension is capable of providing + /// permessage_deflate functionality + bool is_implemented() const { + return false; + } + + /// Returns true if permessage_deflate functionality is active for this + /// connection + bool is_enabled() const { + return false; + } + + /// Generate extension offer + /** + * Creates an offer string to include in the Sec-WebSocket-Extensions + * header of outgoing client requests. + * + * @return A WebSocket extension offer string for this extension + */ + std::string generate_offer() const { + return ""; + } + + /// Compress bytes + /** + * @param [in] in String to compress + * @param [out] out String to append compressed bytes to + * @return Error or status code + */ + lib::error_code compress(std::string const &, std::string &) { + return make_error_code(error::disabled); + } + + /// Decompress bytes + /** + * @param buf Byte buffer to decompress + * @param len Length of buf + * @param out String to append decompressed bytes to + * @return Error or status code + */ + lib::error_code decompress(uint8_t const *, size_t, std::string &) { + return make_error_code(error::disabled); + } +}; + +} // namespace permessage_deflate +} // namespace extensions +} // namespace websocketpp + +#endif // WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP diff --git a/websocketpp/extensions/permessage_deflate/enabled.hpp b/websocketpp/extensions/permessage_deflate/enabled.hpp new file mode 100644 index 00000000..1581f14c --- /dev/null +++ b/websocketpp/extensions/permessage_deflate/enabled.hpp @@ -0,0 +1,752 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP +#define WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP + + +#include +#include +#include +#include +#include + +#include + +#include "zlib.h" + +#include +#include +#include + +namespace websocketpp { +namespace extensions { + +/// Implementation of the draft permessage-deflate WebSocket extension +/** + * ### permessage-deflate interface + * + * **init**\n + * `lib::error_code init(bool is_server)`\n + * Performs initialization + * + * **is_implimented**\n + * `bool is_implimented()`\n + * Returns whether or not the object impliments the extension or not + * + * **is_enabled**\n + * `bool is_enabled()`\n + * Returns whether or not the extension was negotiated for the current + * connection + * + * **generate_offer**\n + * `std::string generate_offer() const`\n + * Create an extension offer string based on local policy + * + * **validate_response**\n + * `lib::error_code validate_response(http::attribute_list const & response)`\n + * Negotiate the parameters of extension use + * + * **negotiate**\n + * `err_str_pair negotiate(http::attribute_list const & attributes)`\n + * Negotiate the parameters of extension use + * + * **compress**\n + * `lib::error_code compress(std::string const & in, std::string & out)`\n + * Compress the bytes in `in` and append them to `out` + * + * **decompress**\n + * `lib::error_code decompress(uint8_t const * buf, size_t len, std::string & + * out)`\n + * Decompress `len` bytes from `buf` and append them to string `out` + */ +namespace permessage_deflate { + +/// Permessage deflate error values +namespace error { +enum value { + /// Catch all + general = 1, + + /// Invalid extension attributes + invalid_attributes, + + /// Invalid extension attribute value + invalid_attribute_value, + + /// Invalid megotiation mode + invalid_mode, + + /// Unsupported extension attributes + unsupported_attributes, + + /// Invalid value for max_window_bits + invalid_max_window_bits, + + /// ZLib Error + zlib_error, + + /// Uninitialized + uninitialized, +}; + +/// Permessage-deflate error category +class category : public lib::error_category { +public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.extension.permessage-deflate"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic permessage-compress error"; + case invalid_attributes: + return "Invalid extension attributes"; + case invalid_attribute_value: + return "Invalid extension attribute value"; + case invalid_mode: + return "Invalid permessage-deflate negotiation mode"; + case unsupported_attributes: + return "Unsupported extension attributes"; + case invalid_max_window_bits: + return "Invalid value for max_window_bits"; + case zlib_error: + return "A zlib function returned an error"; + case uninitialized: + return "Deflate extension must be initialized before use"; + default: + return "Unknown permessage-compress error"; + } + } +}; + +/// Get a reference to a static copy of the permessage-deflate error category +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +/// Create an error code in the permessage-deflate category +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace permessage_deflate +} // namespace extensions +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum + +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ +namespace websocketpp { +namespace extensions { +namespace permessage_deflate { + +/// Default value for server_max_window_bits as defined by draft 17 +static uint8_t const default_server_max_window_bits = 15; +/// Minimum value for server_max_window_bits as defined by draft 17 +static uint8_t const min_server_max_window_bits = 8; +/// Maximum value for server_max_window_bits as defined by draft 17 +static uint8_t const max_server_max_window_bits = 15; + +/// Default value for client_max_window_bits as defined by draft 17 +static uint8_t const default_client_max_window_bits = 15; +/// Minimum value for client_max_window_bits as defined by draft 17 +static uint8_t const min_client_max_window_bits = 8; +/// Maximum value for client_max_window_bits as defined by draft 17 +static uint8_t const max_client_max_window_bits = 15; + +namespace mode { +enum value { + /// Accept any value the remote endpoint offers + accept = 1, + /// Decline any value the remote endpoint offers. Insist on defaults. + decline, + /// Use the largest value common to both offers + largest, + /// Use the smallest value common to both offers + smallest +}; +} // namespace mode + +template +class enabled { +public: + enabled() + : m_enabled(false) + , m_server_no_context_takeover(false) + , m_client_no_context_takeover(false) + , m_server_max_window_bits(15) + , m_client_max_window_bits(15) + , m_server_max_window_bits_mode(mode::accept) + , m_client_max_window_bits_mode(mode::accept) + , m_initialized(false) + , m_compress_buffer_size(16384) + { + m_dstate.zalloc = Z_NULL; + m_dstate.zfree = Z_NULL; + m_dstate.opaque = Z_NULL; + + m_istate.zalloc = Z_NULL; + m_istate.zfree = Z_NULL; + m_istate.opaque = Z_NULL; + m_istate.avail_in = 0; + m_istate.next_in = Z_NULL; + } + + ~enabled() { + if (!m_initialized) { + return; + } + + int ret = deflateEnd(&m_dstate); + + if (ret != Z_OK) { + //std::cout << "error cleaning up zlib compression state" + // << std::endl; + } + + ret = inflateEnd(&m_istate); + + if (ret != Z_OK) { + //std::cout << "error cleaning up zlib decompression state" + // << std::endl; + } + } + + /// Initialize zlib state + /** + * Note: this should be called *after* the negotiation methods. It will use + * information from the negotiation to determine how to initialize the zlib + * data structures. + * + * @todo memory level, strategy, etc are hardcoded + * + * @param is_server True to initialize as a server, false for a client. + * @return A code representing the error that occurred, if any + */ + lib::error_code init(bool is_server) { + uint8_t deflate_bits; + uint8_t inflate_bits; + + if (is_server) { + deflate_bits = m_server_max_window_bits; + inflate_bits = m_client_max_window_bits; + } else { + deflate_bits = m_client_max_window_bits; + inflate_bits = m_server_max_window_bits; + } + + int ret = deflateInit2( + &m_dstate, + Z_DEFAULT_COMPRESSION, + Z_DEFLATED, + -1*deflate_bits, + 4, // memory level 1-9 + Z_DEFAULT_STRATEGY + ); + + if (ret != Z_OK) { + return make_error_code(error::zlib_error); + } + + ret = inflateInit2( + &m_istate, + -1*inflate_bits + ); + + if (ret != Z_OK) { + return make_error_code(error::zlib_error); + } + + m_compress_buffer.reset(new unsigned char[m_compress_buffer_size]); + if ((m_server_no_context_takeover && is_server) || + (m_client_no_context_takeover && !is_server)) + { + m_flush = Z_FULL_FLUSH; + } else { + m_flush = Z_SYNC_FLUSH; + } + m_initialized = true; + return lib::error_code(); + } + + /// Test if this object implements the permessage-deflate specification + /** + * Because this object does implieent it, it will always return true. + * + * @return Whether or not this object implements permessage-deflate + */ + bool is_implemented() const { + return true; + } + + /// Test if the extension was negotiated for this connection + /** + * Retrieves whether or not this extension is in use based on the initial + * handshake extension negotiations. + * + * @return Whether or not the extension is in use + */ + bool is_enabled() const { + return m_enabled; + } + + /// Reset server's outgoing LZ77 sliding window for each new message + /** + * Enabling this setting will cause the server's compressor to reset the + * compression state (the LZ77 sliding window) for every message. This + * means that the compressor will not look back to patterns in previous + * messages to improve compression. This will reduce the compression + * efficiency for large messages somewhat and small messages drastically. + * + * This option may reduce server compressor memory usage and client + * decompressor memory usage. + * @todo Document to what extent memory usage will be reduced + * + * For clients, this option is dependent on server support. Enabling it + * via this method does not guarantee that it will be successfully + * negotiated, only that it will be requested. + * + * For servers, no client support is required. Enabling this option on a + * server will result in its use. The server will signal to clients that + * the option will be in use so they can optimize resource usage if they + * are able. + */ + void enable_server_no_context_takeover() { + m_server_no_context_takeover = true; + } + + /// Reset client's outgoing LZ77 sliding window for each new message + /** + * Enabling this setting will cause the client's compressor to reset the + * compression state (the LZ77 sliding window) for every message. This + * means that the compressor will not look back to patterns in previous + * messages to improve compression. This will reduce the compression + * efficiency for large messages somewhat and small messages drastically. + * + * This option may reduce client compressor memory usage and server + * decompressor memory usage. + * @todo Document to what extent memory usage will be reduced + * + * This option is supported by all compliant clients and servers. Enabling + * it via either endpoint should be sufficient to ensure it is used. + */ + void enable_client_no_context_takeover() { + m_client_no_context_takeover = true; + } + + /// Limit server LZ77 sliding window size + /** + * The bits setting is the base 2 logarithm of the maximum window size that + * the server must use to compress outgoing messages. The permitted range + * is 8 to 15 inclusive. 8 represents a 256 byte window and 15 a 32KiB + * window. The default setting is 15. + * + * Mode Options: + * - accept: Accept whatever the remote endpoint offers. + * - decline: Decline any offers to deviate from the defaults + * - largest: Accept largest window size acceptable to both endpoints + * - smallest: Accept smallest window size acceptiable to both endpoints + * + * This setting is dependent on server support. A client requesting this + * setting may be rejected by the server or have the exact value used + * adjusted by the server. A server may unilaterally set this value without + * client support. + * + * @param bits The size to request for the outgoing window size + * @param mode The mode to use for negotiating this parameter + * @return A status code + */ + lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode) { + if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) { + return error::make_error_code(error::invalid_max_window_bits); + } + m_server_max_window_bits = bits; + m_server_max_window_bits_mode = mode; + + return lib::error_code(); + } + + /// Limit client LZ77 sliding window size + /** + * The bits setting is the base 2 logarithm of the window size that the + * client must use to compress outgoing messages. The permitted range is 8 + * to 15 inclusive. 8 represents a 256 byte window and 15 a 32KiB window. + * The default setting is 15. + * + * Mode Options: + * - accept: Accept whatever the remote endpoint offers. + * - decline: Decline any offers to deviate from the defaults + * - largest: Accept largest window size acceptable to both endpoints + * - smallest: Accept smallest window size acceptiable to both endpoints + * + * This setting is dependent on client support. A client may limit its own + * outgoing window size unilaterally. A server may only limit the client's + * window size if the remote client supports that feature. + * + * @param bits The size to request for the outgoing window size + * @param mode The mode to use for negotiating this parameter + * @return A status code + */ + lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode) { + if (bits < min_client_max_window_bits || bits > max_client_max_window_bits) { + return error::make_error_code(error::invalid_max_window_bits); + } + m_client_max_window_bits = bits; + m_client_max_window_bits_mode = mode; + + return lib::error_code(); + } + + /// Generate extension offer + /** + * Creates an offer string to include in the Sec-WebSocket-Extensions + * header of outgoing client requests. + * + * @return A WebSocket extension offer string for this extension + */ + std::string generate_offer() const { + // TODO: this should be dynamically generated based on user settings + return "permessage-deflate; client_no_context_takeover; client_max_window_bits"; + } + + /// Validate extension response + /** + * Confirm that the server has negotiated settings compatible with our + * original offer and apply those settings to the extension state. + * + * @param response The server response attribute list to validate + * @return Validation error or 0 on success + */ + lib::error_code validate_offer(http::attribute_list const &) { + return lib::error_code(); + } + + /// Negotiate extension + /** + * Confirm that the client's extension negotiation offer has settings + * compatible with local policy. If so, generate a reply and apply those + * settings to the extension state. + * + * @param offer Attribute from client's offer + * @return Status code and value to return to remote endpoint + */ + err_str_pair negotiate(http::attribute_list const & offer) { + err_str_pair ret; + + http::attribute_list::const_iterator it; + for (it = offer.begin(); it != offer.end(); ++it) { + if (it->first == "server_no_context_takeover") { + negotiate_server_no_context_takeover(it->second,ret.first); + } else if (it->first == "client_no_context_takeover") { + negotiate_client_no_context_takeover(it->second,ret.first); + } else if (it->first == "server_max_window_bits") { + negotiate_server_max_window_bits(it->second,ret.first); + } else if (it->first == "client_max_window_bits") { + negotiate_client_max_window_bits(it->second,ret.first); + } else { + ret.first = make_error_code(error::invalid_attributes); + } + + if (ret.first) { + break; + } + } + + if (ret.first == lib::error_code()) { + m_enabled = true; + ret.second = generate_response(); + } + + return ret; + } + + /// Compress bytes + /** + * @todo: avail_in/out is 32 bit, need to fix for cases of >32 bit frames + * on 64 bit machines. + * + * @param [in] in String to compress + * @param [out] out String to append compressed bytes to + * @return Error or status code + */ + lib::error_code compress(std::string const & in, std::string & out) { + if (!m_initialized) { + return make_error_code(error::uninitialized); + } + + size_t output; + + if (in.empty()) { + uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff}; + out.append((char *)(buf),6); + return lib::error_code(); + } + + m_dstate.avail_in = in.size(); + m_dstate.next_in = (unsigned char *)(const_cast(in.data())); + + do { + // Output to local buffer + m_dstate.avail_out = m_compress_buffer_size; + m_dstate.next_out = m_compress_buffer.get(); + + deflate(&m_dstate, m_flush); + + output = m_compress_buffer_size - m_dstate.avail_out; + + out.append((char *)(m_compress_buffer.get()),output); + } while (m_dstate.avail_out == 0); + + return lib::error_code(); + } + + /// Decompress bytes + /** + * @param buf Byte buffer to decompress + * @param len Length of buf + * @param out String to append decompressed bytes to + * @return Error or status code + */ + lib::error_code decompress(uint8_t const * buf, size_t len, std::string & + out) + { + if (!m_initialized) { + return make_error_code(error::uninitialized); + } + + int ret; + + m_istate.avail_in = len; + m_istate.next_in = const_cast(buf); + + do { + m_istate.avail_out = m_compress_buffer_size; + m_istate.next_out = m_compress_buffer.get(); + + ret = inflate(&m_istate, Z_SYNC_FLUSH); + + if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) { + return make_error_code(error::zlib_error); + } + + out.append( + reinterpret_cast(m_compress_buffer.get()), + m_compress_buffer_size - m_istate.avail_out + ); + } while (m_istate.avail_out == 0); + + return lib::error_code(); + } +private: + /// Generate negotiation response + /** + * @return Generate extension negotiation reponse string to send to client + */ + std::string generate_response() { + std::string ret = "permessage-deflate"; + + if (m_server_no_context_takeover) { + ret += "; server_no_context_takeover"; + } + + if (m_client_no_context_takeover) { + ret += "; client_no_context_takeover"; + } + + if (m_server_max_window_bits < default_server_max_window_bits) { + std::stringstream s; + s << int(m_server_max_window_bits); + ret += "; server_max_window_bits="+s.str(); + } + + if (m_client_max_window_bits < default_client_max_window_bits) { + std::stringstream s; + s << int(m_client_max_window_bits); + ret += "; client_max_window_bits="+s.str(); + } + + return ret; + } + + /// Negotiate server_no_context_takeover attribute + /** + * @param [in] value The value of the attribute from the offer + * @param [out] ec A reference to the error code to return errors via + */ + void negotiate_server_no_context_takeover(std::string const & value, + lib::error_code & ec) + { + if (!value.empty()) { + ec = make_error_code(error::invalid_attribute_value); + return; + } + + m_server_no_context_takeover = true; + } + + /// Negotiate client_no_context_takeover attribute + /** + * @param [in] value The value of the attribute from the offer + * @param [out] ec A reference to the error code to return errors via + */ + void negotiate_client_no_context_takeover(std::string const & value, + lib::error_code & ec) + { + if (!value.empty()) { + ec = make_error_code(error::invalid_attribute_value); + return; + } + + m_client_no_context_takeover = true; + } + + /// Negotiate server_max_window_bits attribute + /** + * When this method starts, m_server_max_window_bits will contain the server's + * preferred value and m_server_max_window_bits_mode will contain the mode the + * server wants to use to for negotiation. `value` contains the value the + * client requested that we use. + * + * options: + * - decline (refuse to use the attribute) + * - accept (use whatever the client says) + * - largest (use largest possible value) + * - smallest (use smallest possible value) + * + * @param [in] value The value of the attribute from the offer + * @param [out] ec A reference to the error code to return errors via + */ + void negotiate_server_max_window_bits(std::string const & value, + lib::error_code & ec) + { + uint8_t bits = uint8_t(atoi(value.c_str())); + + if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) { + ec = make_error_code(error::invalid_attribute_value); + m_server_max_window_bits = default_server_max_window_bits; + return; + } + + switch (m_server_max_window_bits_mode) { + case mode::decline: + m_server_max_window_bits = default_server_max_window_bits; + break; + case mode::accept: + m_server_max_window_bits = bits; + break; + case mode::largest: + m_server_max_window_bits = std::min(bits,m_server_max_window_bits); + break; + case mode::smallest: + m_server_max_window_bits = min_server_max_window_bits; + break; + default: + ec = make_error_code(error::invalid_mode); + m_server_max_window_bits = default_server_max_window_bits; + } + } + + /// Negotiate client_max_window_bits attribute + /** + * When this method starts, m_client_max_window_bits and m_c2s_max_window_mode + * will contain the server's preferred values for window size and + * negotiation mode. + * + * options: + * - decline (refuse to use the attribute) + * - accept (use whatever the client says) + * - largest (use largest possible value) + * - smallest (use smallest possible value) + * + * @param [in] value The value of the attribute from the offer + * @param [out] ec A reference to the error code to return errors via + */ + void negotiate_client_max_window_bits(std::string const & value, + lib::error_code & ec) + { + uint8_t bits = uint8_t(atoi(value.c_str())); + + if (value.empty()) { + bits = default_client_max_window_bits; + } else if (bits < min_client_max_window_bits || + bits > max_client_max_window_bits) + { + ec = make_error_code(error::invalid_attribute_value); + m_client_max_window_bits = default_client_max_window_bits; + return; + } + + switch (m_client_max_window_bits_mode) { + case mode::decline: + m_client_max_window_bits = default_client_max_window_bits; + break; + case mode::accept: + m_client_max_window_bits = bits; + break; + case mode::largest: + m_client_max_window_bits = std::min(bits,m_client_max_window_bits); + break; + case mode::smallest: + m_client_max_window_bits = min_client_max_window_bits; + break; + default: + ec = make_error_code(error::invalid_mode); + m_client_max_window_bits = default_client_max_window_bits; + } + } + + bool m_enabled; + bool m_server_no_context_takeover; + bool m_client_no_context_takeover; + uint8_t m_server_max_window_bits; + uint8_t m_client_max_window_bits; + mode::value m_server_max_window_bits_mode; + mode::value m_client_max_window_bits_mode; + + bool m_initialized; + int m_flush; + size_t m_compress_buffer_size; + lib::unique_ptr_uchar_array m_compress_buffer; + z_stream m_dstate; + z_stream m_istate; +}; + +} // namespace permessage_deflate +} // namespace extensions +} // namespace websocketpp + +#endif // WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP diff --git a/websocketpp/frame.hpp b/websocketpp/frame.hpp new file mode 100644 index 00000000..8a173375 --- /dev/null +++ b/websocketpp/frame.hpp @@ -0,0 +1,861 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_FRAME_HPP +#define WEBSOCKETPP_FRAME_HPP + +#include +#include + +#include +#include + +#include + +namespace websocketpp { +/// Data structures and utility functions for manipulating WebSocket frames +/** + * namespace frame provides a number of data structures and utility functions + * for reading, writing, and manipulating binary encoded WebSocket frames. + */ +namespace frame { + +/// Minimum length of a WebSocket frame header. +static unsigned int const BASIC_HEADER_LENGTH = 2; +/// Maximum length of a WebSocket header +static unsigned int const MAX_HEADER_LENGTH = 14; +/// Maximum length of the variable portion of the WebSocket header +static unsigned int const MAX_EXTENDED_HEADER_LENGTH = 12; + +/// Two byte conversion union +union uint16_converter { + uint16_t i; + uint8_t c[2]; +}; + +/// Four byte conversion union +union uint32_converter { + uint32_t i; + uint8_t c[4]; +}; + +/// Eight byte conversion union +union uint64_converter { + uint64_t i; + uint8_t c[8]; +}; + +/// Constants and utility functions related to WebSocket opcodes +/** + * WebSocket Opcodes are 4 bits. See RFC6455 section 5.2. + */ +namespace opcode { + enum value { + continuation = 0x0, + text = 0x1, + binary = 0x2, + rsv3 = 0x3, + rsv4 = 0x4, + rsv5 = 0x5, + rsv6 = 0x6, + rsv7 = 0x7, + close = 0x8, + ping = 0x9, + pong = 0xA, + control_rsvb = 0xB, + control_rsvc = 0xC, + control_rsvd = 0xD, + control_rsve = 0xE, + control_rsvf = 0xF, + + CONTINUATION = 0x0, + TEXT = 0x1, + BINARY = 0x2, + RSV3 = 0x3, + RSV4 = 0x4, + RSV5 = 0x5, + RSV6 = 0x6, + RSV7 = 0x7, + CLOSE = 0x8, + PING = 0x9, + PONG = 0xA, + CONTROL_RSVB = 0xB, + CONTROL_RSVC = 0xC, + CONTROL_RSVD = 0xD, + CONTROL_RSVE = 0xE, + CONTROL_RSVF = 0xF + }; + + /// Check if an opcode is reserved + /** + * @param v The opcode to test. + * @return Whether or not the opcode is reserved. + */ + inline bool reserved(value v) { + return (v >= rsv3 && v <= rsv7) || + (v >= control_rsvb && v <= control_rsvf); + } + + /// Check if an opcode is invalid + /** + * Invalid opcodes are negative or require greater than 4 bits to store. + * + * @param v The opcode to test. + * @return Whether or not the opcode is invalid. + */ + inline bool invalid(value v) { + return (v > 0xF || v < 0); + } + + /// Check if an opcode is for a control frame + /** + * @param v The opcode to test. + * @return Whether or not the opcode is a control opcode. + */ + inline bool is_control(value v) { + return v >= 0x8; + } +} + +/// Constants related to frame and payload limits +namespace limits { + /// Minimum length of a WebSocket frame header. + static unsigned int const basic_header_length = 2; + + /// Maximum length of a WebSocket header + static unsigned int const max_header_length = 14; + + /// Maximum length of the variable portion of the WebSocket header + static unsigned int const max_extended_header_length = 12; + + /// Maximum size of a basic WebSocket payload + static uint8_t const payload_size_basic = 125; + + /// Maximum size of an extended WebSocket payload (basic payload = 126) + static uint16_t const payload_size_extended = 0xFFFF; // 2^16, 65535 + + /// Maximum size of a jumbo WebSocket payload (basic payload = 127) + static uint64_t const payload_size_jumbo = 0x7FFFFFFFFFFFFFFFLL;//2^63 + + /// Maximum size of close frame reason + /** + * This is payload_size_basic - 2 bytes (as first two bytes are used for + * the close code + */ + static uint8_t const close_reason_size = 123; +} + + +// masks for fields in the basic header +static uint8_t const BHB0_OPCODE = 0x0F; +static uint8_t const BHB0_RSV3 = 0x10; +static uint8_t const BHB0_RSV2 = 0x20; +static uint8_t const BHB0_RSV1 = 0x40; +static uint8_t const BHB0_FIN = 0x80; + +static uint8_t const BHB1_PAYLOAD = 0x7F; +static uint8_t const BHB1_MASK = 0x80; + +static uint8_t const payload_size_code_16bit = 0x7E; // 126 +static uint8_t const payload_size_code_64bit = 0x7F; // 127 + +typedef uint32_converter masking_key_type; + +/// The constant size component of a WebSocket frame header +struct basic_header { + basic_header() : b0(0x00),b1(0x00) {} + + basic_header(uint8_t p0, uint8_t p1) : b0(p0), b1(p1) {} + + basic_header(opcode::value op, uint64_t size, bool fin, bool mask, + bool rsv1 = false, bool rsv2 = false, bool rsv3 = false) : b0(0x00), + b1(0x00) + { + if (fin) { + b0 |= BHB0_FIN; + } + if (rsv1) { + b0 |= BHB0_RSV1; + } + if (rsv2) { + b0 |= BHB0_RSV2; + } + if (rsv3) { + b0 |= BHB0_RSV3; + } + b0 |= (op & BHB0_OPCODE); + + if (mask) { + b1 |= BHB1_MASK; + } + + uint8_t basic_value; + + if (size <= limits::payload_size_basic) { + basic_value = static_cast(size); + } else if (size <= limits::payload_size_extended) { + basic_value = payload_size_code_16bit; + } else { + basic_value = payload_size_code_64bit; + } + + + b1 |= basic_value; + } + + uint8_t b0; + uint8_t b1; +}; + +/// The variable size component of a WebSocket frame header +struct extended_header { + extended_header() { + std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00); + } + + extended_header(uint64_t payload_size) { + std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00); + + copy_payload(payload_size); + } + + extended_header(uint64_t payload_size, uint32_t masking_key) { + std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00); + + // Copy payload size + int offset = copy_payload(payload_size); + + // Copy Masking Key + uint32_converter temp32; + temp32.i = masking_key; + std::copy(temp32.c,temp32.c+4,bytes+offset); + } + + uint8_t bytes[MAX_EXTENDED_HEADER_LENGTH]; +private: + int copy_payload(uint64_t payload_size) { + int payload_offset = 0; + + if (payload_size <= limits::payload_size_basic) { + payload_offset = 8; + } else if (payload_size <= limits::payload_size_extended) { + payload_offset = 6; + } + + uint64_converter temp64; + temp64.i = lib::net::_htonll(payload_size); + std::copy(temp64.c+payload_offset,temp64.c+8,bytes); + + return 8-payload_offset; + } +}; + +bool get_fin(basic_header const &h); +void set_fin(basic_header &h, bool value); +bool get_rsv1(basic_header const &h); +void set_rsv1(basic_header &h, bool value); +bool get_rsv2(basic_header const &h); +void set_rsv2(basic_header &h, bool value); +bool get_rsv3(basic_header const &h); +void set_rsv3(basic_header &h, bool value); +opcode::value get_opcode(basic_header const &h); +bool get_masked(basic_header const &h); +void set_masked(basic_header &h, bool value); +uint8_t get_basic_size(basic_header const &); +size_t get_header_len(basic_header const &); +unsigned int get_masking_key_offset(basic_header const &); + +std::string write_header(basic_header const &, extended_header const &); +masking_key_type get_masking_key(basic_header const &, extended_header const &); +uint16_t get_extended_size(extended_header const &); +uint64_t get_jumbo_size(extended_header const &); +uint64_t get_payload_size(basic_header const &, extended_header const &); + +size_t prepare_masking_key(masking_key_type const & key); +size_t circshift_prepared_key(size_t prepared_key, size_t offset); + +// Functions for performing xor based masking and unmasking +template +void byte_mask(input_iter b, input_iter e, output_iter o, masking_key_type + const & key, size_t key_offset = 0); +template +void byte_mask(iter_type b, iter_type e, masking_key_type const & key, + size_t key_offset = 0); +void word_mask_exact(uint8_t * input, uint8_t * output, size_t length, + masking_key_type const & key); +void word_mask_exact(uint8_t * data, size_t length, masking_key_type const & + key); +size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length, + size_t prepared_key); +size_t word_mask_circ(uint8_t * data, size_t length, size_t prepared_key); + +/// Check whether the frame's FIN bit is set. +/** + * @param [in] h The basic header to extract from. + * @return True if the header's fin bit is set. + */ +inline bool get_fin(basic_header const & h) { + return ((h.b0 & BHB0_FIN) == BHB0_FIN); +} + +/// Set the frame's FIN bit +/** + * @param [out] h Header to set. + * @param [in] value Value to set it to. + */ +inline void set_fin(basic_header & h, bool value) { + h.b0 = (value ? h.b0 | BHB0_FIN : h.b0 & ~BHB0_FIN); +} + +/// check whether the frame's RSV1 bit is set +/** + * @param [in] h The basic header to extract from. + * @return True if the header's RSV1 bit is set. + */ +inline bool get_rsv1(const basic_header &h) { + return ((h.b0 & BHB0_RSV1) == BHB0_RSV1); +} + +/// Set the frame's RSV1 bit +/** + * @param [out] h Header to set. + * @param [in] value Value to set it to. + */ +inline void set_rsv1(basic_header &h, bool value) { + h.b0 = (value ? h.b0 | BHB0_RSV1 : h.b0 & ~BHB0_RSV1); +} + +/// check whether the frame's RSV2 bit is set +/** + * @param [in] h The basic header to extract from. + * @return True if the header's RSV2 bit is set. + */ +inline bool get_rsv2(const basic_header &h) { + return ((h.b0 & BHB0_RSV2) == BHB0_RSV2); +} + +/// Set the frame's RSV2 bit +/** + * @param [out] h Header to set. + * @param [in] value Value to set it to. + */ +inline void set_rsv2(basic_header &h, bool value) { + h.b0 = (value ? h.b0 | BHB0_RSV2 : h.b0 & ~BHB0_RSV2); +} + +/// check whether the frame's RSV3 bit is set +/** + * @param [in] h The basic header to extract from. + * @return True if the header's RSV3 bit is set. + */ +inline bool get_rsv3(const basic_header &h) { + return ((h.b0 & BHB0_RSV3) == BHB0_RSV3); +} + +/// Set the frame's RSV3 bit +/** + * @param [out] h Header to set. + * @param [in] value Value to set it to. + */ +inline void set_rsv3(basic_header &h, bool value) { + h.b0 = (value ? h.b0 | BHB0_RSV3 : h.b0 & ~BHB0_RSV3); +} + +/// Extract opcode from basic header +/** + * @param [in] h The basic header to extract from. + * @return The opcode value of the header. + */ +inline opcode::value get_opcode(const basic_header &h) { + return opcode::value(h.b0 & BHB0_OPCODE); +} + +/// check whether the frame is masked +/** + * @param [in] h The basic header to extract from. + * @return True if the header mask bit is set. + */ +inline bool get_masked(basic_header const & h) { + return ((h.b1 & BHB1_MASK) == BHB1_MASK); +} + +/// Set the frame's MASK bit +/** + * @param [out] h Header to set. + * @param value Value to set it to. + */ +inline void set_masked(basic_header & h, bool value) { + h.b1 = (value ? h.b1 | BHB1_MASK : h.b1 & ~BHB1_MASK); +} + +/// Extracts the raw payload length specified in the basic header +/** + * A basic WebSocket frame header contains a 7 bit value that represents the + * payload size. There are two reserved values that are used to indicate that + * the actual payload size will not fit in 7 bits and that the full payload + * size is included in a separate field. The values are as follows: + * + * PAYLOAD_SIZE_CODE_16BIT (0x7E) indicates that the actual payload is less + * than 16 bit + * + * PAYLOAD_SIZE_CODE_64BIT (0x7F) indicates that the actual payload is less + * than 63 bit + * + * @param [in] h Basic header to read value from. + * @return The exact size encoded in h. + */ +inline uint8_t get_basic_size(const basic_header &h) { + return h.b1 & BHB1_PAYLOAD; +} + +/// Calculates the full length of the header based on the first bytes. +/** + * A WebSocket frame header always has at least two bytes. Encoded within the + * first two bytes is all the information necessary to calculate the full + * (variable) header length. get_header_len() calculates the full header + * length for the given two byte basic header. + * + * @param h Basic frame header to extract size from. + * @return Full length of the extended header. + */ +inline size_t get_header_len(basic_header const & h) { + // TODO: check extensions? + + // masking key offset represents the space used for the extended length + // fields + size_t size = BASIC_HEADER_LENGTH + get_masking_key_offset(h); + + // If the header is masked there is a 4 byte masking key + if (get_masked(h)) { + size += 4; + } + + return size; +} + +/// Calculate the offset location of the masking key within the extended header +/** + * Calculate the offset location of the masking key within the extended header + * using information from its corresponding basic header + * + * @param h Corresponding basic header to calculate from. + * + * @return byte offset of the first byte of the masking key + */ +inline unsigned int get_masking_key_offset(const basic_header &h) { + if (get_basic_size(h) == payload_size_code_16bit) { + return 2; + } else if (get_basic_size(h) == payload_size_code_64bit) { + return 8; + } else { + return 0; + } +} + +/// Generate a properly sized contiguous string that encodes a full frame header +/** + * Copy the basic header h and extended header e into a properly sized + * contiguous frame header string for the purposes of writing out to the wire. + * + * @param h The basic header to include + * @param e The extended header to include + * + * @return A contiguous string containing h and e + */ +inline std::string prepare_header(const basic_header &h, const + extended_header &e) +{ + std::string ret; + + ret.push_back(char(h.b0)); + ret.push_back(char(h.b1)); + ret.append( + reinterpret_cast(e.bytes), + get_header_len(h)-BASIC_HEADER_LENGTH + ); + + return ret; +} + +/// Extract the masking key from a frame header +/** + * Note that while read and written as an integer at times, this value is not + * an integer and should never be interpreted as one. Big and little endian + * machines will generate and store masking keys differently without issue as + * long as the integer values remain irrelivant. + * + * @param h The basic header to extract from + * @param e The extended header to extract from + * + * @return The masking key as an integer. + */ +inline masking_key_type get_masking_key(const basic_header &h, const + extended_header &e) +{ + masking_key_type temp32; + + if (!get_masked(h)) { + temp32.i = 0; + } else { + unsigned int offset = get_masking_key_offset(h); + std::copy(e.bytes+offset,e.bytes+offset+4,temp32.c); + } + + return temp32; +} + +/// Extract the extended size field from an extended header +/** + * It is the responsibility of the caller to verify that e is a valid extended + * header. This function assumes that e contains an extended payload size. + * + * @param e The extended header to extract from + * + * @return The size encoded in the extended header in host byte order + */ +inline uint16_t get_extended_size(const extended_header &e) { + uint16_converter temp16; + std::copy(e.bytes,e.bytes+2,temp16.c); + return ntohs(temp16.i); +} + +/// Extract the jumbo size field from an extended header +/** + * It is the responsibility of the caller to verify that e is a valid extended + * header. This function assumes that e contains a jumbo payload size. + * + * @param e The extended header to extract from + * + * @return The size encoded in the extended header in host byte order + */ +inline uint64_t get_jumbo_size(const extended_header &e) { + uint64_converter temp64; + std::copy(e.bytes,e.bytes+8,temp64.c); + return lib::net::_ntohll(temp64.i); +} + +/// Extract the full payload size field from a WebSocket header +/** + * It is the responsibility of the caller to verify that h and e together + * represent a valid WebSocket frame header. This function assumes only that h + * and e are valid. It uses information in the basic header to determine where + * to look for the payload_size + * + * @param h The basic header to extract from + * @param e The extended header to extract from + * + * @return The size encoded in the combined header in host byte order. + */ +inline uint64_t get_payload_size(const basic_header &h, const + extended_header &e) +{ + uint8_t val = get_basic_size(h); + + if (val <= limits::payload_size_basic) { + return val; + } else if (val == payload_size_code_16bit) { + return get_extended_size(e); + } else { + return get_jumbo_size(e); + } +} + +/// Extract a masking key into a value the size of a machine word. +/** + * Machine word size must be 4 or 8. + * + * @param key Masking key to extract from + * + * @return prepared key as a machine word + */ +inline size_t prepare_masking_key(const masking_key_type& key) { + size_t low_bits = static_cast(key.i); + + if (sizeof(size_t) == 8) { + uint64_t high_bits = static_cast(key.i); + return static_cast((high_bits << 32) | low_bits); + } else { + return low_bits; + } +} + +/// circularly shifts the supplied prepared masking key by offset bytes +/** + * Prepared_key must be the output of prepare_masking_key with the associated + * restrictions on the machine word size. offset must be greater than or equal + * to zero and less than sizeof(size_t). + */ +inline size_t circshift_prepared_key(size_t prepared_key, size_t offset) { + if (lib::net::is_little_endian()) { + size_t temp = prepared_key << (sizeof(size_t)-offset)*8; + return (prepared_key >> offset*8) | temp; + } else { + size_t temp = prepared_key >> (sizeof(size_t)-offset)*8; + return (prepared_key << offset*8) | temp; + } +} + +/// Byte by byte mask/unmask +/** + * Iterator based byte by byte masking and unmasking for WebSocket payloads. + * Performs masking in place using the supplied key offset by the supplied + * offset number of bytes. + * + * This function is simple and can be done in place on input with arbitrary + * lengths and does not vary based on machine word size. It is slow. + * + * @param b Beginning iterator to start masking + * + * @param e Ending iterator to end masking + * + * @param o Beginning iterator to store masked results + * + * @param key 32 bit key to mask with. + * + * @param key_offset offset value to start masking at. + */ +template +void byte_mask(input_iter first, input_iter last, output_iter result, + masking_key_type const & key, size_t key_offset) +{ + size_t key_index = key_offset%4; + while (first != last) { + *result = *first ^ key.c[key_index++]; + key_index %= 4; + ++result; + ++first; + } +} + +/// Byte by byte mask/unmask (in place) +/** + * Iterator based byte by byte masking and unmasking for WebSocket payloads. + * Performs masking in place using the supplied key offset by the supplied + * offset number of bytes. + * + * This function is simple and can be done in place on input with arbitrary + * lengths and does not vary based on machine word size. It is slow. + * + * @param b Beginning iterator to start masking + * + * @param e Ending iterator to end masking + * + * @param key 32 bit key to mask with. + * + * @param key_offset offset value to start masking at. + */ +template +void byte_mask(iter_type b, iter_type e, masking_key_type const & key, + size_t key_offset) +{ + byte_mask(b,e,b,key,key_offset); +} + +/// Exact word aligned mask/unmask +/** + * Balanced combination of byte by byte and circular word by word masking. + * Best used to mask complete messages at once. Has much higher setup costs than + * word_mask_circ but works with exact sized buffers. + * + * Buffer based word by word masking and unmasking for WebSocket payloads. + * Masking is done in word by word chunks with the remainder not divisible by + * the word size done byte by byte. + * + * input and output must both be at least length bytes. Exactly length bytes + * will be written. + * + * @param input buffer to mask or unmask + * + * @param output buffer to store the output. May be the same as input. + * + * @param length length of data buffer + * + * @param key Masking key to use + */ +inline void word_mask_exact(uint8_t* input, uint8_t* output, size_t length, + const masking_key_type& key) +{ + size_t prepared_key = prepare_masking_key(key); + size_t n = length/sizeof(size_t); + size_t* input_word = reinterpret_cast(input); + size_t* output_word = reinterpret_cast(output); + + for (size_t i = 0; i < n; i++) { + output_word[i] = input_word[i] ^ prepared_key; + } + + for (size_t i = n*sizeof(size_t); i < length; i++) { + output[i] = input[i] ^ key.c[i%4]; + } +} + +/// Exact word aligned mask/unmask (in place) +/** + * In place version of word_mask_exact + * + * @see word_mask_exact + * + * @param data buffer to read and write from + * + * @param length length of data buffer + * + * @param key Masking key to use + */ +inline void word_mask_exact(uint8_t* data, size_t length, const + masking_key_type& key) +{ + word_mask_exact(data,data,length,key); +} + +/// Circular word aligned mask/unmask +/** + * Performs a circular mask/unmask in word sized chunks using pre-prepared keys + * that store state between calls. Best for providing streaming masking or + * unmasking of small chunks at a time of a larger message. Requires that the + * underlying allocated size of the data buffer be a multiple of the word size. + * Data in the buffer after `length` will be overwritten only with the same + * values that were originally present. + * + * Buffer based word by word masking and unmasking for WebSocket payloads. + * Performs masking in place using the supplied key. Casts the data buffer to + * an array of size_t's and performs masking word by word. The underlying + * buffer size must be a muliple of the word size. + * + * word_mask returns a copy of prepared_key circularly shifted based on the + * length value. The returned value may be fed back into word_mask when more + * data is available. + * + * input and output must both have length at least: + * ceil(length/sizeof(size_t))*sizeof(size_t) + * Exactly that many bytes will be written, although only exactly length bytes + * will be changed (trailing bytes will be replaced without masking) + * + * @param data Character buffer to mask + * + * @param length Length of data + * + * @param prepared_key Prepared key to use. + * + * @return the prepared_key shifted to account for the input length + */ +inline size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length, + size_t prepared_key) +{ + size_t n = length / sizeof(size_t); // whole words + size_t l = length - (n * sizeof(size_t)); // remaining bytes + size_t * input_word = reinterpret_cast(input); + size_t * output_word = reinterpret_cast(output); + + // mask word by word + for (size_t i = 0; i < n; i++) { + output_word[i] = input_word[i] ^ prepared_key; + } + + // mask partial word at the end + size_t start = length - l; + uint8_t * byte_key = reinterpret_cast(&prepared_key); + for (size_t i = 0; i < l; ++i) { + output[start+i] = input[start+i] ^ byte_key[i]; + } + + return circshift_prepared_key(prepared_key,l); +} + +/// Circular word aligned mask/unmask (in place) +/** + * In place version of word_mask_circ + * + * @see word_mask_circ + * + * @param data Character buffer to read from and write to + * + * @param length Length of data + * + * @param prepared_key Prepared key to use. + * + * @return the prepared_key shifted to account for the input length + */ +inline size_t word_mask_circ(uint8_t* data, size_t length, size_t prepared_key){ + return word_mask_circ(data,data,length,prepared_key); +} + +/// Circular byte aligned mask/unmask +/** + * Performs a circular mask/unmask in byte sized chunks using pre-prepared keys + * that store state between calls. Best for providing streaming masking or + * unmasking of small chunks at a time of a larger message. Requires that the + * underlying allocated size of the data buffer be a multiple of the word size. + * Data in the buffer after `length` will be overwritten only with the same + * values that were originally present. + * + * word_mask returns a copy of prepared_key circularly shifted based on the + * length value. The returned value may be fed back into byte_mask when more + * data is available. + * + * @param data Character buffer to mask + * + * @param length Length of data + * + * @param prepared_key Prepared key to use. + * + * @return the prepared_key shifted to account for the input length + */ +inline size_t byte_mask_circ(uint8_t * input, uint8_t * output, size_t length, + size_t prepared_key) +{ + uint32_converter key; + key.i = prepared_key; + + for (size_t i = 0; i < length; ++i) { + output[i] = input[i] ^ key.c[i % 4]; + } + + return circshift_prepared_key(prepared_key,length % 4); +} + +/// Circular byte aligned mask/unmask (in place) +/** + * In place version of byte_mask_circ + * + * @see byte_mask_circ + * + * @param data Character buffer to read from and write to + * + * @param length Length of data + * + * @param prepared_key Prepared key to use. + * + * @return the prepared_key shifted to account for the input length + */ +inline size_t byte_mask_circ(uint8_t* data, size_t length, size_t prepared_key){ + return byte_mask_circ(data,data,length,prepared_key); +} + +} // namespace frame +} // namespace websocketpp + +#endif //WEBSOCKETPP_FRAME_HPP diff --git a/websocketpp/http/constants.hpp b/websocketpp/http/constants.hpp new file mode 100644 index 00000000..f946cb31 --- /dev/null +++ b/websocketpp/http/constants.hpp @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_CONSTANTS_HPP +#define HTTP_CONSTANTS_HPP + +#include +#include +#include +#include +#include + +namespace websocketpp { +/// HTTP handling support +namespace http { + /// The type of an HTTP attribute list + /** + * The attribute list is an unordered key/value map. Encoded attribute + * values are delimited by semicolons. + */ + typedef std::map attribute_list; + + /// The type of an HTTP parameter list + /** + * The parameter list is an ordered pairing of a parameter and its + * associated attribute list. Encoded parameter values are delimited by + * commas. + */ + typedef std::vector< std::pair > parameter_list; + + /// Literal value of the HTTP header delimiter + static char const header_delimiter[] = "\r\n"; + + /// Literal value of the HTTP header separator + static char const header_separator[] = ":"; + + /// Literal value of an empty header + static std::string const empty_header; + + /// Maximum size in bytes before rejecting an HTTP header as too big. + size_t const max_header_size = 16000; + + /// Default Maximum size in bytes for HTTP message bodies. + size_t const max_body_size = 32000000; + + /// Number of bytes to use for temporary istream read buffers + size_t const istream_buffer = 512; + + /// invalid HTTP token characters + /** + * 0x00 - 0x32, 0x7f-0xff + * ( ) < > @ , ; : \ " / [ ] ? = { } + */ + static char const header_token[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..0f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 10..1f + 0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0, // 20..2f + 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, // 30..3f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 40..4f + 1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1, // 50..5f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 60..6f + 1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0, // 70..7f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 80..8f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 90..9f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // a0..af + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // b0..bf + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c0..cf + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // d0..df + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // e0..ef + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // f0..ff + }; + + /// Is the character a token + inline bool is_token_char(unsigned char c) { + return (header_token[c] == 1); + } + + /// Is the character a non-token + inline bool is_not_token_char(unsigned char c) { + return !header_token[c]; + } + + /// Is the character whitespace + /** + * whitespace is space (32) or horizontal tab (9) + */ + inline bool is_whitespace_char(unsigned char c) { + return (c == 9 || c == 32); + } + + /// Is the character non-whitespace + inline bool is_not_whitespace_char(unsigned char c) { + return (c != 9 && c != 32); + } + + /// HTTP Status codes + namespace status_code { + enum value { + uninitialized = 0, + + continue_code = 100, + switching_protocols = 101, + + ok = 200, + created = 201, + accepted = 202, + non_authoritative_information = 203, + no_content = 204, + reset_content = 205, + partial_content = 206, + + multiple_choices = 300, + moved_permanently = 301, + found = 302, + see_other = 303, + not_modified = 304, + use_proxy = 305, + temporary_redirect = 307, + + bad_request = 400, + unauthorized = 401, + payment_required = 402, + forbidden = 403, + not_found = 404, + method_not_allowed = 405, + not_acceptable = 406, + proxy_authentication_required = 407, + request_timeout = 408, + conflict = 409, + gone = 410, + length_required = 411, + precondition_failed = 412, + request_entity_too_large = 413, + request_uri_too_long = 414, + unsupported_media_type = 415, + request_range_not_satisfiable = 416, + expectation_failed = 417, + im_a_teapot = 418, + upgrade_required = 426, + precondition_required = 428, + too_many_requests = 429, + request_header_fields_too_large = 431, + + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503, + gateway_timeout = 504, + http_version_not_supported = 505, + not_extended = 510, + network_authentication_required = 511 + }; + + // TODO: should this be inline? + inline std::string get_string(value c) { + switch (c) { + case uninitialized: + return "Uninitialized"; + case continue_code: + return "Continue"; + case switching_protocols: + return "Switching Protocols"; + case ok: + return "OK"; + case created: + return "Created"; + case accepted: + return "Accepted"; + case non_authoritative_information: + return "Non Authoritative Information"; + case no_content: + return "No Content"; + case reset_content: + return "Reset Content"; + case partial_content: + return "Partial Content"; + case multiple_choices: + return "Multiple Choices"; + case moved_permanently: + return "Moved Permanently"; + case found: + return "Found"; + case see_other: + return "See Other"; + case not_modified: + return "Not Modified"; + case use_proxy: + return "Use Proxy"; + case temporary_redirect: + return "Temporary Redirect"; + case bad_request: + return "Bad Request"; + case unauthorized: + return "Unauthorized"; + case payment_required: + return "Payment Required"; + case forbidden: + return "Forbidden"; + case not_found: + return "Not Found"; + case method_not_allowed: + return "Method Not Allowed"; + case not_acceptable: + return "Not Acceptable"; + case proxy_authentication_required: + return "Proxy Authentication Required"; + case request_timeout: + return "Request Timeout"; + case conflict: + return "Conflict"; + case gone: + return "Gone"; + case length_required: + return "Length Required"; + case precondition_failed: + return "Precondition Failed"; + case request_entity_too_large: + return "Request Entity Too Large"; + case request_uri_too_long: + return "Request-URI Too Long"; + case unsupported_media_type: + return "Unsupported Media Type"; + case request_range_not_satisfiable: + return "Requested Range Not Satisfiable"; + case expectation_failed: + return "Expectation Failed"; + case im_a_teapot: + return "I'm a teapot"; + case upgrade_required: + return "Upgrade Required"; + case precondition_required: + return "Precondition Required"; + case too_many_requests: + return "Too Many Requests"; + case request_header_fields_too_large: + return "Request Header Fields Too Large"; + case internal_server_error: + return "Internal Server Error"; + case not_implemented: + return "Not Implemented"; + case bad_gateway: + return "Bad Gateway"; + case service_unavailable: + return "Service Unavailable"; + case gateway_timeout: + return "Gateway Timeout"; + case http_version_not_supported: + return "HTTP Version Not Supported"; + case not_extended: + return "Not Extended"; + case network_authentication_required: + return "Network Authentication Required"; + default: + return "Unknown"; + } + } + } + + class exception : public std::exception { + public: + exception(const std::string& log_msg, + status_code::value error_code, + const std::string& error_msg = std::string(), + const std::string& body = std::string()) + : m_msg(log_msg) + , m_error_msg(error_msg) + , m_body(body) + , m_error_code(error_code) {} + + ~exception() throw() {} + + virtual const char* what() const throw() { + return m_msg.c_str(); + } + + std::string m_msg; + std::string m_error_msg; + std::string m_body; + status_code::value m_error_code; + }; +} +} + +#endif // HTTP_CONSTANTS_HPP diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp new file mode 100644 index 00000000..1d59b938 --- /dev/null +++ b/websocketpp/http/impl/parser.hpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_IMPL_HPP +#define HTTP_PARSER_IMPL_HPP + +#include +#include +#include +#include +#include + +namespace websocketpp { +namespace http { +namespace parser { + +inline void parser::set_version(std::string const & version) { + m_version = version; +} + +inline std::string const & parser::get_header(std::string const & key) const { + header_list::const_iterator h = m_headers.find(key); + + if (h == m_headers.end()) { + return empty_header; + } else { + return h->second; + } +} + +inline bool parser::get_header_as_plist(std::string const & key, + parameter_list & out) const +{ + header_list::const_iterator it = m_headers.find(key); + + if (it == m_headers.end() || it->second.size() == 0) { + return false; + } + + return this->parse_parameter_list(it->second,out); +} + +inline void parser::append_header(std::string const & key, std::string const & + val) +{ + if (std::find_if(key.begin(),key.end(),is_not_token_char) != key.end()) { + throw exception("Invalid header name",status_code::bad_request); + } + + if (this->get_header(key).empty()) { + m_headers[key] = val; + } else { + m_headers[key] += ", " + val; + } +} + +inline void parser::replace_header(std::string const & key, std::string const & + val) +{ + m_headers[key] = val; +} + +inline void parser::remove_header(std::string const & key) { + m_headers.erase(key); +} + +inline void parser::set_body(std::string const & value) { + if (value.size() == 0) { + remove_header("Content-Length"); + m_body.clear(); + return; + } + + // TODO: should this method respect the max size? If so how should errors + // be indicated? + + std::stringstream len; + len << value.size(); + replace_header("Content-Length", len.str()); + m_body = value; +} + +inline bool parser::parse_parameter_list(std::string const & in, + parameter_list & out) const +{ + if (in.size() == 0) { + return false; + } + + std::string::const_iterator it; + it = extract_parameters(in.begin(),in.end(),out); + return (it == in.begin()); +} + +inline bool parser::prepare_body() { + if (!get_header("Content-Length").empty()) { + std::string const & cl_header = get_header("Content-Length"); + char * end; + + // TODO: not 100% sure what the compatibility of this method is. Also, + // I believe this will only work up to 32bit sizes. Is there a need for + // > 4GiB HTTP payloads? + m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10); + + if (m_body_bytes_needed > m_body_bytes_max) { + throw exception("HTTP message body too large", + status_code::request_entity_too_large); + } + + m_body_encoding = body_encoding::plain; + return true; + } else if (get_header("Transfer-Encoding") == "chunked") { + // TODO + //m_body_encoding = body_encoding::chunked; + return false; + } else { + return false; + } +} + +inline size_t parser::process_body(char const * buf, size_t len) { + if (m_body_encoding == body_encoding::plain) { + size_t processed = (std::min)(m_body_bytes_needed,len); + m_body.append(buf,processed); + m_body_bytes_needed -= processed; + return processed; + } else if (m_body_encoding == body_encoding::chunked) { + // TODO: + throw exception("Unexpected body encoding", + status_code::internal_server_error); + } else { + throw exception("Unexpected body encoding", + status_code::internal_server_error); + } +} + +inline void parser::process_header(std::string::iterator begin, + std::string::iterator end) +{ + std::string::iterator cursor = std::search( + begin, + end, + header_separator, + header_separator + sizeof(header_separator) - 1 + ); + + if (cursor == end) { + throw exception("Invalid header line",status_code::bad_request); + } + + append_header(strip_lws(std::string(begin,cursor)), + strip_lws(std::string(cursor+sizeof(header_separator)-1,end))); +} + +inline std::string parser::raw_headers() const { + std::stringstream raw; + + header_list::const_iterator it; + for (it = m_headers.begin(); it != m_headers.end(); it++) { + raw << it->first << ": " << it->second << "\r\n"; + } + + return raw.str(); +} + + + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#endif // HTTP_PARSER_IMPL_HPP diff --git a/websocketpp/http/impl/request.hpp b/websocketpp/http/impl/request.hpp new file mode 100644 index 00000000..311a620f --- /dev/null +++ b/websocketpp/http/impl/request.hpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_REQUEST_IMPL_HPP +#define HTTP_PARSER_REQUEST_IMPL_HPP + +#include +#include +#include + +#include + +namespace websocketpp { +namespace http { +namespace parser { + +inline size_t request::consume(char const * buf, size_t len) { + size_t bytes_processed; + + if (m_ready) {return 0;} + + if (m_body_bytes_needed > 0) { + bytes_processed = process_body(buf,len); + if (body_ready()) { + m_ready = true; + } + return bytes_processed; + } + + // copy new header bytes into buffer + m_buf->append(buf,len); + + // Search for delimiter in buf. If found read until then. If not read all + std::string::iterator begin = m_buf->begin(); + std::string::iterator end; + + for (;;) { + // search for line delimiter + end = std::search( + begin, + m_buf->end(), + header_delimiter, + header_delimiter+sizeof(header_delimiter)-1 + ); + + m_header_bytes += (end-begin+sizeof(header_delimiter)); + + if (m_header_bytes > max_header_size) { + // exceeded max header size + throw exception("Maximum header size exceeded.", + status_code::request_header_fields_too_large); + } + + if (end == m_buf->end()) { + // we are out of bytes. Discard the processed bytes and copy the + // remaining unprecessed bytes to the beginning of the buffer + std::copy(begin,end,m_buf->begin()); + m_buf->resize(static_cast(end-begin)); + m_header_bytes -= m_buf->size(); + + return len; + } + + //the range [begin,end) now represents a line to be processed. + if (end-begin == 0) { + // we got a blank line + if (m_method.empty() || get_header("Host").empty()) { + throw exception("Incomplete Request",status_code::bad_request); + } + + bytes_processed = ( + len - static_cast(m_buf->end()-end) + + sizeof(header_delimiter) - 1 + ); + + // frees memory used temporarily during request parsing + m_buf.reset(); + + // if this was not an upgrade request and has a content length + // continue capturing content-length bytes and expose them as a + // request body. + + if (prepare_body()) { + bytes_processed += process_body(buf+bytes_processed,len-bytes_processed); + if (body_ready()) { + m_ready = true; + } + return bytes_processed; + } else { + m_ready = true; + + // return number of bytes processed (starting bytes - bytes left) + return bytes_processed; + } + } else { + if (m_method.empty()) { + this->process(begin,end); + } else { + this->process_header(begin,end); + } + } + + begin = end+(sizeof(header_delimiter)-1); + } +} + +inline std::string request::raw() const { + // TODO: validation. Make sure all required fields have been set? + std::stringstream ret; + + ret << m_method << " " << m_uri << " " << get_version() << "\r\n"; + ret << raw_headers() << "\r\n" << m_body; + + return ret.str(); +} + +inline std::string request::raw_head() const { + // TODO: validation. Make sure all required fields have been set? + std::stringstream ret; + + ret << m_method << " " << m_uri << " " << get_version() << "\r\n"; + ret << raw_headers() << "\r\n"; + + return ret.str(); +} + +inline void request::set_method(std::string const & method) { + if (std::find_if(method.begin(),method.end(),is_not_token_char) != method.end()) { + throw exception("Invalid method token.",status_code::bad_request); + } + + m_method = method; +} + +inline void request::set_uri(std::string const & uri) { + // TODO: validation? + m_uri = uri; +} + +inline void request::process(std::string::iterator begin, std::string::iterator + end) +{ + std::string::iterator cursor_start = begin; + std::string::iterator cursor_end = std::find(begin,end,' '); + + if (cursor_end == end) { + throw exception("Invalid request line1",status_code::bad_request); + } + + set_method(std::string(cursor_start,cursor_end)); + + cursor_start = cursor_end+1; + cursor_end = std::find(cursor_start,end,' '); + + if (cursor_end == end) { + throw exception("Invalid request line2",status_code::bad_request); + } + + set_uri(std::string(cursor_start,cursor_end)); + set_version(std::string(cursor_end+1,end)); +} + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#endif // HTTP_PARSER_REQUEST_IMPL_HPP diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp new file mode 100644 index 00000000..4400cda5 --- /dev/null +++ b/websocketpp/http/impl/response.hpp @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_RESPONSE_IMPL_HPP +#define HTTP_PARSER_RESPONSE_IMPL_HPP + +#include +#include +#include +#include + +#include + +namespace websocketpp { +namespace http { +namespace parser { + +inline size_t response::consume(char const * buf, size_t len) { + if (m_state == DONE) {return 0;} + + if (m_state == BODY) { + return this->process_body(buf,len); + } + + // copy new header bytes into buffer + m_buf->append(buf,len); + + // Search for delimiter in buf. If found read until then. If not read all + std::string::iterator begin = m_buf->begin(); + std::string::iterator end = begin; + + + for (;;) { + // search for delimiter + end = std::search( + begin, + m_buf->end(), + header_delimiter, + header_delimiter + sizeof(header_delimiter) - 1 + ); + + m_header_bytes += (end-begin+sizeof(header_delimiter)); + + if (m_header_bytes > max_header_size) { + // exceeded max header size + throw exception("Maximum header size exceeded.", + status_code::request_header_fields_too_large); + } + + if (end == m_buf->end()) { + // we are out of bytes. Discard the processed bytes and copy the + // remaining unprecessed bytes to the beginning of the buffer + std::copy(begin,end,m_buf->begin()); + m_buf->resize(static_cast(end-begin)); + + m_read += len; + m_header_bytes -= m_buf->size(); + + return len; + } + + //the range [begin,end) now represents a line to be processed. + + if (end-begin == 0) { + // we got a blank line + if (m_state == RESPONSE_LINE) { + throw exception("Incomplete Request",status_code::bad_request); + } + + // TODO: grab content-length + std::string length = get_header("Content-Length"); + + if (length.empty()) { + // no content length found, read indefinitely + m_read = 0; + } else { + std::istringstream ss(length); + + if ((ss >> m_read).fail()) { + throw exception("Unable to parse Content-Length header", + status_code::bad_request); + } + } + + m_state = BODY; + + // calc header bytes processed (starting bytes - bytes left) + size_t read = ( + len - static_cast(m_buf->end() - end) + + sizeof(header_delimiter) - 1 + ); + + // if there were bytes left process them as body bytes + if (read < len) { + read += this->process_body(buf+read,(len-read)); + } + + // frees memory used temporarily during header parsing + m_buf.reset(); + + return read; + } else { + if (m_state == RESPONSE_LINE) { + this->process(begin,end); + m_state = HEADERS; + } else { + this->process_header(begin,end); + } + } + + begin = end+(sizeof(header_delimiter) - 1); + } +} + +inline size_t response::consume(std::istream & s) { + char buf[istream_buffer]; + size_t bytes_read; + size_t bytes_processed; + size_t total = 0; + + while (s.good()) { + s.getline(buf,istream_buffer); + bytes_read = static_cast(s.gcount()); + + if (s.fail() || s.eof()) { + bytes_processed = this->consume(buf,bytes_read); + total += bytes_processed; + + if (bytes_processed != bytes_read) { + // problem + break; + } + } else if (s.bad()) { + // problem + break; + } else { + // the delimiting newline was found. Replace the trailing null with + // the newline that was discarded, since our raw consume function + // expects the newline to be be there. + buf[bytes_read-1] = '\n'; + bytes_processed = this->consume(buf,bytes_read); + total += bytes_processed; + + if (bytes_processed != bytes_read) { + // problem + break; + } + } + } + + return total; +} + +inline std::string response::raw() const { + // TODO: validation. Make sure all required fields have been set? + + std::stringstream ret; + + ret << get_version() << " " << m_status_code << " " << m_status_msg; + ret << "\r\n" << raw_headers() << "\r\n"; + + ret << m_body; + + return ret.str(); +} + +inline void response::set_status(status_code::value code) { + // TODO: validation? + m_status_code = code; + m_status_msg = get_string(code); +} + +inline void response::set_status(status_code::value code, std::string const & + msg) +{ + // TODO: validation? + m_status_code = code; + m_status_msg = msg; +} + +inline void response::process(std::string::iterator begin, + std::string::iterator end) +{ + std::string::iterator cursor_start = begin; + std::string::iterator cursor_end = std::find(begin,end,' '); + + if (cursor_end == end) { + throw exception("Invalid response line",status_code::bad_request); + } + + set_version(std::string(cursor_start,cursor_end)); + + cursor_start = cursor_end+1; + cursor_end = std::find(cursor_start,end,' '); + + if (cursor_end == end) { + throw exception("Invalid request line",status_code::bad_request); + } + + int code; + + std::istringstream ss(std::string(cursor_start,cursor_end)); + + if ((ss >> code).fail()) { + throw exception("Unable to parse response code",status_code::bad_request); + } + + set_status(status_code::value(code),std::string(cursor_end+1,end)); +} + +inline size_t response::process_body(char const * buf, size_t len) { + // If no content length was set then we read forever and never set m_ready + if (m_read == 0) { + //m_body.append(buf,len); + //return len; + m_state = DONE; + return 0; + } + + // Otherwise m_read is the number of bytes left. + size_t to_read; + + if (len >= m_read) { + // if we have more bytes than we need read, read only the amount needed + // then set done state + to_read = m_read; + m_state = DONE; + } else { + // we need more bytes than are available, read them all + to_read = len; + } + + m_body.append(buf,to_read); + m_read -= to_read; + return to_read; +} + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#endif // HTTP_PARSER_RESPONSE_IMPL_HPP diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp new file mode 100644 index 00000000..90f49ebe --- /dev/null +++ b/websocketpp/http/parser.hpp @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_HPP +#define HTTP_PARSER_HPP + +#include +#include +#include +#include + +#include +#include + +namespace websocketpp { +namespace http { +namespace parser { + +namespace state { + enum value { + method, + resource, + version, + headers + }; +} + +namespace body_encoding { + enum value { + unknown, + plain, + chunked + }; +} + +typedef std::map header_list; + +/// Read and return the next token in the stream +/** + * Read until a non-token character is found and then return the token and + * iterator to the next character to read + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return A pair containing the token and an iterator to the next character in + * the stream + */ +template +std::pair extract_token(InputIterator begin, + InputIterator end) +{ + InputIterator it = std::find_if(begin,end,&is_not_token_char); + return std::make_pair(std::string(begin,it),it); +} + +/// Read and return the next quoted string in the stream +/** + * Read a double quoted string starting at `begin`. The quotes themselves are + * stripped. The quoted value is returned along with an iterator to the next + * character to read + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return A pair containing the string read and an iterator to the next + * character in the stream + */ +template +std::pair extract_quoted_string(InputIterator begin, + InputIterator end) +{ + std::string s; + + if (end == begin) { + return std::make_pair(s,begin); + } + + if (*begin != '"') { + return std::make_pair(s,begin); + } + + InputIterator cursor = begin+1; + InputIterator marker = cursor; + + cursor = std::find(cursor,end,'"'); + + while (cursor != end) { + // either this is the end or a quoted string + if (*(cursor-1) == '\\') { + s.append(marker,cursor-1); + s.append(1,'"'); + ++cursor; + marker = cursor; + } else { + s.append(marker,cursor); + ++cursor; + return std::make_pair(s,cursor); + } + + cursor = std::find(cursor,end,'"'); + } + + return std::make_pair("",begin); +} + +/// Read and discard one unit of linear whitespace +/** + * Read one unit of linear white space and return the iterator to the character + * afterwards. If `begin` is returned, no whitespace was extracted. + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return An iterator to the character after the linear whitespace read + */ +template +InputIterator extract_lws(InputIterator begin, InputIterator end) { + InputIterator it = begin; + + // strip leading CRLF + if (end-begin > 2 && *begin == '\r' && *(begin+1) == '\n' && + is_whitespace_char(static_cast(*(begin+2)))) + { + it+=3; + } + + it = std::find_if(it,end,&is_not_whitespace_char); + return it; +} + +/// Read and discard linear whitespace +/** + * Read linear white space until a non-lws character is read and return an + * iterator to that character. If `begin` is returned, no whitespace was + * extracted. + * + * @param begin An iterator to the beginning of the sequence + * @param end An iterator to the end of the sequence + * @return An iterator to the character after the linear whitespace read + */ +template +InputIterator extract_all_lws(InputIterator begin, InputIterator end) { + InputIterator old_it; + InputIterator new_it = begin; + + do { + // Pull value from previous iteration + old_it = new_it; + + // look ahead another pass + new_it = extract_lws(old_it,end); + } while (new_it != end && old_it != new_it); + + return new_it; +} + +/// Extract HTTP attributes +/** + * An http attributes list is a semicolon delimited list of key value pairs in + * the format: *( ";" attribute "=" value ) where attribute is a token and value + * is a token or quoted string. + * + * Attributes extracted are appended to the supplied attributes list + * `attributes`. + * + * @param [in] begin An iterator to the beginning of the sequence + * @param [in] end An iterator to the end of the sequence + * @param [out] attributes A reference to the attributes list to append + * attribute/value pairs extracted to + * @return An iterator to the character after the last atribute read + */ +template +InputIterator extract_attributes(InputIterator begin, InputIterator end, + attribute_list & attributes) +{ + InputIterator cursor; + bool first = true; + + if (begin == end) { + return begin; + } + + cursor = begin; + std::pair ret; + + while (cursor != end) { + std::string name; + + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end) { + break; + } + + if (first) { + // ignore this check for the very first pass + first = false; + } else { + if (*cursor == ';') { + // advance past the ';' + ++cursor; + } else { + // non-semicolon in this position indicates end end of the + // attribute list, break and return. + break; + } + } + + cursor = http::parser::extract_all_lws(cursor,end); + ret = http::parser::extract_token(cursor,end); + + if (ret.first.empty()) { + // error: expected a token + return begin; + } else { + name = ret.first; + cursor = ret.second; + } + + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end || *cursor != '=') { + // if there is an equals sign, read the attribute value. Otherwise + // record a blank value and continue + attributes[name].clear(); + continue; + } + + // advance past the '=' + ++cursor; + + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end) { + // error: expected a token or quoted string + return begin; + } + + ret = http::parser::extract_quoted_string(cursor,end); + if (ret.second != cursor) { + attributes[name] = ret.first; + cursor = ret.second; + continue; + } + + ret = http::parser::extract_token(cursor,end); + if (ret.first.empty()) { + // error : expected token or quoted string + return begin; + } else { + attributes[name] = ret.first; + cursor = ret.second; + } + } + + return cursor; +} + +/// Extract HTTP parameters +/** + * An http parameters list is a comma delimited list of tokens followed by + * optional semicolon delimited attributes lists. + * + * Parameters extracted are appended to the supplied parameters list + * `parameters`. + * + * @param [in] begin An iterator to the beginning of the sequence + * @param [in] end An iterator to the end of the sequence + * @param [out] parameters A reference to the parameters list to append + * paramter values extracted to + * @return An iterator to the character after the last parameter read + */ +template +InputIterator extract_parameters(InputIterator begin, InputIterator end, + parameter_list ¶meters) +{ + InputIterator cursor; + + if (begin == end) { + // error: expected non-zero length range + return begin; + } + + cursor = begin; + std::pair ret; + + /** + * LWS + * token + * LWS + * *(";" method-param) + * LWS + * ,=loop again + */ + while (cursor != end) { + std::string parameter_name; + attribute_list attributes; + + // extract any stray whitespace + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end) {break;} + + ret = http::parser::extract_token(cursor,end); + + if (ret.first.empty()) { + // error: expected a token + return begin; + } else { + parameter_name = ret.first; + cursor = ret.second; + } + + // Safe break point, insert parameter with blank attributes and exit + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end) { + //parameters[parameter_name] = attributes; + parameters.push_back(std::make_pair(parameter_name,attributes)); + break; + } + + // If there is an attribute list, read it in + if (*cursor == ';') { + InputIterator acursor; + + ++cursor; + acursor = http::parser::extract_attributes(cursor,end,attributes); + + if (acursor == cursor) { + // attribute extraction ended in syntax error + return begin; + } + + cursor = acursor; + } + + // insert parameter into output list + //parameters[parameter_name] = attributes; + parameters.push_back(std::make_pair(parameter_name,attributes)); + + cursor = http::parser::extract_all_lws(cursor,end); + if (cursor == end) {break;} + + // if next char is ',' then read another parameter, else stop + if (*cursor != ',') { + break; + } + + // advance past comma + ++cursor; + + if (cursor == end) { + // expected more bytes after a comma + return begin; + } + } + + return cursor; +} + +inline std::string strip_lws(std::string const & input) { + std::string::const_iterator begin = extract_all_lws(input.begin(),input.end()); + if (begin == input.end()) { + return std::string(); + } + + std::string::const_reverse_iterator rbegin = extract_all_lws(input.rbegin(),input.rend()); + if (rbegin == input.rend()) { + return std::string(); + } + + return std::string(begin,rbegin.base()); +} + +/// Base HTTP parser +/** + * Includes methods and data elements common to all types of HTTP messages such + * as headers, versions, bodies, etc. + */ +class parser { +public: + parser() + : m_header_bytes(0) + , m_body_bytes_needed(0) + , m_body_bytes_max(max_body_size) + , m_body_encoding(body_encoding::unknown) {} + + /// Get the HTTP version string + /** + * @return The version string for this parser + */ + std::string const & get_version() const { + return m_version; + } + + /// Set HTTP parser Version + /** + * Input should be in format: HTTP/x.y where x and y are positive integers. + * @todo Does this method need any validation? + * + * @param [in] version The value to set the HTTP version to. + */ + void set_version(std::string const & version); + + /// Get the value of an HTTP header + /** + * @todo Make this method case insensitive. + * + * @param [in] key The name/key of the header to get. + * @return The value associated with the given HTTP header key. + */ + std::string const & get_header(std::string const & key) const; + + /// Extract an HTTP parameter list from a parser header. + /** + * If the header requested doesn't exist or exists and is empty the + * parameter list is valid (but empty). + * + * @param [in] key The name/key of the HTTP header to use as input. + * @param [out] out The parameter list to store extracted parameters in. + * @return Whether or not the input was a valid parameter list. + */ + bool get_header_as_plist(std::string const & key, parameter_list & out) + const; + + /// Append a value to an existing HTTP header + /** + * This method will set the value of the HTTP header `key` with the + * indicated value. If a header with the name `key` already exists, `val` + * will be appended to the existing value. + * + * @todo Make this method case insensitive. + * @todo Should there be any restrictions on which keys are allowed? + * @todo Exception free varient + * + * @see replace_header + * + * @param [in] key The name/key of the header to append to. + * @param [in] val The value to append. + */ + void append_header(std::string const & key, std::string const & val); + + /// Set a value for an HTTP header, replacing an existing value + /** + * This method will set the value of the HTTP header `key` with the + * indicated value. If a header with the name `key` already exists, `val` + * will replace the existing value. + * + * @todo Make this method case insensitive. + * @todo Should there be any restrictions on which keys are allowed? + * @todo Exception free varient + * + * @see append_header + * + * @param [in] key The name/key of the header to append to. + * @param [in] val The value to append. + */ + void replace_header(std::string const & key, std::string const & val); + + /// Remove a header from the parser + /** + * Removes the header entirely from the parser. This is different than + * setting the value of the header to blank. + * + * @todo Make this method case insensitive. + * + * @param [in] key The name/key of the header to remove. + */ + void remove_header(std::string const & key); + + /// Get HTTP body + /** + * Gets the body of the HTTP object + * + * @return The body of the HTTP message. + */ + std::string const & get_body() const { + return m_body; + } + + /// Set body content + /** + * Set the body content of the HTTP response to the parameter string. Note + * set_body will also set the Content-Length HTTP header to the appropriate + * value. If you want the Content-Length header to be something else, do so + * via replace_header("Content-Length") after calling set_body() + * + * @param value String data to include as the body content. + */ + void set_body(std::string const & value); + + /// Get body size limit + /** + * Retrieves the maximum number of bytes to parse & buffer before canceling + * a request. + * + * @since 0.5.0 + * + * @return The maximum length of a message body. + */ + size_t get_max_body_size() const { + return m_body_bytes_max; + } + + /// Set body size limit + /** + * Set the maximum number of bytes to parse and buffer before canceling a + * request. + * + * @since 0.5.0 + * + * @param value The size to set the max body length to. + */ + void set_max_body_size(size_t value) { + m_body_bytes_max = value; + } + + /// Extract an HTTP parameter list from a string. + /** + * @param [in] in The input string. + * @param [out] out The parameter list to store extracted parameters in. + * @return Whether or not the input was a valid parameter list. + */ + bool parse_parameter_list(std::string const & in, parameter_list & out) + const; +protected: + /// Process a header line + /** + * @todo Update this method to be exception free. + * + * @param [in] begin An iterator to the beginning of the sequence. + * @param [in] end An iterator to the end of the sequence. + */ + void process_header(std::string::iterator begin, std::string::iterator end); + + /// Prepare the parser to begin parsing body data + /** + * Inspects headers to determine if the message has a body that needs to be + * read. If so, sets up the necessary state, otherwise returns false. If + * this method returns true and loading the message body is desired call + * `process_body` until it returns zero bytes or an error. + * + * Must not be called until after all headers have been processed. + * + * @since 0.5.0 + * + * @return True if more bytes are needed to load the body, false otherwise. + */ + bool prepare_body(); + + /// Process body data + /** + * Parses body data. + * + * @since 0.5.0 + * + * @param [in] begin An iterator to the beginning of the sequence. + * @param [in] end An iterator to the end of the sequence. + * @return The number of bytes processed + */ + size_t process_body(char const * buf, size_t len); + + /// Check if the parser is done parsing the body + /** + * Behavior before a call to `prepare_body` is undefined. + * + * @since 0.5.0 + * + * @return True if the message body has been completed loaded. + */ + bool body_ready() const { + return (m_body_bytes_needed == 0); + } + + /// Generate and return the HTTP headers as a string + /** + * Each headers will be followed by the \r\n sequence including the last one. + * A second \r\n sequence (blank header) is not appended by this method + * + * @return The HTTP headers as a string. + */ + std::string raw_headers() const; + + std::string m_version; + header_list m_headers; + + size_t m_header_bytes; + + std::string m_body; + size_t m_body_bytes_needed; + size_t m_body_bytes_max; + body_encoding::value m_body_encoding; +}; + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#include + +#endif // HTTP_PARSER_HPP diff --git a/websocketpp/http/request.hpp b/websocketpp/http/request.hpp new file mode 100644 index 00000000..3355c99b --- /dev/null +++ b/websocketpp/http/request.hpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_REQUEST_HPP +#define HTTP_PARSER_REQUEST_HPP + +#include + +#include +#include + +namespace websocketpp { +namespace http { +namespace parser { + +/// Stores, parses, and manipulates HTTP requests +/** + * http::request provides the following functionality for working with HTTP + * requests. + * + * - Initialize request via manually setting each element + * - Initialize request via reading raw bytes and parsing + * - Once initialized, access individual parsed elements + * - Once initialized, read entire request as raw bytes + */ +class request : public parser { +public: + typedef request type; + typedef lib::shared_ptr ptr; + + request() + : m_buf(lib::make_shared()) + , m_ready(false) {} + + /// Process bytes in the input buffer + /** + * Process up to len bytes from input buffer buf. Returns the number of + * bytes processed. Bytes left unprocessed means bytes left over after the + * final header delimiters. + * + * Consume is a streaming processor. It may be called multiple times on one + * request and the full headers need not be available before processing can + * begin. If the end of the request was reached during this call to consume + * the ready flag will be set. Further calls to consume once ready will be + * ignored. + * + * Consume will throw an http::exception in the case of an error. Typical + * error reasons include malformed requests, incomplete requests, and max + * header size being reached. + * + * @param buf Pointer to byte buffer + * @param len Size of byte buffer + * @return Number of bytes processed. + */ + size_t consume(char const * buf, size_t len); + + /// Returns whether or not the request is ready for reading. + bool ready() const { + return m_ready; + } + + /// Returns the full raw request (including the body) + std::string raw() const; + + /// Returns the raw request headers only (similar to an HTTP HEAD request) + std::string raw_head() const; + + /// Set the HTTP method. Must be a valid HTTP token + void set_method(std::string const & method); + + /// Return the request method + std::string const & get_method() const { + return m_method; + } + + /// Set the HTTP uri. Must be a valid HTTP uri + void set_uri(std::string const & uri); + + /// Return the requested URI + std::string const & get_uri() const { + return m_uri; + } + +private: + /// Helper function for message::consume. Process request line + void process(std::string::iterator begin, std::string::iterator end); + + lib::shared_ptr m_buf; + std::string m_method; + std::string m_uri; + bool m_ready; +}; + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#include + +#endif // HTTP_PARSER_REQUEST_HPP diff --git a/websocketpp/http/response.hpp b/websocketpp/http/response.hpp new file mode 100644 index 00000000..e724a3d3 --- /dev/null +++ b/websocketpp/http/response.hpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef HTTP_PARSER_RESPONSE_HPP +#define HTTP_PARSER_RESPONSE_HPP + +#include +#include + +#include + +namespace websocketpp { +namespace http { +namespace parser { + +/// Stores, parses, and manipulates HTTP responses +/** + * http::response provides the following functionality for working with HTTP + * responses. + * + * - Initialize response via manually setting each element + * - Initialize response via reading raw bytes and parsing + * - Once initialized, access individual parsed elements + * - Once initialized, read entire response as raw bytes + * + * http::response checks for header completeness separately from the full + * response. Once the header is complete, the Content-Length header is read to + * determine when to stop reading body bytes. If no Content-Length is present + * ready() will never return true. It is the responsibility of the caller to + * consume to determine when the response is complete (ie when the connection + * terminates, or some other metric). + */ +class response : public parser { +public: + typedef response type; + typedef lib::shared_ptr ptr; + + response() + : m_read(0) + , m_buf(lib::make_shared()) + , m_status_code(status_code::uninitialized) + , m_state(RESPONSE_LINE) {} + + /// Process bytes in the input buffer + /** + * Process up to len bytes from input buffer buf. Returns the number of + * bytes processed. Bytes left unprocessed means bytes left over after the + * final header delimiters. + * + * Consume is a streaming processor. It may be called multiple times on one + * response and the full headers need not be available before processing can + * begin. If the end of the response was reached during this call to consume + * the ready flag will be set. Further calls to consume once ready will be + * ignored. + * + * Consume will throw an http::exception in the case of an error. Typical + * error reasons include malformed responses, incomplete responses, and max + * header size being reached. + * + * @param buf Pointer to byte buffer + * @param len Size of byte buffer + * @return Number of bytes processed. + */ + size_t consume(char const * buf, size_t len); + + /// Process bytes in the input buffer (istream version) + /** + * Process bytes from istream s. Returns the number of bytes processed. + * Bytes left unprocessed means bytes left over after the final header + * delimiters. + * + * Consume is a streaming processor. It may be called multiple times on one + * response and the full headers need not be available before processing can + * begin. If the end of the response was reached during this call to consume + * the ready flag will be set. Further calls to consume once ready will be + * ignored. + * + * Consume will throw an http::exception in the case of an error. Typical + * error reasons include malformed responses, incomplete responses, and max + * header size being reached. + * + * @param buf Pointer to byte buffer + * @param len Size of byte buffer + * @return Number of bytes processed. + */ + size_t consume(std::istream & s); + + /// Returns true if the response is ready. + /** + * @note will never return true if the content length header is not present + */ + bool ready() const { + return m_state == DONE; + } + + /// Returns true if the response headers are fully parsed. + bool headers_ready() const { + return (m_state == BODY || m_state == DONE); + } + + /// Returns the full raw response + std::string raw() const; + + /// Set response status code and message + /** + * Sets the response status code to `code` and looks up the corresponding + * message for standard codes. Non-standard codes will be entered as Unknown + * use set_status(status_code::value,std::string) overload to set both + * values explicitly. + * + * @param code Code to set + * @param msg Message to set + */ + void set_status(status_code::value code); + + /// Set response status code and message + /** + * Sets the response status code and message to independent custom values. + * use set_status(status_code::value) to set the code and have the standard + * message be automatically set. + * + * @param code Code to set + * @param msg Message to set + */ + void set_status(status_code::value code, std::string const & msg); + + /// Return the response status code + status_code::value get_status_code() const { + return m_status_code; + } + + /// Return the response status message + const std::string& get_status_msg() const { + return m_status_msg; + } +private: + /// Helper function for consume. Process response line + void process(std::string::iterator begin, std::string::iterator end); + + /// Helper function for processing body bytes + size_t process_body(char const * buf, size_t len); + + enum state { + RESPONSE_LINE = 0, + HEADERS = 1, + BODY = 2, + DONE = 3 + }; + + std::string m_status_msg; + size_t m_read; + lib::shared_ptr m_buf; + status_code::value m_status_code; + state m_state; + +}; + +} // namespace parser +} // namespace http +} // namespace websocketpp + +#include + +#endif // HTTP_PARSER_RESPONSE_HPP diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp new file mode 100644 index 00000000..d1f8dff2 --- /dev/null +++ b/websocketpp/impl/connection_impl.hpp @@ -0,0 +1,2372 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONNECTION_IMPL_HPP +#define WEBSOCKETPP_CONNECTION_IMPL_HPP + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace websocketpp { + +namespace istate = session::internal_state; + +template +void connection::set_termination_handler( + termination_handler new_handler) +{ + m_alog.write(log::alevel::devel, + "connection set_termination_handler"); + + //scoped_lock_type lock(m_connection_state_lock); + + m_termination_handler = new_handler; +} + +template +std::string const & connection::get_origin() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_processor->get_origin(m_request); +} + +template +size_t connection::get_buffered_amount() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_send_buffer_size; +} + +template +session::state::value connection::get_state() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_state; +} + +template +lib::error_code connection::send(std::string const & payload, + frame::opcode::value op) +{ + message_ptr msg = m_msg_manager->get_message(op,payload.size()); + msg->append_payload(payload); + msg->set_compressed(true); + + return send(msg); +} + +template +lib::error_code connection::send(void const * payload, size_t len, + frame::opcode::value op) +{ + message_ptr msg = m_msg_manager->get_message(op,len); + msg->append_payload(payload,len); + + return send(msg); +} + +template +lib::error_code connection::send(typename config::message_type::ptr msg) +{ + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection send"); + } + + { + scoped_lock_type lock(m_connection_state_lock); + if (m_state != session::state::open) { + return error::make_error_code(error::invalid_state); + } + } + + message_ptr outgoing_msg; + bool needs_writing = false; + + if (msg->get_prepared()) { + outgoing_msg = msg; + + scoped_lock_type lock(m_write_lock); + write_push(outgoing_msg); + needs_writing = !m_write_flag && !m_send_queue.empty(); + } else { + outgoing_msg = m_msg_manager->get_message(); + + if (!outgoing_msg) { + return error::make_error_code(error::no_outgoing_buffers); + } + + scoped_lock_type lock(m_write_lock); + lib::error_code ec = m_processor->prepare_data_frame(msg,outgoing_msg); + + if (ec) { + return ec; + } + + write_push(outgoing_msg); + needs_writing = !m_write_flag && !m_send_queue.empty(); + } + + if (needs_writing) { + transport_con_type::dispatch(lib::bind( + &type::write_frame, + type::get_shared() + )); + } + + return lib::error_code(); +} + +template +void connection::ping(std::string const& payload, lib::error_code& ec) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection ping"); + } + + { + scoped_lock_type lock(m_connection_state_lock); + if (m_state != session::state::open) { + std::stringstream ss; + ss << "connection::ping called from invalid state " << m_state; + m_alog.write(log::alevel::devel,ss.str()); + ec = error::make_error_code(error::invalid_state); + return; + } + } + + message_ptr msg = m_msg_manager->get_message(); + if (!msg) { + ec = error::make_error_code(error::no_outgoing_buffers); + return; + } + + ec = m_processor->prepare_ping(payload,msg); + if (ec) {return;} + + // set ping timer if we are listening for one + if (m_pong_timeout_handler) { + // Cancel any existing timers + if (m_ping_timer) { + m_ping_timer->cancel(); + } + + if (m_pong_timeout_dur > 0) { + m_ping_timer = transport_con_type::set_timer( + m_pong_timeout_dur, + lib::bind( + &type::handle_pong_timeout, + type::get_shared(), + payload, + lib::placeholders::_1 + ) + ); + } + + if (!m_ping_timer) { + // Our transport doesn't support timers + m_elog.write(log::elevel::warn,"Warning: a pong_timeout_handler is \ + set but the transport in use does not support timeouts."); + } + } + + bool needs_writing = false; + { + scoped_lock_type lock(m_write_lock); + write_push(msg); + needs_writing = !m_write_flag && !m_send_queue.empty(); + } + + if (needs_writing) { + transport_con_type::dispatch(lib::bind( + &type::write_frame, + type::get_shared() + )); + } + + ec = lib::error_code(); +} + +template +void connection::ping(std::string const & payload) { + lib::error_code ec; + ping(payload,ec); + if (ec) { + throw exception(ec); + } +} + +template +void connection::handle_pong_timeout(std::string payload, + lib::error_code const & ec) +{ + if (ec) { + if (ec == transport::error::operation_aborted) { + // ignore, this is expected + return; + } + + m_elog.write(log::elevel::devel,"pong_timeout error: "+ec.message()); + return; + } + + if (m_pong_timeout_handler) { + m_pong_timeout_handler(m_connection_hdl,payload); + } +} + +template +void connection::pong(std::string const& payload, lib::error_code& ec) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection pong"); + } + + { + scoped_lock_type lock(m_connection_state_lock); + if (m_state != session::state::open) { + std::stringstream ss; + ss << "connection::pong called from invalid state " << m_state; + m_alog.write(log::alevel::devel,ss.str()); + ec = error::make_error_code(error::invalid_state); + return; + } + } + + message_ptr msg = m_msg_manager->get_message(); + if (!msg) { + ec = error::make_error_code(error::no_outgoing_buffers); + return; + } + + ec = m_processor->prepare_pong(payload,msg); + if (ec) {return;} + + bool needs_writing = false; + { + scoped_lock_type lock(m_write_lock); + write_push(msg); + needs_writing = !m_write_flag && !m_send_queue.empty(); + } + + if (needs_writing) { + transport_con_type::dispatch(lib::bind( + &type::write_frame, + type::get_shared() + )); + } + + ec = lib::error_code(); +} + +template +void connection::pong(std::string const & payload) { + lib::error_code ec; + pong(payload,ec); + if (ec) { + throw exception(ec); + } +} + +template +void connection::close(close::status::value const code, + std::string const & reason, lib::error_code & ec) +{ + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection close"); + } + + // Truncate reason to maximum size allowable in a close frame. + std::string tr(reason,0,std::min(reason.size(), + frame::limits::close_reason_size)); + + scoped_lock_type lock(m_connection_state_lock); + + if (m_state != session::state::open) { + ec = error::make_error_code(error::invalid_state); + return; + } + + ec = this->send_close_frame(code,tr,false,close::status::terminal(code)); +} + +template +void connection::close(close::status::value const code, + std::string const & reason) +{ + lib::error_code ec; + close(code,reason,ec); + if (ec) { + throw exception(ec); + } +} + +/// Trigger the on_interrupt handler +/** + * This is thread safe if the transport is thread safe + */ +template +lib::error_code connection::interrupt() { + m_alog.write(log::alevel::devel,"connection connection::interrupt"); + return transport_con_type::interrupt( + lib::bind( + &type::handle_interrupt, + type::get_shared() + ) + ); +} + + +template +void connection::handle_interrupt() { + if (m_interrupt_handler) { + m_interrupt_handler(m_connection_hdl); + } +} + +template +lib::error_code connection::pause_reading() { + m_alog.write(log::alevel::devel,"connection connection::pause_reading"); + return transport_con_type::dispatch( + lib::bind( + &type::handle_pause_reading, + type::get_shared() + ) + ); +} + +/// Pause reading handler. Not safe to call directly +template +void connection::handle_pause_reading() { + m_alog.write(log::alevel::devel,"connection connection::handle_pause_reading"); + m_read_flag = false; +} + +template +lib::error_code connection::resume_reading() { + m_alog.write(log::alevel::devel,"connection connection::resume_reading"); + return transport_con_type::dispatch( + lib::bind( + &type::handle_resume_reading, + type::get_shared() + ) + ); +} + +/// Resume reading helper method. Not safe to call directly +template +void connection::handle_resume_reading() { + m_read_flag = true; + read_frame(); +} + + + + + + + + + + + +template +bool connection::get_secure() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_uri->get_secure(); +} + +template +std::string const & connection::get_host() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_uri->get_host(); +} + +template +std::string const & connection::get_resource() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_uri->get_resource(); +} + +template +uint16_t connection::get_port() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_uri->get_port(); +} + +template +uri_ptr connection::get_uri() const { + //scoped_lock_type lock(m_connection_state_lock); + return m_uri; +} + +template +void connection::set_uri(uri_ptr uri) { + //scoped_lock_type lock(m_connection_state_lock); + m_uri = uri; +} + + + + + + +template +std::string const & connection::get_subprotocol() const { + return m_subprotocol; +} + +template +std::vector const & +connection::get_requested_subprotocols() const { + return m_requested_subprotocols; +} + +template +void connection::add_subprotocol(std::string const & value, + lib::error_code & ec) +{ + if (m_is_server) { + ec = error::make_error_code(error::client_only); + return; + } + + // If the value is empty or has a non-RFC2616 token character it is invalid. + if (value.empty() || std::find_if(value.begin(),value.end(), + http::is_not_token_char) != value.end()) + { + ec = error::make_error_code(error::invalid_subprotocol); + return; + } + + m_requested_subprotocols.push_back(value); +} + +template +void connection::add_subprotocol(std::string const & value) { + lib::error_code ec; + this->add_subprotocol(value,ec); + if (ec) { + throw exception(ec); + } +} + + +template +void connection::select_subprotocol(std::string const & value, + lib::error_code & ec) +{ + if (!m_is_server) { + ec = error::make_error_code(error::server_only); + return; + } + + if (value.empty()) { + ec = lib::error_code(); + return; + } + + std::vector::iterator it; + + it = std::find(m_requested_subprotocols.begin(), + m_requested_subprotocols.end(), + value); + + if (it == m_requested_subprotocols.end()) { + ec = error::make_error_code(error::unrequested_subprotocol); + return; + } + + m_subprotocol = value; +} + +template +void connection::select_subprotocol(std::string const & value) { + lib::error_code ec; + this->select_subprotocol(value,ec); + if (ec) { + throw exception(ec); + } +} + + +template +std::string const & +connection::get_request_header(std::string const & key) const { + return m_request.get_header(key); +} + +template +std::string const & +connection::get_request_body() const { + return m_request.get_body(); +} + +template +std::string const & +connection::get_response_header(std::string const & key) const { + return m_response.get_header(key); +} + +// TODO: EXCEPTION_FREE +template +void connection::set_status(http::status_code::value code) +{ + if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { + throw exception("Call to set_status from invalid state", + error::make_error_code(error::invalid_state)); + } + m_response.set_status(code); +} + +// TODO: EXCEPTION_FREE +template +void connection::set_status(http::status_code::value code, + std::string const & msg) +{ + if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { + throw exception("Call to set_status from invalid state", + error::make_error_code(error::invalid_state)); + } + + m_response.set_status(code,msg); +} + +// TODO: EXCEPTION_FREE +template +void connection::set_body(std::string const & value) { + if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { + throw exception("Call to set_status from invalid state", + error::make_error_code(error::invalid_state)); + } + + m_response.set_body(value); +} + +// TODO: EXCEPTION_FREE +template +void connection::append_header(std::string const & key, + std::string const & val) +{ + if (m_is_server) { + if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { + // we are setting response headers for an incoming server connection + m_response.append_header(key,val); + } else { + throw exception("Call to append_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } else { + if (m_internal_state == istate::USER_INIT) { + // we are setting initial headers for an outgoing client connection + m_request.append_header(key,val); + } else { + throw exception("Call to append_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } +} + +// TODO: EXCEPTION_FREE +template +void connection::replace_header(std::string const & key, + std::string const & val) +{ + if (m_is_server) { + if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { + // we are setting response headers for an incoming server connection + m_response.replace_header(key,val); + } else { + throw exception("Call to replace_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } else { + if (m_internal_state == istate::USER_INIT) { + // we are setting initial headers for an outgoing client connection + m_request.replace_header(key,val); + } else { + throw exception("Call to replace_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } +} + +// TODO: EXCEPTION_FREE +template +void connection::remove_header(std::string const & key) +{ + if (m_is_server) { + if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { + // we are setting response headers for an incoming server connection + m_response.remove_header(key); + } else { + throw exception("Call to remove_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } else { + if (m_internal_state == istate::USER_INIT) { + // we are setting initial headers for an outgoing client connection + m_request.remove_header(key); + } else { + throw exception("Call to remove_header from invalid state", + error::make_error_code(error::invalid_state)); + } + } +} + +/// Defer HTTP Response until later +/** + * Used in the http handler to defer the HTTP response for this connection + * until later. Handshake timers will be canceled and the connection will be + * left open until `send_http_response` or an equivalent is called. + * + * Warning: deferred connections won't time out and as a result can tie up + * resources. + * + * @return A status code, zero on success, non-zero otherwise + */ +template +lib::error_code connection::defer_http_response() { + // Cancel handshake timer, otherwise the connection will time out and we'll + // close the connection before the app has a chance to send a response. + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + // Do something to signal deferral + m_http_state = session::http_state::deferred; + + return lib::error_code(); +} + +/// Send deferred HTTP Response (exception free) +/** + * Sends an http response to an HTTP connection that was deferred. This will + * send a complete response including all headers, status line, and body + * text. The connection will be closed afterwards. + * + * @since 0.6.0 + * + * @param ec A status code, zero on success, non-zero otherwise + */ +template +void connection::send_http_response(lib::error_code & ec) { + { + scoped_lock_type lock(m_connection_state_lock); + if (m_http_state != session::http_state::deferred) { + ec = error::make_error_code(error::invalid_state); + return; + } + + m_http_state = session::http_state::body_written; + } + + this->write_http_response(lib::error_code()); + ec = lib::error_code(); +} + +template +void connection::send_http_response() { + lib::error_code ec; + this->send_http_response(ec); + if (ec) { + throw exception(ec); + } +} + + + + +/******** logic thread ********/ + +template +void connection::start() { + m_alog.write(log::alevel::devel,"connection start"); + + if (m_internal_state != istate::USER_INIT) { + m_alog.write(log::alevel::devel,"Start called in invalid state"); + this->terminate(error::make_error_code(error::invalid_state)); + return; + } + + m_internal_state = istate::TRANSPORT_INIT; + + // Depending on how the transport implements init this function may return + // immediately and call handle_transport_init later or call + // handle_transport_init from this function. + transport_con_type::init( + lib::bind( + &type::handle_transport_init, + type::get_shared(), + lib::placeholders::_1 + ) + ); +} + +template +void connection::handle_transport_init(lib::error_code const & ec) { + m_alog.write(log::alevel::devel,"connection handle_transport_init"); + + lib::error_code ecm = ec; + + if (m_internal_state != istate::TRANSPORT_INIT) { + m_alog.write(log::alevel::devel, + "handle_transport_init must be called from transport init state"); + ecm = error::make_error_code(error::invalid_state); + } + + if (ecm) { + std::stringstream s; + s << "handle_transport_init received error: "<< ecm.message(); + m_elog.write(log::elevel::rerror,s.str()); + + this->terminate(ecm); + return; + } + + // At this point the transport is ready to read and write bytes. + if (m_is_server) { + m_internal_state = istate::READ_HTTP_REQUEST; + this->read_handshake(1); + } else { + // We are a client. Set the processor to the version specified in the + // config file and send a handshake request. + m_internal_state = istate::WRITE_HTTP_REQUEST; + m_processor = get_processor(config::client_version); + this->send_http_request(); + } +} + +template +void connection::read_handshake(size_t num_bytes) { + m_alog.write(log::alevel::devel,"connection read_handshake"); + + if (m_open_handshake_timeout_dur > 0) { + m_handshake_timer = transport_con_type::set_timer( + m_open_handshake_timeout_dur, + lib::bind( + &type::handle_open_handshake_timeout, + type::get_shared(), + lib::placeholders::_1 + ) + ); + } + + transport_con_type::async_read_at_least( + num_bytes, + m_buf, + config::connection_read_buffer_size, + lib::bind( + &type::handle_read_handshake, + type::get_shared(), + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); +} + +// All exit paths for this function need to call write_http_response() or submit +// a new read request with this function as the handler. +template +void connection::handle_read_handshake(lib::error_code const & ec, + size_t bytes_transferred) +{ + m_alog.write(log::alevel::devel,"connection handle_read_handshake"); + + lib::error_code ecm = ec; + + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::READ_HTTP_REQUEST) { + ecm = error::make_error_code(error::invalid_state); + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog.write(log::alevel::devel, + "handle_read_handshake invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } + } + + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog.write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_read_handshake",ecm); + this->terminate(ecm); + return; + } + + // Boundaries checking. TODO: How much of this should be done? + if (bytes_transferred > config::connection_read_buffer_size) { + m_elog.write(log::elevel::fatal,"Fatal boundaries checking error."); + this->terminate(make_error_code(error::general)); + return; + } + + size_t bytes_processed = 0; + try { + bytes_processed = m_request.consume(m_buf,bytes_transferred); + } catch (http::exception &e) { + // All HTTP exceptions will result in this request failing and an error + // response being returned. No more bytes will be read in this con. + m_response.set_status(e.m_error_code,e.m_error_msg); + this->write_http_response_error(error::make_error_code(error::http_parse_error)); + return; + } + + // More paranoid boundaries checking. + // TODO: Is this overkill? + if (bytes_processed > bytes_transferred) { + m_elog.write(log::elevel::fatal,"Fatal boundaries checking error."); + this->terminate(make_error_code(error::general)); + return; + } + + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "bytes_transferred: " << bytes_transferred + << " bytes, bytes processed: " << bytes_processed << " bytes"; + m_alog.write(log::alevel::devel,s.str()); + } + + if (m_request.ready()) { + lib::error_code processor_ec = this->initialize_processor(); + if (processor_ec) { + this->write_http_response_error(processor_ec); + return; + } + + if (m_processor && m_processor->get_version() == 0) { + // Version 00 has an extra requirement to read some bytes after the + // handshake + if (bytes_transferred-bytes_processed >= 8) { + m_request.replace_header( + "Sec-WebSocket-Key3", + std::string(m_buf+bytes_processed,m_buf+bytes_processed+8) + ); + bytes_processed += 8; + } else { + // TODO: need more bytes + m_alog.write(log::alevel::devel,"short key3 read"); + m_response.set_status(http::status_code::internal_server_error); + this->write_http_response_error(processor::error::make_error_code(processor::error::short_key3)); + return; + } + } + + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,m_request.raw()); + if (!m_request.get_header("Sec-WebSocket-Key3").empty()) { + m_alog.write(log::alevel::devel, + utility::to_hex(m_request.get_header("Sec-WebSocket-Key3"))); + } + } + + // The remaining bytes in m_buf are frame data. Copy them to the + // beginning of the buffer and note the length. They will be read after + // the handshake completes and before more bytes are read. + std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf); + m_buf_cursor = bytes_transferred-bytes_processed; + + + m_internal_state = istate::PROCESS_HTTP_REQUEST; + + // We have the complete request. Process it. + lib::error_code handshake_ec = this->process_handshake_request(); + + // Write a response if this is a websocket connection or if it is an + // HTTP connection for which the response has not been deferred or + // started yet by a different system (i.e. still in init state). + if (!m_is_http || m_http_state == session::http_state::init) { + this->write_http_response(handshake_ec); + } + } else { + // read at least 1 more byte + transport_con_type::async_read_at_least( + 1, + m_buf, + config::connection_read_buffer_size, + lib::bind( + &type::handle_read_handshake, + type::get_shared(), + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); + } +} + +// write_http_response requires the request to be fully read and the connection +// to be in the PROCESS_HTTP_REQUEST state. In some cases we can detect errors +// before the request is fully read (specifically at a point where we aren't +// sure if the hybi00 key3 bytes need to be read). This method sets the correct +// state and calls write_http_response +template +void connection::write_http_response_error(lib::error_code const & ec) { + if (m_internal_state != istate::READ_HTTP_REQUEST) { + m_alog.write(log::alevel::devel, + "write_http_response_error called in invalid state"); + this->terminate(error::make_error_code(error::invalid_state)); + return; + } + + m_internal_state = istate::PROCESS_HTTP_REQUEST; + + this->write_http_response(ec); +} + +// All exit paths for this function need to call write_http_response() or submit +// a new read request with this function as the handler. +template +void connection::handle_read_frame(lib::error_code const & ec, + size_t bytes_transferred) +{ + //m_alog.write(log::alevel::devel,"connection handle_read_frame"); + + lib::error_code ecm = ec; + + if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) { + ecm = error::make_error_code(error::invalid_state); + } + + if (ecm) { + log::level echannel = log::elevel::rerror; + + if (ecm == transport::error::eof) { + if (m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + // just ignore it + m_alog.write(log::alevel::devel,"got eof from closed con"); + return; + } else if (m_state == session::state::closing && !m_is_server) { + // If we are a client we expect to get eof in the closing state, + // this is a signal to terminate our end of the connection after + // the closing handshake + terminate(lib::error_code()); + return; + } + } else if (ecm == error::invalid_state) { + // In general, invalid state errors in the closed state are the + // result of handlers that were in the system already when the state + // changed and should be ignored as they pose no problems and there + // is nothing useful that we can do about them. + if (m_state == session::state::closed) { + m_alog.write(log::alevel::devel, + "handle_read_frame: got invalid istate in closed state"); + return; + } + } else if (ecm == transport::error::tls_short_read) { + if (m_state == session::state::closed) { + // We expect to get a TLS short read if we try to read after the + // connection is closed. If this happens ignore and exit the + // read frame path. + terminate(lib::error_code()); + return; + } + echannel = log::elevel::rerror; + } else if (ecm == transport::error::action_after_shutdown) { + echannel = log::elevel::info; + } + + log_err(echannel, "handle_read_frame", ecm); + this->terminate(ecm); + return; + } + + // Boundaries checking. TODO: How much of this should be done? + /*if (bytes_transferred > config::connection_read_buffer_size) { + m_elog.write(log::elevel::fatal,"Fatal boundaries checking error"); + this->terminate(make_error_code(error::general)); + return; + }*/ + + size_t p = 0; + + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "p = " << p << " bytes transferred = " << bytes_transferred; + m_alog.write(log::alevel::devel,s.str()); + } + + while (p < bytes_transferred) { + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "calling consume with " << bytes_transferred-p << " bytes"; + m_alog.write(log::alevel::devel,s.str()); + } + + lib::error_code consume_ec; + + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "Processing Bytes: " << utility::to_hex(reinterpret_cast(m_buf)+p,bytes_transferred-p); + m_alog.write(log::alevel::devel,s.str()); + } + + p += m_processor->consume( + reinterpret_cast(m_buf)+p, + bytes_transferred-p, + consume_ec + ); + + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "bytes left after consume: " << bytes_transferred-p; + m_alog.write(log::alevel::devel,s.str()); + } + if (consume_ec) { + log_err(log::elevel::rerror, "consume", consume_ec); + + if (config::drop_on_protocol_error) { + this->terminate(consume_ec); + return; + } else { + lib::error_code close_ec; + this->close( + processor::error::to_ws(consume_ec), + consume_ec.message(), + close_ec + ); + + if (close_ec) { + log_err(log::elevel::fatal, "Protocol error close frame ", close_ec); + this->terminate(close_ec); + return; + } + } + return; + } + + if (m_processor->ready()) { + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "Complete message received. Dispatching"; + m_alog.write(log::alevel::devel,s.str()); + } + + message_ptr msg = m_processor->get_message(); + + if (!msg) { + m_alog.write(log::alevel::devel, "null message from m_processor"); + } else if (!is_control(msg->get_opcode())) { + // data message, dispatch to user + if (m_state != session::state::open) { + m_elog.write(log::elevel::warn, "got non-close frame while closing"); + } else if (m_message_handler) { + m_message_handler(m_connection_hdl, msg); + } + } else { + process_control_frame(msg); + } + } + } + + read_frame(); +} + +/// Issue a new transport read unless reading is paused. +template +void connection::read_frame() { + if (!m_read_flag) { + return; + } + + transport_con_type::async_read_at_least( + // std::min wont work with undefined static const values. + // TODO: is there a more elegant way to do this? + // Need to determine if requesting 1 byte or the exact number of bytes + // is better here. 1 byte lets us be a bit more responsive at a + // potential expense of additional runs through handle_read_frame + /*(m_processor->get_bytes_needed() > config::connection_read_buffer_size ? + config::connection_read_buffer_size : m_processor->get_bytes_needed())*/ + 1, + m_buf, + config::connection_read_buffer_size, + m_handle_read_frame + ); +} + +template +lib::error_code connection::initialize_processor() { + m_alog.write(log::alevel::devel,"initialize_processor"); + + // if it isn't a websocket handshake nothing to do. + if (!processor::is_websocket_handshake(m_request)) { + return lib::error_code(); + } + + int version = processor::get_websocket_version(m_request); + + if (version < 0) { + m_alog.write(log::alevel::devel, "BAD REQUEST: can't determine version"); + m_response.set_status(http::status_code::bad_request); + return error::make_error_code(error::invalid_version); + } + + m_processor = get_processor(version); + + // if the processor is not null we are done + if (m_processor) { + return lib::error_code(); + } + + // We don't have a processor for this version. Return bad request + // with Sec-WebSocket-Version header filled with values we do accept + m_alog.write(log::alevel::devel, "BAD REQUEST: no processor for version"); + m_response.set_status(http::status_code::bad_request); + + std::stringstream ss; + std::string sep; + std::vector::const_iterator it; + for (it = versions_supported.begin(); it != versions_supported.end(); it++) + { + ss << sep << *it; + sep = ","; + } + + m_response.replace_header("Sec-WebSocket-Version",ss.str()); + return error::make_error_code(error::unsupported_version); +} + +template +lib::error_code connection::process_handshake_request() { + m_alog.write(log::alevel::devel,"process handshake request"); + + if (!processor::is_websocket_handshake(m_request)) { + // this is not a websocket handshake. Process as plain HTTP + m_alog.write(log::alevel::devel,"HTTP REQUEST"); + + // extract URI from request + m_uri = processor::get_uri_from_host( + m_request, + (transport_con_type::is_secure() ? "https" : "http") + ); + + if (!m_uri->get_valid()) { + m_alog.write(log::alevel::devel, "Bad request: failed to parse uri"); + m_response.set_status(http::status_code::bad_request); + return error::make_error_code(error::invalid_uri); + } + + if (m_http_handler) { + m_is_http = true; + m_http_handler(m_connection_hdl); + + if (m_state == session::state::closed) { + return error::make_error_code(error::http_connection_ended); + } + } else { + set_status(http::status_code::upgrade_required); + return error::make_error_code(error::upgrade_required); + } + + return lib::error_code(); + } + + lib::error_code ec = m_processor->validate_handshake(m_request); + + // Validate: make sure all required elements are present. + if (ec){ + // Not a valid handshake request + m_alog.write(log::alevel::devel, "Bad request " + ec.message()); + m_response.set_status(http::status_code::bad_request); + return ec; + } + + // Read extension parameters and set up values necessary for the end user + // to complete extension negotiation. + std::pair neg_results; + neg_results = m_processor->negotiate_extensions(m_request); + + if (neg_results.first) { + // There was a fatal error in extension parsing that should result in + // a failed connection attempt. + m_alog.write(log::alevel::devel, "Bad request: " + neg_results.first.message()); + m_response.set_status(http::status_code::bad_request); + return neg_results.first; + } else { + // extension negotiation succeeded, set response header accordingly + // we don't send an empty extensions header because it breaks many + // clients. + if (neg_results.second.size() > 0) { + m_response.replace_header("Sec-WebSocket-Extensions", + neg_results.second); + } + } + + // extract URI from request + m_uri = m_processor->get_uri(m_request); + + + if (!m_uri->get_valid()) { + m_alog.write(log::alevel::devel, "Bad request: failed to parse uri"); + m_response.set_status(http::status_code::bad_request); + return error::make_error_code(error::invalid_uri); + } + + // extract subprotocols + lib::error_code subp_ec = m_processor->extract_subprotocols(m_request, + m_requested_subprotocols); + + if (subp_ec) { + // should we do anything? + } + + // Ask application to validate the connection + if (!m_validate_handler || m_validate_handler(m_connection_hdl)) { + m_response.set_status(http::status_code::switching_protocols); + + // Write the appropriate response headers based on request and + // processor version + ec = m_processor->process_handshake(m_request,m_subprotocol,m_response); + + if (ec) { + std::stringstream s; + s << "Processing error: " << ec << "(" << ec.message() << ")"; + m_alog.write(log::alevel::devel, s.str()); + + m_response.set_status(http::status_code::internal_server_error); + return ec; + } + } else { + // User application has rejected the handshake + m_alog.write(log::alevel::devel, "USER REJECT"); + + // Use Bad Request if the user handler did not provide a more + // specific http response error code. + // TODO: is there a better default? + if (m_response.get_status_code() == http::status_code::uninitialized) { + m_response.set_status(http::status_code::bad_request); + } + + return error::make_error_code(error::rejected); + } + + return lib::error_code(); +} + +template +void connection::write_http_response(lib::error_code const & ec) { + m_alog.write(log::alevel::devel,"connection write_http_response"); + + if (ec == error::make_error_code(error::http_connection_ended)) { + m_alog.write(log::alevel::http,"An HTTP handler took over the connection."); + return; + } + + if (m_response.get_status_code() == http::status_code::uninitialized) { + m_response.set_status(http::status_code::internal_server_error); + m_ec = error::make_error_code(error::general); + } else { + m_ec = ec; + } + + m_response.set_version("HTTP/1.1"); + + // Set server header based on the user agent settings + if (m_response.get_header("Server").empty()) { + if (!m_user_agent.empty()) { + m_response.replace_header("Server",m_user_agent); + } else { + m_response.remove_header("Server"); + } + } + + // have the processor generate the raw bytes for the wire (if it exists) + if (m_processor) { + m_handshake_buffer = m_processor->get_raw(m_response); + } else { + // a processor wont exist for raw HTTP responses. + m_handshake_buffer = m_response.raw(); + } + + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer); + if (!m_response.get_header("Sec-WebSocket-Key3").empty()) { + m_alog.write(log::alevel::devel, + utility::to_hex(m_response.get_header("Sec-WebSocket-Key3"))); + } + } + + // write raw bytes + transport_con_type::async_write( + m_handshake_buffer.data(), + m_handshake_buffer.size(), + lib::bind( + &type::handle_write_http_response, + type::get_shared(), + lib::placeholders::_1 + ) + ); +} + +template +void connection::handle_write_http_response(lib::error_code const & ec) { + m_alog.write(log::alevel::devel,"handle_write_http_response"); + + lib::error_code ecm = ec; + + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { + ecm = error::make_error_code(error::invalid_state); + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog.write(log::alevel::devel, + "handle_write_http_response invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } + } + + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog.write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_write_http_response",ecm); + this->terminate(ecm); + return; + } + + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + if (m_response.get_status_code() != http::status_code::switching_protocols) + { + /*if (m_processor || m_ec == error::http_parse_error || + m_ec == error::invalid_version || m_ec == error::unsupported_version + || m_ec == error::upgrade_required) + {*/ + if (!m_is_http) { + std::stringstream s; + s << "Handshake ended with HTTP error: " + << m_response.get_status_code(); + m_elog.write(log::elevel::rerror,s.str()); + } else { + // if this was not a websocket connection, we have written + // the expected response and the connection can be closed. + + this->log_http_result(); + + if (m_ec) { + m_alog.write(log::alevel::devel, + "got to writing HTTP results with m_ec set: "+m_ec.message()); + } + m_ec = make_error_code(error::http_connection_ended); + } + + this->terminate(m_ec); + return; + } + + this->log_open_result(); + + m_internal_state = istate::PROCESS_CONNECTION; + m_state = session::state::open; + + if (m_open_handler) { + m_open_handler(m_connection_hdl); + } + + this->handle_read_frame(lib::error_code(), m_buf_cursor); +} + +template +void connection::send_http_request() { + m_alog.write(log::alevel::devel,"connection send_http_request"); + + // TODO: origin header? + + // Have the protocol processor fill in the appropriate fields based on the + // selected client version + if (m_processor) { + lib::error_code ec; + ec = m_processor->client_handshake_request(m_request,m_uri, + m_requested_subprotocols); + + if (ec) { + log_err(log::elevel::fatal,"Internal library error: Processor",ec); + return; + } + } else { + m_elog.write(log::elevel::fatal,"Internal library error: missing processor"); + return; + } + + // Unless the user has overridden the user agent, send generic WS++ UA. + if (m_request.get_header("User-Agent").empty()) { + if (!m_user_agent.empty()) { + m_request.replace_header("User-Agent",m_user_agent); + } else { + m_request.remove_header("User-Agent"); + } + } + + m_handshake_buffer = m_request.raw(); + + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer); + } + + if (m_open_handshake_timeout_dur > 0) { + m_handshake_timer = transport_con_type::set_timer( + m_open_handshake_timeout_dur, + lib::bind( + &type::handle_open_handshake_timeout, + type::get_shared(), + lib::placeholders::_1 + ) + ); + } + + transport_con_type::async_write( + m_handshake_buffer.data(), + m_handshake_buffer.size(), + lib::bind( + &type::handle_send_http_request, + type::get_shared(), + lib::placeholders::_1 + ) + ); +} + +template +void connection::handle_send_http_request(lib::error_code const & ec) { + m_alog.write(log::alevel::devel,"handle_send_http_request"); + + lib::error_code ecm = ec; + + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::WRITE_HTTP_REQUEST) { + ecm = error::make_error_code(error::invalid_state); + } else { + m_internal_state = istate::READ_HTTP_RESPONSE; + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog.write(log::alevel::devel, + "handle_send_http_request invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } + } + + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog.write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_send_http_request",ecm); + this->terminate(ecm); + return; + } + + transport_con_type::async_read_at_least( + 1, + m_buf, + config::connection_read_buffer_size, + lib::bind( + &type::handle_read_http_response, + type::get_shared(), + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); +} + +template +void connection::handle_read_http_response(lib::error_code const & ec, + size_t bytes_transferred) +{ + m_alog.write(log::alevel::devel,"handle_read_http_response"); + + lib::error_code ecm = ec; + + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::READ_HTTP_RESPONSE) { + ecm = error::make_error_code(error::invalid_state); + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog.write(log::alevel::devel, + "handle_read_http_response invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } + } + + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog.write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_read_http_response",ecm); + this->terminate(ecm); + return; + } + + size_t bytes_processed = 0; + // TODO: refactor this to use error codes rather than exceptions + try { + bytes_processed = m_response.consume(m_buf,bytes_transferred); + } catch (http::exception & e) { + m_elog.write(log::elevel::rerror, + std::string("error in handle_read_http_response: ")+e.what()); + this->terminate(make_error_code(error::general)); + return; + } + + m_alog.write(log::alevel::devel,std::string("Raw response: ")+m_response.raw()); + + if (m_response.headers_ready()) { + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + lib::error_code validate_ec = m_processor->validate_server_handshake_response( + m_request, + m_response + ); + if (validate_ec) { + log_err(log::elevel::rerror,"Server handshake response",validate_ec); + this->terminate(validate_ec); + return; + } + + // Read extension parameters and set up values necessary for the end + // user to complete extension negotiation. + std::pair neg_results; + neg_results = m_processor->negotiate_extensions(m_response); + + if (neg_results.first) { + // There was a fatal error in extension negotiation. For the moment + // kill all connections that fail extension negotiation. + + // TODO: deal with cases where the response is well formed but + // doesn't match the options requested by the client. Its possible + // that the best behavior in this cases is to log and continue with + // an unextended connection. + m_alog.write(log::alevel::devel, "Extension negotiation failed: " + + neg_results.first.message()); + this->terminate(make_error_code(error::extension_neg_failed)); + // TODO: close connection with reason 1010 (and list extensions) + } + + // response is valid, connection can now be assumed to be open + m_internal_state = istate::PROCESS_CONNECTION; + m_state = session::state::open; + + this->log_open_result(); + + if (m_open_handler) { + m_open_handler(m_connection_hdl); + } + + // The remaining bytes in m_buf are frame data. Copy them to the + // beginning of the buffer and note the length. They will be read after + // the handshake completes and before more bytes are read. + std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf); + m_buf_cursor = bytes_transferred-bytes_processed; + + this->handle_read_frame(lib::error_code(), m_buf_cursor); + } else { + transport_con_type::async_read_at_least( + 1, + m_buf, + config::connection_read_buffer_size, + lib::bind( + &type::handle_read_http_response, + type::get_shared(), + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); + } +} + +template +void connection::handle_open_handshake_timeout( + lib::error_code const & ec) +{ + if (ec == transport::error::operation_aborted) { + m_alog.write(log::alevel::devel,"open handshake timer cancelled"); + } else if (ec) { + m_alog.write(log::alevel::devel, + "open handle_open_handshake_timeout error: "+ec.message()); + // TODO: ignore or fail here? + } else { + m_alog.write(log::alevel::devel,"open handshake timer expired"); + terminate(make_error_code(error::open_handshake_timeout)); + } +} + +template +void connection::handle_close_handshake_timeout( + lib::error_code const & ec) +{ + if (ec == transport::error::operation_aborted) { + m_alog.write(log::alevel::devel,"asio close handshake timer cancelled"); + } else if (ec) { + m_alog.write(log::alevel::devel, + "asio open handle_close_handshake_timeout error: "+ec.message()); + // TODO: ignore or fail here? + } else { + m_alog.write(log::alevel::devel, "asio close handshake timer expired"); + terminate(make_error_code(error::close_handshake_timeout)); + } +} + +template +void connection::terminate(lib::error_code const & ec) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection terminate"); + } + + // Cancel close handshake timer + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + terminate_status tstat = unknown; + if (ec) { + m_ec = ec; + m_local_close_code = close::status::abnormal_close; + m_local_close_reason = ec.message(); + } + + // TODO: does any of this need a mutex? + if (m_is_http) { + m_http_state = session::http_state::closed; + } + if (m_state == session::state::connecting) { + m_state = session::state::closed; + tstat = failed; + + // Log fail result here before socket is shut down and we can't get + // the remote address, etc anymore + if (m_ec != error::http_connection_ended) { + log_fail_result(); + } + } else if (m_state != session::state::closed) { + m_state = session::state::closed; + tstat = closed; + } else { + m_alog.write(log::alevel::devel, + "terminate called on connection that was already terminated"); + return; + } + + // TODO: choose between shutdown and close based on error code sent + + transport_con_type::async_shutdown( + lib::bind( + &type::handle_terminate, + type::get_shared(), + tstat, + lib::placeholders::_1 + ) + ); +} + +template +void connection::handle_terminate(terminate_status tstat, + lib::error_code const & ec) +{ + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection handle_terminate"); + } + + if (ec) { + // there was an error actually shutting down the connection + log_err(log::elevel::devel,"handle_terminate",ec); + } + + // clean shutdown + if (tstat == failed) { + if (m_ec != error::http_connection_ended) { + if (m_fail_handler) { + m_fail_handler(m_connection_hdl); + } + } + } else if (tstat == closed) { + if (m_close_handler) { + m_close_handler(m_connection_hdl); + } + log_close_result(); + } else { + m_elog.write(log::elevel::rerror,"Unknown terminate_status"); + } + + // call the termination handler if it exists + // if it exists it might (but shouldn't) refer to a bad memory location. + // If it does, we don't care and should catch and ignore it. + if (m_termination_handler) { + try { + m_termination_handler(type::get_shared()); + } catch (std::exception const & e) { + m_elog.write(log::elevel::warn, + std::string("termination_handler call failed. Reason was: ")+e.what()); + } + } +} + +template +void connection::write_frame() { + //m_alog.write(log::alevel::devel,"connection write_frame"); + + { + scoped_lock_type lock(m_write_lock); + + // Check the write flag. If true, there is an outstanding transport + // write already. In this case we just return. The write handler will + // start a new write if the write queue isn't empty. If false, we set + // the write flag and proceed to initiate a transport write. + if (m_write_flag) { + return; + } + + // pull off all the messages that are ready to write. + // stop if we get a message marked terminal + message_ptr next_message = write_pop(); + while (next_message) { + m_current_msgs.push_back(next_message); + if (!next_message->get_terminal()) { + next_message = write_pop(); + } else { + next_message = message_ptr(); + } + } + + if (m_current_msgs.empty()) { + // there was nothing to send + return; + } else { + // At this point we own the next messages to be sent and are + // responsible for holding the write flag until they are + // successfully sent or there is some error + m_write_flag = true; + } + } + + typename std::vector::iterator it; + for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) { + std::string const & header = (*it)->get_header(); + std::string const & payload = (*it)->get_payload(); + + m_send_buffer.push_back(transport::buffer(header.c_str(),header.size())); + m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size())); + } + + // Print detailed send stats if those log levels are enabled + if (m_alog.static_test(log::alevel::frame_header)) { + if (m_alog.dynamic_test(log::alevel::frame_header)) { + std::stringstream general,header,payload; + + general << "Dispatching write containing " << m_current_msgs.size() + <<" message(s) containing "; + header << "Header Bytes: \n"; + payload << "Payload Bytes: \n"; + + size_t hbytes = 0; + size_t pbytes = 0; + + for (size_t i = 0; i < m_current_msgs.size(); i++) { + hbytes += m_current_msgs[i]->get_header().size(); + pbytes += m_current_msgs[i]->get_payload().size(); + + + header << "[" << i << "] (" + << m_current_msgs[i]->get_header().size() << ") " + << utility::to_hex(m_current_msgs[i]->get_header()) << "\n"; + + if (m_alog.static_test(log::alevel::frame_payload)) { + if (m_alog.dynamic_test(log::alevel::frame_payload)) { + payload << "[" << i << "] (" + << m_current_msgs[i]->get_payload().size() << ") ["<get_opcode()<<"] " + << (m_current_msgs[i]->get_opcode() == frame::opcode::text ? + m_current_msgs[i]->get_payload() : + utility::to_hex(m_current_msgs[i]->get_payload()) + ) + << "\n"; + } + } + } + + general << hbytes << " header bytes and " << pbytes << " payload bytes"; + + m_alog.write(log::alevel::frame_header,general.str()); + m_alog.write(log::alevel::frame_header,header.str()); + m_alog.write(log::alevel::frame_payload,payload.str()); + } + } + + transport_con_type::async_write( + m_send_buffer, + m_write_frame_handler + ); +} + +template +void connection::handle_write_frame(lib::error_code const & ec) +{ + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection handle_write_frame"); + } + + bool terminal = m_current_msgs.back()->get_terminal(); + + m_send_buffer.clear(); + m_current_msgs.clear(); + // TODO: recycle instead of deleting + + if (ec) { + log_err(log::elevel::fatal,"handle_write_frame",ec); + this->terminate(ec); + return; + } + + if (terminal) { + this->terminate(lib::error_code()); + return; + } + + bool needs_writing = false; + { + scoped_lock_type lock(m_write_lock); + + // release write flag + m_write_flag = false; + + needs_writing = !m_send_queue.empty(); + } + + if (needs_writing) { + transport_con_type::dispatch(lib::bind( + &type::write_frame, + type::get_shared() + )); + } +} + +template +std::vector const & connection::get_supported_versions() const +{ + return versions_supported; +} + +template +void connection::process_control_frame(typename config::message_type::ptr msg) +{ + m_alog.write(log::alevel::devel,"process_control_frame"); + + frame::opcode::value op = msg->get_opcode(); + lib::error_code ec; + + std::stringstream s; + s << "Control frame received with opcode " << op; + m_alog.write(log::alevel::control,s.str()); + + if (m_state == session::state::closed) { + m_elog.write(log::elevel::warn,"got frame in state closed"); + return; + } + if (op != frame::opcode::CLOSE && m_state != session::state::open) { + m_elog.write(log::elevel::warn,"got non-close frame in state closing"); + return; + } + + if (op == frame::opcode::PING) { + bool should_reply = true; + + if (m_ping_handler) { + should_reply = m_ping_handler(m_connection_hdl, msg->get_payload()); + } + + if (should_reply) { + this->pong(msg->get_payload(),ec); + if (ec) { + log_err(log::elevel::devel,"Failed to send response pong",ec); + } + } + } else if (op == frame::opcode::PONG) { + if (m_pong_handler) { + m_pong_handler(m_connection_hdl, msg->get_payload()); + } + if (m_ping_timer) { + m_ping_timer->cancel(); + } + } else if (op == frame::opcode::CLOSE) { + m_alog.write(log::alevel::devel,"got close frame"); + // record close code and reason somewhere + + m_remote_close_code = close::extract_code(msg->get_payload(),ec); + if (ec) { + s.str(""); + if (config::drop_on_protocol_error) { + s << "Received invalid close code " << m_remote_close_code + << " dropping connection per config."; + m_elog.write(log::elevel::devel,s.str()); + this->terminate(ec); + } else { + s << "Received invalid close code " << m_remote_close_code + << " sending acknowledgement and closing"; + m_elog.write(log::elevel::devel,s.str()); + ec = send_close_ack(close::status::protocol_error, + "Invalid close code"); + if (ec) { + log_err(log::elevel::devel,"send_close_ack",ec); + } + } + return; + } + + m_remote_close_reason = close::extract_reason(msg->get_payload(),ec); + if (ec) { + if (config::drop_on_protocol_error) { + m_elog.write(log::elevel::devel, + "Received invalid close reason. Dropping connection per config"); + this->terminate(ec); + } else { + m_elog.write(log::elevel::devel, + "Received invalid close reason. Sending acknowledgement and closing"); + ec = send_close_ack(close::status::protocol_error, + "Invalid close reason"); + if (ec) { + log_err(log::elevel::devel,"send_close_ack",ec); + } + } + return; + } + + if (m_state == session::state::open) { + s.str(""); + s << "Received close frame with code " << m_remote_close_code + << " and reason " << m_remote_close_reason; + m_alog.write(log::alevel::devel,s.str()); + + ec = send_close_ack(); + if (ec) { + log_err(log::elevel::devel,"send_close_ack",ec); + } + } else if (m_state == session::state::closing && !m_was_clean) { + // ack of our close + m_alog.write(log::alevel::devel, "Got acknowledgement of close"); + + m_was_clean = true; + + // If we are a server terminate the connection now. Clients should + // leave the connection open to give the server an opportunity to + // initiate the TCP close. The client's timer will handle closing + // its side of the connection if the server misbehaves. + // + // TODO: different behavior if the underlying transport doesn't + // support timers? + if (m_is_server) { + terminate(lib::error_code()); + } + } else { + // spurious, ignore + m_elog.write(log::elevel::devel, "Got close frame in wrong state"); + } + } else { + // got an invalid control opcode + m_elog.write(log::elevel::devel, "Got control frame with invalid opcode"); + // initiate protocol error shutdown + } +} + +template +lib::error_code connection::send_close_ack(close::status::value code, + std::string const & reason) +{ + return send_close_frame(code,reason,true,m_is_server); +} + +template +lib::error_code connection::send_close_frame(close::status::value code, + std::string const & reason, bool ack, bool terminal) +{ + m_alog.write(log::alevel::devel,"send_close_frame"); + + // check for special codes + + // If silent close is set, respect it and blank out close information + // Otherwise use whatever has been specified in the parameters. If + // parameters specifies close::status::blank then determine what to do + // based on whether or not this is an ack. If it is not an ack just + // send blank info. If it is an ack then echo the close information from + // the remote endpoint. + if (config::silent_close) { + m_alog.write(log::alevel::devel,"closing silently"); + m_local_close_code = close::status::no_status; + m_local_close_reason.clear(); + } else if (code != close::status::blank) { + m_alog.write(log::alevel::devel,"closing with specified codes"); + m_local_close_code = code; + m_local_close_reason = reason; + } else if (!ack) { + m_alog.write(log::alevel::devel,"closing with no status code"); + m_local_close_code = close::status::no_status; + m_local_close_reason.clear(); + } else if (m_remote_close_code == close::status::no_status) { + m_alog.write(log::alevel::devel, + "acknowledging a no-status close with normal code"); + m_local_close_code = close::status::normal; + m_local_close_reason.clear(); + } else { + m_alog.write(log::alevel::devel,"acknowledging with remote codes"); + m_local_close_code = m_remote_close_code; + m_local_close_reason = m_remote_close_reason; + } + + std::stringstream s; + s << "Closing with code: " << m_local_close_code << ", and reason: " + << m_local_close_reason; + m_alog.write(log::alevel::devel,s.str()); + + message_ptr msg = m_msg_manager->get_message(); + if (!msg) { + return error::make_error_code(error::no_outgoing_buffers); + } + + lib::error_code ec = m_processor->prepare_close(m_local_close_code, + m_local_close_reason,msg); + if (ec) { + return ec; + } + + // Messages flagged terminal will result in the TCP connection being dropped + // after the message has been written. This is typically used when servers + // send an ack and when any endpoint encounters a protocol error + if (terminal) { + msg->set_terminal(true); + } + + m_state = session::state::closing; + + if (ack) { + m_was_clean = true; + } + + // Start a timer so we don't wait forever for the acknowledgement close + // frame + if (m_close_handshake_timeout_dur > 0) { + m_handshake_timer = transport_con_type::set_timer( + m_close_handshake_timeout_dur, + lib::bind( + &type::handle_close_handshake_timeout, + type::get_shared(), + lib::placeholders::_1 + ) + ); + } + + bool needs_writing = false; + { + scoped_lock_type lock(m_write_lock); + write_push(msg); + needs_writing = !m_write_flag && !m_send_queue.empty(); + } + + if (needs_writing) { + transport_con_type::dispatch(lib::bind( + &type::write_frame, + type::get_shared() + )); + } + + return lib::error_code(); +} + +template +typename connection::processor_ptr +connection::get_processor(int version) const { + // TODO: allow disabling certain versions + + processor_ptr p; + + switch (version) { + case 0: + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager + ); + break; + case 7: + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + lib::ref(m_rng) + ); + break; + case 8: + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + lib::ref(m_rng) + ); + break; + case 13: + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + lib::ref(m_rng) + ); + break; + default: + return p; + } + + // Settings not configured by the constructor + p->set_max_message_size(m_max_message_size); + + return p; +} + +template +void connection::write_push(typename config::message_type::ptr msg) +{ + if (!msg) { + return; + } + + m_send_buffer_size += msg->get_payload().size(); + m_send_queue.push(msg); + + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "write_push: message count: " << m_send_queue.size() + << " buffer size: " << m_send_buffer_size; + m_alog.write(log::alevel::devel,s.str()); + } +} + +template +typename config::message_type::ptr connection::write_pop() +{ + message_ptr msg; + + if (m_send_queue.empty()) { + return msg; + } + + msg = m_send_queue.front(); + + m_send_buffer_size -= msg->get_payload().size(); + m_send_queue.pop(); + + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "write_pop: message count: " << m_send_queue.size() + << " buffer size: " << m_send_buffer_size; + m_alog.write(log::alevel::devel,s.str()); + } + return msg; +} + +template +void connection::log_open_result() +{ + std::stringstream s; + + int version; + if (!processor::is_websocket_handshake(m_request)) { + version = -1; + } else { + version = processor::get_websocket_version(m_request); + } + + // Connection Type + s << (version == -1 ? "HTTP" : "WebSocket") << " Connection "; + + // Remote endpoint address + s << transport_con_type::get_remote_endpoint() << " "; + + // Version string if WebSocket + if (version != -1) { + s << "v" << version << " "; + } + + // User Agent + std::string ua = m_request.get_header("User-Agent"); + if (ua.empty()) { + s << "\"\" "; + } else { + // check if there are any quotes in the user agent + s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" "; + } + + // URI + s << (m_uri ? m_uri->get_resource() : "NULL") << " "; + + // Status code + s << m_response.get_status_code(); + + m_alog.write(log::alevel::connect,s.str()); +} + +template +void connection::log_close_result() +{ + std::stringstream s; + + s << "Disconnect " + << "close local:[" << m_local_close_code + << (m_local_close_reason.empty() ? "" : ","+m_local_close_reason) + << "] remote:[" << m_remote_close_code + << (m_remote_close_reason.empty() ? "" : ","+m_remote_close_reason) << "]"; + + m_alog.write(log::alevel::disconnect,s.str()); +} + +template +void connection::log_fail_result() +{ + std::stringstream s; + + int version = processor::get_websocket_version(m_request); + + // Connection Type + s << "WebSocket Connection "; + + // Remote endpoint address & WebSocket version + s << transport_con_type::get_remote_endpoint(); + if (version < 0) { + s << " -"; + } else { + s << " v" << version; + } + + // User Agent + std::string ua = m_request.get_header("User-Agent"); + if (ua.empty()) { + s << " \"\" "; + } else { + // check if there are any quotes in the user agent + s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" "; + } + + // URI + s << (m_uri ? m_uri->get_resource() : "-"); + + // HTTP Status code + s << " " << m_response.get_status_code(); + + // WebSocket++ error code & reason + s << " " << m_ec << " " << m_ec.message(); + + m_alog.write(log::alevel::fail,s.str()); +} + +template +void connection::log_http_result() { + std::stringstream s; + + if (processor::is_websocket_handshake(m_request)) { + m_alog.write(log::alevel::devel,"Call to log_http_result for WebSocket"); + return; + } + + // Connection Type + s << (m_request.get_header("host").empty() ? "-" : m_request.get_header("host")) + << " " << transport_con_type::get_remote_endpoint() + << " \"" << m_request.get_method() + << " " << (m_uri ? m_uri->get_resource() : "-") + << " " << m_request.get_version() << "\" " << m_response.get_status_code() + << " " << m_response.get_body().size(); + + // User Agent + std::string ua = m_request.get_header("User-Agent"); + if (ua.empty()) { + s << " \"\" "; + } else { + // check if there are any quotes in the user agent + s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" "; + } + + m_alog.write(log::alevel::http,s.str()); +} + +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONNECTION_IMPL_HPP diff --git a/websocketpp/impl/endpoint_impl.hpp b/websocketpp/impl/endpoint_impl.hpp new file mode 100644 index 00000000..e09cda95 --- /dev/null +++ b/websocketpp/impl/endpoint_impl.hpp @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_ENDPOINT_IMPL_HPP +#define WEBSOCKETPP_ENDPOINT_IMPL_HPP + +#include + +namespace websocketpp { + +template +typename endpoint::connection_ptr +endpoint::create_connection() { + m_alog.write(log::alevel::devel,"create_connection"); + //scoped_lock_type lock(m_state_lock); + + /*if (m_state == STOPPING || m_state == STOPPED) { + return connection_ptr(); + }*/ + + //scoped_lock_type guard(m_mutex); + // Create a connection on the heap and manage it using a shared pointer + connection_ptr con = lib::make_shared(m_is_server, + m_user_agent, lib::ref(m_alog), lib::ref(m_elog), lib::ref(m_rng)); + + connection_weak_ptr w(con); + + // Create a weak pointer on the heap using that shared_ptr. + // Cast that weak pointer to void* and manage it using another shared_ptr + // connection_hdl hdl(reinterpret_cast(new connection_weak_ptr(con))); + + con->set_handle(w); + + // Copy default handlers from the endpoint + con->set_open_handler(m_open_handler); + con->set_close_handler(m_close_handler); + con->set_fail_handler(m_fail_handler); + con->set_ping_handler(m_ping_handler); + con->set_pong_handler(m_pong_handler); + con->set_pong_timeout_handler(m_pong_timeout_handler); + con->set_interrupt_handler(m_interrupt_handler); + con->set_http_handler(m_http_handler); + con->set_validate_handler(m_validate_handler); + con->set_message_handler(m_message_handler); + + if (m_open_handshake_timeout_dur != config::timeout_open_handshake) { + con->set_open_handshake_timeout(m_open_handshake_timeout_dur); + } + if (m_close_handshake_timeout_dur != config::timeout_close_handshake) { + con->set_close_handshake_timeout(m_close_handshake_timeout_dur); + } + if (m_pong_timeout_dur != config::timeout_pong) { + con->set_pong_timeout(m_pong_timeout_dur); + } + if (m_max_message_size != config::max_message_size) { + con->set_max_message_size(m_max_message_size); + } + con->set_max_http_body_size(m_max_http_body_size); + + lib::error_code ec; + + ec = transport_type::init(con); + if (ec) { + m_elog.write(log::elevel::fatal,ec.message()); + return connection_ptr(); + } + + return con; +} + +template +void endpoint::interrupt(connection_hdl hdl, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + + m_alog.write(log::alevel::devel,"Interrupting connection"); + + ec = con->interrupt(); +} + +template +void endpoint::interrupt(connection_hdl hdl) { + lib::error_code ec; + interrupt(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::pause_reading(connection_hdl hdl, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + + ec = con->pause_reading(); +} + +template +void endpoint::pause_reading(connection_hdl hdl) { + lib::error_code ec; + pause_reading(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::resume_reading(connection_hdl hdl, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + + ec = con->resume_reading(); +} + +template +void endpoint::resume_reading(connection_hdl hdl) { + lib::error_code ec; + resume_reading(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::send_http_response(connection_hdl hdl, + lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + con->send_http_response(ec); +} + +template +void endpoint::send_http_response(connection_hdl hdl) { + lib::error_code ec; + send_http_response(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::send(connection_hdl hdl, std::string const & payload, + frame::opcode::value op, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + + ec = con->send(payload,op); +} + +template +void endpoint::send(connection_hdl hdl, std::string const & payload, + frame::opcode::value op) +{ + lib::error_code ec; + send(hdl,payload,op,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::send(connection_hdl hdl, void const * payload, + size_t len, frame::opcode::value op, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + ec = con->send(payload,len,op); +} + +template +void endpoint::send(connection_hdl hdl, void const * payload, + size_t len, frame::opcode::value op) +{ + lib::error_code ec; + send(hdl,payload,len,op,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::send(connection_hdl hdl, message_ptr msg, + lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + ec = con->send(msg); +} + +template +void endpoint::send(connection_hdl hdl, message_ptr msg) { + lib::error_code ec; + send(hdl,msg,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::close(connection_hdl hdl, close::status::value + const code, std::string const & reason, + lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + con->close(code,reason,ec); +} + +template +void endpoint::close(connection_hdl hdl, close::status::value + const code, std::string const & reason) +{ + lib::error_code ec; + close(hdl,code,reason,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::ping(connection_hdl hdl, std::string const & + payload, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + con->ping(payload,ec); +} + +template +void endpoint::ping(connection_hdl hdl, std::string const & payload) +{ + lib::error_code ec; + ping(hdl,payload,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::pong(connection_hdl hdl, std::string const & payload, + lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + con->pong(payload,ec); +} + +template +void endpoint::pong(connection_hdl hdl, std::string const & payload) +{ + lib::error_code ec; + pong(hdl,payload,ec); + if (ec) { throw exception(ec); } +} + +} // namespace websocketpp + +#endif // WEBSOCKETPP_ENDPOINT_IMPL_HPP diff --git a/websocketpp/impl/utilities_impl.hpp b/websocketpp/impl/utilities_impl.hpp new file mode 100644 index 00000000..6f86e22f --- /dev/null +++ b/websocketpp/impl/utilities_impl.hpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_UTILITIES_IMPL_HPP +#define WEBSOCKETPP_UTILITIES_IMPL_HPP + +#include +#include + +namespace websocketpp { +namespace utility { + +inline std::string to_lower(std::string const & in) { + std::string out = in; + std::transform(out.begin(),out.end(),out.begin(),::tolower); + return out; +} + +inline std::string to_hex(std::string const & input) { + std::string output; + std::string hex = "0123456789ABCDEF"; + + for (size_t i = 0; i < input.size(); i++) { + output += hex[(input[i] & 0xF0) >> 4]; + output += hex[input[i] & 0x0F]; + output += " "; + } + + return output; +} + +inline std::string to_hex(uint8_t const * input, size_t length) { + std::string output; + std::string hex = "0123456789ABCDEF"; + + for (size_t i = 0; i < length; i++) { + output += hex[(input[i] & 0xF0) >> 4]; + output += hex[input[i] & 0x0F]; + output += " "; + } + + return output; +} + +inline std::string to_hex(const char* input,size_t length) { + return to_hex(reinterpret_cast(input),length); +} + +inline std::string string_replace_all(std::string subject, std::string const & + search, std::string const & replace) +{ + size_t pos = 0; + while((pos = subject.find(search, pos)) != std::string::npos) { + subject.replace(pos, search.length(), replace); + pos += replace.length(); + } + return subject; +} + +} // namespace utility +} // namespace websocketpp + +#endif // WEBSOCKETPP_UTILITIES_IMPL_HPP diff --git a/websocketpp/logger/basic.hpp b/websocketpp/logger/basic.hpp new file mode 100644 index 00000000..84514130 --- /dev/null +++ b/websocketpp/logger/basic.hpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_LOGGER_BASIC_HPP +#define WEBSOCKETPP_LOGGER_BASIC_HPP + +/* Need a way to print a message to the log + * + * - timestamps + * - channels + * - thread safe + * - output to stdout or file + * - selective output channels, both compile time and runtime + * - named channels + * - ability to test whether a log message will be printed at compile time + * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace websocketpp { +namespace log { + +/// Basic logger that outputs to an ostream +template +class basic { +public: + basic(channel_type_hint::value h = + channel_type_hint::access) + : m_static_channels(0xffffffff) + , m_dynamic_channels(0) + , m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {} + + basic(std::ostream * out) + : m_static_channels(0xffffffff) + , m_dynamic_channels(0) + , m_out(out) {} + + basic(level c, channel_type_hint::value h = + channel_type_hint::access) + : m_static_channels(c) + , m_dynamic_channels(0) + , m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {} + + basic(level c, std::ostream * out) + : m_static_channels(c) + , m_dynamic_channels(0) + , m_out(out) {} + + /// Destructor + ~basic() {} + + /// Copy constructor + basic(basic const & other) + : m_static_channels(other.m_static_channels) + , m_dynamic_channels(other.m_dynamic_channels) + , m_out(other.m_out) + {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no copy assignment operator because of const member variables + basic & operator=(basic const &) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + basic(basic && other) + : m_static_channels(other.m_static_channels) + , m_dynamic_channels(other.m_dynamic_channels) + , m_out(other.m_out) + {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no move assignment operator because of const member variables + basic & operator=(basic &&) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + + void set_ostream(std::ostream * out = &std::cout) { + m_out = out; + } + + void set_channels(level channels) { + if (channels == names::none) { + clear_channels(names::all); + return; + } + + scoped_lock_type lock(m_lock); + m_dynamic_channels |= (channels & m_static_channels); + } + + void clear_channels(level channels) { + scoped_lock_type lock(m_lock); + m_dynamic_channels &= ~channels; + } + + /// Write a string message to the given channel + /** + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level channel, std::string const & msg) { + scoped_lock_type lock(m_lock); + if (!this->dynamic_test(channel)) { return; } + *m_out << "[" << timestamp << "] " + << "[" << names::channel_name(channel) << "] " + << msg << "\n"; + m_out->flush(); + } + + /// Write a cstring message to the given channel + /** + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level channel, char const * msg) { + scoped_lock_type lock(m_lock); + if (!this->dynamic_test(channel)) { return; } + *m_out << "[" << timestamp << "] " + << "[" << names::channel_name(channel) << "] " + << msg << "\n"; + m_out->flush(); + } + + _WEBSOCKETPP_CONSTEXPR_TOKEN_ bool static_test(level channel) const { + return ((channel & m_static_channels) != 0); + } + + bool dynamic_test(level channel) { + return ((channel & m_dynamic_channels) != 0); + } + +protected: + typedef typename concurrency::scoped_lock_type scoped_lock_type; + typedef typename concurrency::mutex_type mutex_type; + mutex_type m_lock; + +private: + // The timestamp does not include the time zone, because on Windows with the + // default registry settings, the time zone would be written out in full, + // which would be obnoxiously verbose. + // + // TODO: find a workaround for this or make this format user settable + static std::ostream & timestamp(std::ostream & os) { + std::time_t t = std::time(NULL); + std::tm lt = lib::localtime(t); + #ifdef _WEBSOCKETPP_PUTTIME_ + return os << std::put_time(<,"%Y-%m-%d %H:%M:%S"); + #else // Falls back to strftime, which requires a temporary copy of the string. + char buffer[20]; + size_t result = std::strftime(buffer,sizeof(buffer),"%Y-%m-%d %H:%M:%S",<); + return os << (result == 0 ? "Unknown" : buffer); + #endif + } + + level const m_static_channels; + level m_dynamic_channels; + std::ostream * m_out; +}; + +} // log +} // websocketpp + +#endif // WEBSOCKETPP_LOGGER_BASIC_HPP diff --git a/websocketpp/logger/levels.hpp b/websocketpp/logger/levels.hpp new file mode 100644 index 00000000..cd7ccd69 --- /dev/null +++ b/websocketpp/logger/levels.hpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_LOGGER_LEVELS_HPP +#define WEBSOCKETPP_LOGGER_LEVELS_HPP + +#include + +namespace websocketpp { +namespace log { + +/// Type of a channel package +typedef uint32_t level; + +/// Package of values for hinting at the nature of a given logger. +/** + * Used by the library to signal to the logging class a hint that it can use to + * set itself up. For example, the `access` hint indicates that it is an access + * log that might be suitable for being printed to an access log file or to cout + * whereas `error` might be suitable for an error log file or cerr. + */ +struct channel_type_hint { + /// Type of a channel type hint value + typedef uint32_t value; + + /// No information + static value const none = 0; + /// Access log + static value const access = 1; + /// Error log + static value const error = 2; +}; + +/// Package of log levels for logging errors +struct elevel { + /// Special aggregate value representing "no levels" + static level const none = 0x0; + /// Low level debugging information (warning: very chatty) + static level const devel = 0x1; + /// Information about unusual system states or other minor internal library + /// problems, less chatty than devel. + static level const library = 0x2; + /// Information about minor configuration problems or additional information + /// about other warnings. + static level const info = 0x4; + /// Information about important problems not severe enough to terminate + /// connections. + static level const warn = 0x8; + /// Recoverable error. Recovery may mean cleanly closing the connection with + /// an appropriate error code to the remote endpoint. + static level const rerror = 0x10; + /// Unrecoverable error. This error will trigger immediate unclean + /// termination of the connection or endpoint. + static level const fatal = 0x20; + /// Special aggregate value representing "all levels" + static level const all = 0xffffffff; + + /// Get the textual name of a channel given a channel id + /** + * The id must be that of a single channel. Passing an aggregate channel + * package results in undefined behavior. + * + * @param channel The channel id to look up. + * + * @return The name of the specified channel. + */ + static char const * channel_name(level channel) { + switch(channel) { + case devel: + return "devel"; + case library: + return "library"; + case info: + return "info"; + case warn: + return "warning"; + case rerror: + return "error"; + case fatal: + return "fatal"; + default: + return "unknown"; + } + } +}; + +/// Package of log levels for logging access events +struct alevel { + /// Special aggregate value representing "no levels" + static level const none = 0x0; + /// Information about new connections + /** + * One line for each new connection that includes a host of information + * including: the remote address, websocket version, requested resource, + * http code, remote user agent + */ + static level const connect = 0x1; + /// One line for each closed connection. Includes closing codes and reasons. + static level const disconnect = 0x2; + /// One line per control frame + static level const control = 0x4; + /// One line per frame, includes the full frame header + static level const frame_header = 0x8; + /// One line per frame, includes the full message payload (warning: chatty) + static level const frame_payload = 0x10; + /// Reserved + static level const message_header = 0x20; + /// Reserved + static level const message_payload = 0x40; + /// Reserved + static level const endpoint = 0x80; + /// Extra information about opening handshakes + static level const debug_handshake = 0x100; + /// Extra information about closing handshakes + static level const debug_close = 0x200; + /// Development messages (warning: very chatty) + static level const devel = 0x400; + /// Special channel for application specific logs. Not used by the library. + static level const app = 0x800; + /// Access related to HTTP requests + static level const http = 0x1000; + /// One line for each failed WebSocket connection with details + static level const fail = 0x2000; + /// Aggregate package representing the commonly used core access channels + /// Connect, Disconnect, Fail, and HTTP + static level const access_core = 0x00003003; + /// Special aggregate value representing "all levels" + static level const all = 0xffffffff; + + /// Get the textual name of a channel given a channel id + /** + * Get the textual name of a channel given a channel id. The id must be that + * of a single channel. Passing an aggregate channel package results in + * undefined behavior. + * + * @param channel The channelid to look up. + * + * @return The name of the specified channel. + */ + static char const * channel_name(level channel) { + switch(channel) { + case connect: + return "connect"; + case disconnect: + return "disconnect"; + case control: + return "control"; + case frame_header: + return "frame_header"; + case frame_payload: + return "frame_payload"; + case message_header: + return "message_header"; + case message_payload: + return "message_payload"; + case endpoint: + return "endpoint"; + case debug_handshake: + return "debug_handshake"; + case debug_close: + return "debug_close"; + case devel: + return "devel"; + case app: + return "application"; + case http: + return "http"; + case fail: + return "fail"; + default: + return "unknown"; + } + } +}; + +} // logger +} // websocketpp + +#endif //WEBSOCKETPP_LOGGER_LEVELS_HPP diff --git a/websocketpp/logger/stub.hpp b/websocketpp/logger/stub.hpp new file mode 100644 index 00000000..2db6da7d --- /dev/null +++ b/websocketpp/logger/stub.hpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_LOGGER_STUB_HPP +#define WEBSOCKETPP_LOGGER_STUB_HPP + +#include + +#include + +#include + +namespace websocketpp { +namespace log { + +/// Stub logger that ignores all input +class stub { +public: + /// Construct the logger + /** + * @param hint A channel type specific hint for how to construct the logger + */ + explicit stub(channel_type_hint::value) {} + + /// Construct the logger + /** + * @param default_channels A set of channels to statically enable + * @param hint A channel type specific hint for how to construct the logger + */ + stub(level, channel_type_hint::value) {} + _WEBSOCKETPP_CONSTEXPR_TOKEN_ stub() {} + + /// Dynamically enable the given list of channels + /** + * All operations on the stub logger are no-ops and all arguments are + * ignored + * + * @param channels The package of channels to enable + */ + void set_channels(level) {} + + /// Dynamically disable the given list of channels + /** + * All operations on the stub logger are no-ops and all arguments are + * ignored + * + * @param channels The package of channels to disable + */ + void clear_channels(level) {} + + /// Write a string message to the given channel + /** + * Writing on the stub logger is a no-op and all arguments are ignored + * + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level, std::string const &) {} + + /// Write a cstring message to the given channel + /** + * Writing on the stub logger is a no-op and all arguments are ignored + * + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level, char const *) {} + + /// Test whether a channel is statically enabled + /** + * The stub logger has no channels so all arguments are ignored and + * `static_test` always returns false. + * + * @param channel The package of channels to test + */ + _WEBSOCKETPP_CONSTEXPR_TOKEN_ bool static_test(level) const { + return false; + } + + /// Test whether a channel is dynamically enabled + /** + * The stub logger has no channels so all arguments are ignored and + * `dynamic_test` always returns false. + * + * @param channel The package of channels to test + */ + bool dynamic_test(level) { + return false; + } +}; + +} // log +} // websocketpp + +#endif // WEBSOCKETPP_LOGGER_STUB_HPP diff --git a/websocketpp/logger/syslog.hpp b/websocketpp/logger/syslog.hpp new file mode 100644 index 00000000..513abee4 --- /dev/null +++ b/websocketpp/logger/syslog.hpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * The initial version of this logging policy was contributed to the WebSocket++ + * project by Tom Hughes. + */ + +#ifndef WEBSOCKETPP_LOGGER_SYSLOG_HPP +#define WEBSOCKETPP_LOGGER_SYSLOG_HPP + +#include + +#include + +#include +#include + +namespace websocketpp { +namespace log { + +/// Basic logger that outputs to syslog +template +class syslog : public basic { +public: + typedef basic base; + + /// Construct the logger + /** + * @param hint A channel type specific hint for how to construct the logger + */ + syslog(channel_type_hint::value hint = + channel_type_hint::access) + : basic(hint), m_channel_type_hint(hint) {} + + /// Construct the logger + /** + * @param channels A set of channels to statically enable + * @param hint A channel type specific hint for how to construct the logger + */ + syslog(level channels, channel_type_hint::value hint = + channel_type_hint::access) + : basic(channels, hint), m_channel_type_hint(hint) {} + + /// Write a string message to the given channel + /** + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level channel, std::string const & msg) { + write(channel, msg.c_str()); + } + + /// Write a cstring message to the given channel + /** + * @param channel The channel to write to + * @param msg The message to write + */ + void write(level channel, char const * msg) { + scoped_lock_type lock(base::m_lock); + if (!this->dynamic_test(channel)) { return; } + ::syslog(syslog_priority(channel), "[%s] %s", + names::channel_name(channel), msg); + } +private: + typedef typename base::scoped_lock_type scoped_lock_type; + + /// The default level is used for all access logs and any error logs that + /// don't trivially map to one of the standard syslog levels. + static int const default_level = LOG_INFO; + + /// retrieve the syslog priority code given a WebSocket++ channel + /** + * @param channel The level to look up + * @return The syslog level associated with `channel` + */ + int syslog_priority(level channel) const { + if (m_channel_type_hint == channel_type_hint::access) { + return syslog_priority_access(channel); + } else { + return syslog_priority_error(channel); + } + } + + /// retrieve the syslog priority code given a WebSocket++ error channel + /** + * @param channel The level to look up + * @return The syslog level associated with `channel` + */ + int syslog_priority_error(level channel) const { + switch (channel) { + case elevel::devel: + return LOG_DEBUG; + case elevel::library: + return LOG_DEBUG; + case elevel::info: + return LOG_INFO; + case elevel::warn: + return LOG_WARNING; + case elevel::rerror: + return LOG_ERR; + case elevel::fatal: + return LOG_CRIT; + default: + return default_level; + } + } + + /// retrieve the syslog priority code given a WebSocket++ access channel + /** + * @param channel The level to look up + * @return The syslog level associated with `channel` + */ + _WEBSOCKETPP_CONSTEXPR_TOKEN_ int syslog_priority_access(level) const { + return default_level; + } + + channel_type_hint::value m_channel_type_hint; +}; + +} // log +} // websocketpp + +#endif // WEBSOCKETPP_LOGGER_SYSLOG_HPP diff --git a/websocketpp/message_buffer/alloc.hpp b/websocketpp/message_buffer/alloc.hpp new file mode 100644 index 00000000..75d89766 --- /dev/null +++ b/websocketpp/message_buffer/alloc.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP +#define WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP + +#include +#include + +namespace websocketpp { +namespace message_buffer { +namespace alloc { + +/// A connection message manager that allocates a new message for each +/// request. +template +class con_msg_manager + : public lib::enable_shared_from_this > +{ +public: + typedef con_msg_manager type; + typedef lib::shared_ptr ptr; + typedef lib::weak_ptr weak_ptr; + + typedef typename message::ptr message_ptr; + + /// Get an empty message buffer + /** + * @return A shared pointer to an empty new message + */ + message_ptr get_message() { + return message_ptr(lib::make_shared(type::shared_from_this())); + } + + /// Get a message buffer with specified size and opcode + /** + * @param op The opcode to use + * @param size Minimum size in bytes to request for the message payload. + * + * @return A shared pointer to a new message with specified size. + */ + message_ptr get_message(frame::opcode::value op,size_t size) { + return message_ptr(lib::make_shared(type::shared_from_this(),op,size)); + } + + /// Recycle a message + /** + * This method shouldn't be called. If it is, return false to indicate an + * error. The rest of the method recycle chain should notice this and free + * the memory. + * + * @param msg The message to be recycled. + * + * @return true if the message was successfully recycled, false otherwse. + */ + bool recycle(message *) { + return false; + } +}; + +/// An endpoint message manager that allocates a new manager for each +/// connection. +template +class endpoint_msg_manager { +public: + typedef typename con_msg_manager::ptr con_msg_man_ptr; + + /// Get a pointer to a connection message manager + /** + * @return A pointer to the requested connection message manager. + */ + con_msg_man_ptr get_manager() const { + return con_msg_man_ptr(lib::make_shared()); + } +}; + +} // namespace alloc +} // namespace message_buffer +} // namespace websocketpp + +#endif // WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP diff --git a/websocketpp/message_buffer/message.hpp b/websocketpp/message_buffer/message.hpp new file mode 100644 index 00000000..da36e200 --- /dev/null +++ b/websocketpp/message_buffer/message.hpp @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_MESSAGE_BUFFER_MESSAGE_HPP +#define WEBSOCKETPP_MESSAGE_BUFFER_MESSAGE_HPP + +#include +#include + +#include + +namespace websocketpp { +namespace message_buffer { + +/* # message: + * object that stores a message while it is being sent or received. Contains + * the message payload itself, the message header, the extension data, and the + * opcode. + * + * # connection_message_manager: + * An object that manages all of the message_buffers associated with a given + * connection. Implements the get_message_buffer(size) method that returns + * a message buffer at least size bytes long. + * + * Message buffers are reference counted with shared ownership semantics. Once + * requested from the manager the requester and it's associated downstream code + * may keep a pointer to the message indefinitely at a cost of extra resource + * usage. Once the reference count drops to the point where the manager is the + * only reference the messages is recycled using whatever method is implemented + * in the manager. + * + * # endpoint_message_manager: + * An object that manages connection_message_managers. Implements the + * get_message_manager() method. This is used once by each connection to + * request the message manager that they are supposed to use to manage message + * buffers for their own use. + * + * TYPES OF CONNECTION_MESSAGE_MANAGERS + * - allocate a message with the exact size every time one is requested + * - maintain a pool of pre-allocated messages and return one when needed. + * Recycle previously used messages back into the pool + * + * TYPES OF ENDPOINT_MESSAGE_MANAGERS + * - allocate a new connection manager for each connection. Message pools + * become connection specific. This increases memory usage but improves + * concurrency. + * - allocate a single connection manager and share a pointer to it with all + * connections created by this endpoint. The message pool will be shared + * among all connections, improving memory usage and performance at the cost + * of reduced concurrency + */ + + +/// Represents a buffer for a single WebSocket message. +/** + * + * + */ +template class con_msg_manager> +class message { +public: + typedef lib::shared_ptr ptr; + + typedef con_msg_manager con_msg_man_type; + typedef typename con_msg_man_type::ptr con_msg_man_ptr; + typedef typename con_msg_man_type::weak_ptr con_msg_man_weak_ptr; + + /// Construct an empty message + /** + * Construct an empty message + */ + message(const con_msg_man_ptr manager) + : m_manager(manager) + , m_prepared(false) + , m_fin(true) + , m_terminal(false) + , m_compressed(false) {} + + /// Construct a message and fill in some values + /** + * + */ + message(const con_msg_man_ptr manager, frame::opcode::value op, size_t size = 128) + : m_manager(manager) + , m_opcode(op) + , m_prepared(false) + , m_fin(true) + , m_terminal(false) + , m_compressed(false) + { + m_payload.reserve(size); + } + + /// Return whether or not the message has been prepared for sending + /** + * The prepared flag indicates that the message has been prepared by a + * websocket protocol processor and is ready to be written to the wire. + * + * @return whether or not the message has been prepared for sending + */ + bool get_prepared() const { + return m_prepared; + } + + /// Set or clear the flag that indicates that the message has been prepared + /** + * This flag should not be set by end user code without a very good reason. + * + * @param value The value to set the prepared flag to + */ + void set_prepared(bool value) { + m_prepared = value; + } + + /// Return whether or not the message is flagged as compressed + /** + * @return whether or not the message is/should be compressed + */ + bool get_compressed() const { + return m_compressed; + } + + /// Set or clear the compression flag + /** + * Setting the compression flag indicates that the data in this message + * would benefit from compression. If both endpoints negotiate a compression + * extension WebSocket++ will attempt to compress messages with this flag. + * Setting this flag does not guarantee that the message will be compressed. + * + * @param value The value to set the compressed flag to + */ + void set_compressed(bool value) { + m_compressed = value; + } + + /// Get whether or not the message is terminal + /** + * Messages can be flagged as terminal, which results in the connection + * being close after they are written rather than the implementation going + * on to the next message in the queue. This is typically used internally + * for close messages only. + * + * @return Whether or not this message is marked terminal + */ + bool get_terminal() const { + return m_terminal; + } + + /// Set the terminal flag + /** + * This flag should not be set by end user code without a very good reason. + * + * @see get_terminal() + * + * @param value The value to set the terminal flag to. + */ + void set_terminal(bool value) { + m_terminal = value; + } + /// Read the fin bit + /** + * A message with the fin bit set will be sent as the last message of its + * sequence. A message with the fin bit cleared will require subsequent + * frames of opcode continuation until one of them has the fin bit set. + * + * The remote end likely will not deliver any bytes until the frame with the fin + * bit set has been received. + * + * @return Whether or not the fin bit is set + */ + bool get_fin() const { + return m_fin; + } + + /// Set the fin bit + /** + * @see get_fin for a more detailed explaination of the fin bit + * + * @param value The value to set the fin bit to. + */ + void set_fin(bool value) { + m_fin = value; + } + + /// Return the message opcode + frame::opcode::value get_opcode() const { + return m_opcode; + } + + /// Set the opcode + void set_opcode(frame::opcode::value op) { + m_opcode = op; + } + + /// Return the prepared frame header + /** + * This value is typically set by a websocket protocol processor + * and shouldn't be tampered with. + */ + std::string const & get_header() const { + return m_header; + } + + /// Set prepared frame header + /** + * Under normal circumstances this should not be called by end users + * + * @param header A string to set the header to. + */ + void set_header(std::string const & header) { + m_header = header; + } + + std::string const & get_extension_data() const { + return m_extension_data; + } + + /// Get a reference to the payload string + /** + * @return A const reference to the message's payload string + */ + std::string const & get_payload() const { + return m_payload; + } + + /// Get a non-const reference to the payload string + /** + * @return A reference to the message's payload string + */ + std::string & get_raw_payload() { + return m_payload; + } + + /// Set payload data + /** + * Set the message buffer's payload to the given value. + * + * @param payload A string to set the payload to. + */ + void set_payload(std::string const & payload) { + m_payload = payload; + } + + /// Set payload data + /** + * Set the message buffer's payload to the given value. + * + * @param payload A pointer to a data array to set to. + * @param len The length of new payload in bytes. + */ + void set_payload(void const * payload, size_t len) { + m_payload.reserve(len); + char const * pl = static_cast(payload); + m_payload.assign(pl, pl + len); + } + + /// Append payload data + /** + * Append data to the message buffer's payload. + * + * @param payload A string containing the data array to append. + */ + void append_payload(std::string const & payload) { + m_payload.append(payload); + } + + /// Append payload data + /** + * Append data to the message buffer's payload. + * + * @param payload A pointer to a data array to append + * @param len The length of payload in bytes + */ + void append_payload(void const * payload, size_t len) { + m_payload.reserve(m_payload.size()+len); + m_payload.append(static_cast(payload),len); + } + + /// Recycle the message + /** + * A request to recycle this message was received. Forward that request to + * the connection message manager for processing. Errors and exceptions + * from the manager's recycle member function should be passed back up the + * call chain. The caller to message::recycle will deal with them. + * + * Recycle must *only* be called by the message shared_ptr's destructor. + * Once recycled successfully, ownership of the memory has been passed to + * another system and must not be accessed again. + * + * @return true if the message was successfully recycled, false otherwise. + */ + bool recycle() { + con_msg_man_ptr shared = m_manager.lock(); + + if (shared) { + return shared->recycle(this); + } else { + return false; + } + } +private: + con_msg_man_weak_ptr m_manager; + std::string m_header; + std::string m_extension_data; + std::string m_payload; + frame::opcode::value m_opcode; + bool m_prepared; + bool m_fin; + bool m_terminal; + bool m_compressed; +}; + +} // namespace message_buffer +} // namespace websocketpp + +#endif // WEBSOCKETPP_MESSAGE_BUFFER_MESSAGE_HPP diff --git a/websocketpp/message_buffer/pool.hpp b/websocketpp/message_buffer/pool.hpp new file mode 100644 index 00000000..3af9e02a --- /dev/null +++ b/websocketpp/message_buffer/pool.hpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP +#define WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP + +#include + +#include + +namespace websocketpp { +namespace message_buffer { + +/* # message: + * object that stores a message while it is being sent or received. Contains + * the message payload itself, the message header, the extension data, and the + * opcode. + * + * # connection_message_manager: + * An object that manages all of the message_buffers associated with a given + * connection. Implements the get_message_buffer(size) method that returns + * a message buffer at least size bytes long. + * + * Message buffers are reference counted with shared ownership semantics. Once + * requested from the manager the requester and it's associated downstream code + * may keep a pointer to the message indefinitely at a cost of extra resource + * usage. Once the reference count drops to the point where the manager is the + * only reference the messages is recycled using whatever method is implemented + * in the manager. + * + * # endpoint_message_manager: + * An object that manages connection_message_managers. Implements the + * get_message_manager() method. This is used once by each connection to + * request the message manager that they are supposed to use to manage message + * buffers for their own use. + * + * TYPES OF CONNECTION_MESSAGE_MANAGERS + * - allocate a message with the exact size every time one is requested + * - maintain a pool of pre-allocated messages and return one when needed. + * Recycle previously used messages back into the pool + * + * TYPES OF ENDPOINT_MESSAGE_MANAGERS + * - allocate a new connection manager for each connection. Message pools + * become connection specific. This increases memory usage but improves + * concurrency. + * - allocate a single connection manager and share a pointer to it with all + * connections created by this endpoint. The message pool will be shared + * among all connections, improving memory usage and performance at the cost + * of reduced concurrency + */ + +/// Custom deleter for use in shared_ptrs to message. +/** + * This is used to catch messages about to be deleted and offer the manager the + * ability to recycle them instead. Message::recycle will return true if it was + * successfully recycled and false otherwise. In the case of exceptions or error + * this deleter frees the memory. + */ +template +void message_deleter(T* msg) { + try { + if (!msg->recycle()) { + delete msg; + } + } catch (...) { + // TODO: is there a better way to ensure this function doesn't throw? + delete msg; + } +} + +/// Represents a buffer for a single WebSocket message. +/** + * + * + */ +template +class message { +public: + typedef lib::shared_ptr ptr; + + typedef typename con_msg_manager::weak_ptr con_msg_man_ptr; + + message(con_msg_man_ptr manager, size_t size = 128) + : m_manager(manager) + , m_payload(size) {} + + frame::opcode::value get_opcode() const { + return m_opcode; + } + const std::string& get_header() const { + return m_header; + } + const std::string& get_extension_data() const { + return m_extension_data; + } + const std::string& get_payload() const { + return m_payload; + } + + /// Recycle the message + /** + * A request to recycle this message was received. Forward that request to + * the connection message manager for processing. Errors and exceptions + * from the manager's recycle member function should be passed back up the + * call chain. The caller to message::recycle will deal with them. + * + * Recycle must *only* be called by the message shared_ptr's destructor. + * Once recycled successfully, ownership of the memory has been passed to + * another system and must not be accessed again. + * + * @return true if the message was successfully recycled, false otherwise. + */ + bool recycle() { + typename con_msg_manager::ptr shared = m_manager.lock(); + + if (shared) { + return shared->(recycle(this)); + } else { + return false; + } + } +private: + con_msg_man_ptr m_manager; + + frame::opcode::value m_opcode; + std::string m_header; + std::string m_extension_data; + std::string m_payload; +}; + +namespace alloc { + +/// A connection message manager that allocates a new message for each +/// request. +template +class con_msg_manager { +public: + typedef lib::shared_ptr ptr; + typedef lib::weak_ptr weak_ptr; + + typedef typename message::ptr message_ptr; + + /// Get a message buffer with specified size + /** + * @param size Minimum size in bytes to request for the message payload. + * + * @return A shared pointer to a new message with specified size. + */ + message_ptr get_message(size_t size) const { + return lib::make_shared(size); + } + + /// Recycle a message + /** + * This method shouldn't be called. If it is, return false to indicate an + * error. The rest of the method recycle chain should notice this and free + * the memory. + * + * @param msg The message to be recycled. + * + * @return true if the message was successfully recycled, false otherwse. + */ + bool recycle(message * msg) { + return false; + } +}; + +/// An endpoint message manager that allocates a new manager for each +/// connection. +template +class endpoint_msg_manager { +public: + typedef typename con_msg_manager::ptr con_msg_man_ptr; + + /// Get a pointer to a connection message manager + /** + * @return A pointer to the requested connection message manager. + */ + con_msg_man_ptr get_manager() const { + return lib::make_shared(); + } +}; + +} // namespace alloc + +namespace pool { + +/// A connection messages manager that maintains a pool of messages that is +/// used to fulfill get_message requests. +class con_msg_manager { + +}; + +/// An endpoint manager that maintains a shared pool of connection managers +/// and returns an appropriate one for the requesting connection. +class endpoint_msg_manager { + +}; + +} // namespace pool + +} // namespace message_buffer +} // namespace websocketpp + +#endif // WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP diff --git a/websocketpp/processors/base.hpp b/websocketpp/processors/base.hpp new file mode 100644 index 00000000..ddb8b81a --- /dev/null +++ b/websocketpp/processors/base.hpp @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_BASE_HPP +#define WEBSOCKETPP_PROCESSOR_BASE_HPP + +#include +#include +#include + +#include +#include + +#include + +namespace websocketpp { +namespace processor { + +/// Constants related to processing WebSocket connections +namespace constants { + +static char const upgrade_token[] = "websocket"; +static char const connection_token[] = "upgrade"; +static char const handshake_guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +} // namespace constants + + +/// Processor class related error codes +namespace error_cat { +enum value { + BAD_REQUEST = 0, // Error was the result of improperly formatted user input + INTERNAL_ERROR = 1, // Error was a logic error internal to WebSocket++ + PROTOCOL_VIOLATION = 2, + MESSAGE_TOO_BIG = 3, + PAYLOAD_VIOLATION = 4 // Error was due to receiving invalid payload data +}; +} // namespace error_cat + +/// Error code category and codes used by all processor types +namespace error { +enum processor_errors { + /// Catch-all error for processor policy errors that don't fit in other + /// categories + general = 1, + + /// Error was the result of improperly formatted user input + bad_request, + + /// Processor encountered a protocol violation in an incoming message + protocol_violation, + + /// Processor encountered a message that was too large + message_too_big, + + /// Processor encountered invalid payload data. + invalid_payload, + + /// The processor method was called with invalid arguments + invalid_arguments, + + /// Opcode was invalid for requested operation + invalid_opcode, + + /// Control frame too large + control_too_big, + + /// Illegal use of reserved bit + invalid_rsv_bit, + + /// Fragmented control message + fragmented_control, + + /// Continuation without message + invalid_continuation, + + /// Clients may not send unmasked frames + masking_required, + + /// Servers may not send masked frames + masking_forbidden, + + /// Payload length not minimally encoded + non_minimal_encoding, + + /// Not supported on 32 bit systems + requires_64bit, + + /// Invalid UTF-8 encoding + invalid_utf8, + + /// Operation required not implemented functionality + not_implemented, + + /// Invalid HTTP method + invalid_http_method, + + /// Invalid HTTP version + invalid_http_version, + + /// Invalid HTTP status + invalid_http_status, + + /// Missing Required Header + missing_required_header, + + /// Embedded SHA-1 library error + sha1_library, + + /// No support for this feature in this protocol version. + no_protocol_support, + + /// Reserved close code used + reserved_close_code, + + /// Invalid close code used + invalid_close_code, + + /// Using a reason requires a close code + reason_requires_code, + + /// Error parsing subprotocols + subprotocol_parse_error, + + /// Error parsing extensions + extension_parse_error, + + /// Extension related operation was ignored because extensions are disabled + extensions_disabled, + + /// Short Ke3 read. Hybi00 requires a third key to be read from the 8 bytes + /// after the handshake. Less than 8 bytes were read. + short_key3 +}; + +/// Category for processor errors +class processor_category : public lib::error_category { +public: + processor_category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.processor"; + } + + std::string message(int value) const { + switch(value) { + case error::general: + return "Generic processor error"; + case error::bad_request: + return "invalid user input"; + case error::protocol_violation: + return "Generic protocol violation"; + case error::message_too_big: + return "A message was too large"; + case error::invalid_payload: + return "A payload contained invalid data"; + case error::invalid_arguments: + return "invalid function arguments"; + case error::invalid_opcode: + return "invalid opcode"; + case error::control_too_big: + return "Control messages are limited to fewer than 125 characters"; + case error::invalid_rsv_bit: + return "Invalid use of reserved bits"; + case error::fragmented_control: + return "Control messages cannot be fragmented"; + case error::invalid_continuation: + return "Invalid message continuation"; + case error::masking_required: + return "Clients may not send unmasked frames"; + case error::masking_forbidden: + return "Servers may not send masked frames"; + case error::non_minimal_encoding: + return "Payload length was not minimally encoded"; + case error::requires_64bit: + return "64 bit frames are not supported on 32 bit systems"; + case error::invalid_utf8: + return "Invalid UTF8 encoding"; + case error::not_implemented: + return "Operation required not implemented functionality"; + case error::invalid_http_method: + return "Invalid HTTP method."; + case error::invalid_http_version: + return "Invalid HTTP version."; + case error::invalid_http_status: + return "Invalid HTTP status."; + case error::missing_required_header: + return "A required HTTP header is missing"; + case error::sha1_library: + return "SHA-1 library error"; + case error::no_protocol_support: + return "The WebSocket protocol version in use does not support this feature"; + case error::reserved_close_code: + return "Reserved close code used"; + case error::invalid_close_code: + return "Invalid close code used"; + case error::reason_requires_code: + return "Using a close reason requires a valid close code"; + case error::subprotocol_parse_error: + return "Error parsing subprotocol header"; + case error::extension_parse_error: + return "Error parsing extension header"; + case error::extensions_disabled: + return "Extensions are disabled"; + case error::short_key3: + return "Short Hybi00 Key 3 read"; + default: + return "Unknown"; + } + } +}; + +/// Get a reference to a static copy of the processor error category +inline lib::error_category const & get_processor_category() { + static processor_category instance; + return instance; +} + +/// Create an error code with the given value and the processor category +inline lib::error_code make_error_code(error::processor_errors e) { + return lib::error_code(static_cast(e), get_processor_category()); +} + +/// Converts a processor error_code into a websocket close code +/** + * Looks up the appropriate WebSocket close code that should be sent after an + * error of this sort occurred. + * + * If the error is not in the processor category close::status::blank is + * returned. + * + * If the error isn't normally associated with reasons to close a connection + * (such as errors intended to be used internally or delivered to client + * applications, ex: invalid arguments) then + * close::status::internal_endpoint_error is returned. + */ +inline close::status::value to_ws(lib::error_code ec) { + if (ec.category() != get_processor_category()) { + return close::status::blank; + } + + switch (ec.value()) { + case error::protocol_violation: + case error::control_too_big: + case error::invalid_opcode: + case error::invalid_rsv_bit: + case error::fragmented_control: + case error::invalid_continuation: + case error::masking_required: + case error::masking_forbidden: + case error::reserved_close_code: + case error::invalid_close_code: + return close::status::protocol_error; + case error::invalid_payload: + case error::invalid_utf8: + return close::status::invalid_payload; + case error::message_too_big: + return close::status::message_too_big; + default: + return close::status::internal_endpoint_error; + } +} + +} // namespace error +} // namespace processor +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif //WEBSOCKETPP_PROCESSOR_BASE_HPP diff --git a/websocketpp/processors/hybi00.hpp b/websocketpp/processors/hybi00.hpp new file mode 100644 index 00000000..95ad9dfa --- /dev/null +++ b/websocketpp/processors/hybi00.hpp @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_HYBI00_HPP +#define WEBSOCKETPP_PROCESSOR_HYBI00_HPP + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace websocketpp { +namespace processor { + +/// Processor for Hybi Draft version 00 +/** + * There are many differences between Hybi 00 and Hybi 13 + */ +template +class hybi00 : public processor { +public: + typedef processor base; + + typedef typename config::request_type request_type; + typedef typename config::response_type response_type; + + typedef typename config::message_type message_type; + typedef typename message_type::ptr message_ptr; + + typedef typename config::con_msg_manager_type::ptr msg_manager_ptr; + + explicit hybi00(bool secure, bool p_is_server, msg_manager_ptr manager) + : processor(secure, p_is_server) + , msg_hdr(0x00) + , msg_ftr(0xff) + , m_state(HEADER) + , m_msg_manager(manager) {} + + int get_version() const { + return 0; + } + + lib::error_code validate_handshake(request_type const & r) const { + if (r.get_method() != "GET") { + return make_error_code(error::invalid_http_method); + } + + if (r.get_version() != "HTTP/1.1") { + return make_error_code(error::invalid_http_version); + } + + // required headers + // Host is required by HTTP/1.1 + // Connection is required by is_websocket_handshake + // Upgrade is required by is_websocket_handshake + if (r.get_header("Sec-WebSocket-Key1").empty() || + r.get_header("Sec-WebSocket-Key2").empty() || + r.get_header("Sec-WebSocket-Key3").empty()) + { + return make_error_code(error::missing_required_header); + } + + return lib::error_code(); + } + + lib::error_code process_handshake(request_type const & req, + std::string const & subprotocol, response_type & res) const + { + char key_final[16]; + + // copy key1 into final key + decode_client_key(req.get_header("Sec-WebSocket-Key1"), &key_final[0]); + + // copy key2 into final key + decode_client_key(req.get_header("Sec-WebSocket-Key2"), &key_final[4]); + + // copy key3 into final key + // key3 should be exactly 8 bytes. If it is more it will be truncated + // if it is less the final key will almost certainly be wrong. + // TODO: decide if it is best to silently fail here or produce some sort + // of warning or exception. + std::string const & key3 = req.get_header("Sec-WebSocket-Key3"); + std::copy(key3.c_str(), + key3.c_str()+(std::min)(static_cast(8), key3.size()), + &key_final[8]); + + res.append_header( + "Sec-WebSocket-Key3", + md5::md5_hash_string(std::string(key_final,16)) + ); + + res.append_header("Upgrade","WebSocket"); + res.append_header("Connection","Upgrade"); + + // Echo back client's origin unless our local application set a + // more restrictive one. + if (res.get_header("Sec-WebSocket-Origin").empty()) { + res.append_header("Sec-WebSocket-Origin",req.get_header("Origin")); + } + + // Echo back the client's request host unless our local application + // set a different one. + if (res.get_header("Sec-WebSocket-Location").empty()) { + uri_ptr uri = get_uri(req); + res.append_header("Sec-WebSocket-Location",uri->str()); + } + + if (!subprotocol.empty()) { + res.replace_header("Sec-WebSocket-Protocol",subprotocol); + } + + return lib::error_code(); + } + + /// Fill in a set of request headers for a client connection request + /** + * The Hybi 00 processor only implements incoming connections so this will + * always return an error. + * + * @param [out] req Set of headers to fill in + * @param [in] uri The uri being connected to + * @param [in] subprotocols The list of subprotocols to request + */ + lib::error_code client_handshake_request(request_type &, uri_ptr, + std::vector const &) const + { + return error::make_error_code(error::no_protocol_support); + } + + /// Validate the server's response to an outgoing handshake request + /** + * The Hybi 00 processor only implements incoming connections so this will + * always return an error. + * + * @param req The original request sent + * @param res The reponse to generate + * @return An error code, 0 on success, non-zero for other errors + */ + lib::error_code validate_server_handshake_response(request_type const &, + response_type &) const + { + return error::make_error_code(error::no_protocol_support); + } + + std::string get_raw(response_type const & res) const { + response_type temp = res; + temp.remove_header("Sec-WebSocket-Key3"); + return temp.raw() + res.get_header("Sec-WebSocket-Key3"); + } + + std::string const & get_origin(request_type const & r) const { + return r.get_header("Origin"); + } + + /// Extracts requested subprotocols from a handshake request + /** + * hybi00 does support subprotocols + * https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-1.9 + * + * @param [in] req The request to extract from + * @param [out] subprotocol_list A reference to a vector of strings to store + * the results in. + */ + lib::error_code extract_subprotocols(request_type const & req, + std::vector & subprotocol_list) + { + if (!req.get_header("Sec-WebSocket-Protocol").empty()) { + http::parameter_list p; + + if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) { + http::parameter_list::const_iterator it; + + for (it = p.begin(); it != p.end(); ++it) { + subprotocol_list.push_back(it->first); + } + } else { + return error::make_error_code(error::subprotocol_parse_error); + } + } + return lib::error_code(); + } + + uri_ptr get_uri(request_type const & request) const { + std::string h = request.get_header("Host"); + + size_t last_colon = h.rfind(":"); + size_t last_sbrace = h.rfind("]"); + + // no : = hostname with no port + // last : before ] = ipv6 literal with no port + // : with no ] = hostname with port + // : after ] = ipv6 literal with port + + if (last_colon == std::string::npos || + (last_sbrace != std::string::npos && last_sbrace > last_colon)) + { + return lib::make_shared(base::m_secure, h, request.get_uri()); + } else { + return lib::make_shared(base::m_secure, + h.substr(0,last_colon), + h.substr(last_colon+1), + request.get_uri()); + } + + // TODO: check if get_uri is a full uri + } + + /// Get hybi00 handshake key3 + /** + * @todo This doesn't appear to be used anymore. It might be able to be + * removed + */ + std::string get_key3() const { + return ""; + } + + /// Process new websocket connection bytes + size_t consume(uint8_t * buf, size_t len, lib::error_code & ec) { + // if in state header we are expecting a 0x00 byte, if we don't get one + // it is a fatal error + size_t p = 0; // bytes processed + size_t l = 0; + + ec = lib::error_code(); + + while (p < len) { + if (m_state == HEADER) { + if (buf[p] == msg_hdr) { + p++; + m_msg_ptr = m_msg_manager->get_message(frame::opcode::text,1); + + if (!m_msg_ptr) { + ec = make_error_code(websocketpp::error::no_incoming_buffers); + m_state = FATAL_ERROR; + } else { + m_state = PAYLOAD; + } + } else { + ec = make_error_code(error::protocol_violation); + m_state = FATAL_ERROR; + } + } else if (m_state == PAYLOAD) { + uint8_t *it = std::find(buf+p,buf+len,msg_ftr); + + // 0 1 2 3 4 5 + // 0x00 0x23 0x23 0x23 0xff 0xXX + + // Copy payload bytes into message + l = static_cast(it-(buf+p)); + m_msg_ptr->append_payload(buf+p,l); + p += l; + + if (it != buf+len) { + // message is done, copy it and the trailing + p++; + // TODO: validation + m_state = READY; + } + } else { + // TODO + break; + } + } + // If we get one, we create a new message and move to application state + + // if in state application we are copying bytes into the output message + // and validating them for UTF8 until we hit a 0xff byte. Once we hit + // 0x00, the message is complete and is dispatched. Then we go back to + // header state. + + //ec = make_error_code(error::not_implemented); + return p; + } + + bool ready() const { + return (m_state == READY); + } + + bool get_error() const { + return false; + } + + message_ptr get_message() { + message_ptr ret = m_msg_ptr; + m_msg_ptr = message_ptr(); + m_state = HEADER; + return ret; + } + + /// Prepare a message for writing + /** + * Performs validation, masking, compression, etc. will return an error if + * there was an error, otherwise msg will be ready to be written + */ + virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) + { + if (!in || !out) { + return make_error_code(error::invalid_arguments); + } + + // TODO: check if the message is prepared already + + // validate opcode + if (in->get_opcode() != frame::opcode::text) { + return make_error_code(error::invalid_opcode); + } + + std::string& i = in->get_raw_payload(); + //std::string& o = out->get_raw_payload(); + + // validate payload utf8 + if (!utf8_validator::validate(i)) { + return make_error_code(error::invalid_payload); + } + + // generate header + out->set_header(std::string(reinterpret_cast(&msg_hdr),1)); + + // process payload + out->set_payload(i); + out->append_payload(std::string(reinterpret_cast(&msg_ftr),1)); + + // hybi00 doesn't support compression + // hybi00 doesn't have masking + + out->set_prepared(true); + + return lib::error_code(); + } + + /// Prepare a ping frame + /** + * Hybi 00 doesn't support pings so this will always return an error + * + * @param in The string to use for the ping payload + * @param out The message buffer to prepare the ping in. + * @return Status code, zero on success, non-zero on failure + */ + lib::error_code prepare_ping(std::string const &, message_ptr) const + { + return lib::error_code(error::no_protocol_support); + } + + /// Prepare a pong frame + /** + * Hybi 00 doesn't support pongs so this will always return an error + * + * @param in The string to use for the pong payload + * @param out The message buffer to prepare the pong in. + * @return Status code, zero on success, non-zero on failure + */ + lib::error_code prepare_pong(std::string const &, message_ptr) const + { + return lib::error_code(error::no_protocol_support); + } + + /// Prepare a close frame + /** + * Hybi 00 doesn't support the close code or reason so these parameters are + * ignored. + * + * @param code The close code to send + * @param reason The reason string to send + * @param out The message buffer to prepare the fame in + * @return Status code, zero on success, non-zero on failure + */ + lib::error_code prepare_close(close::status::value, std::string const &, + message_ptr out) const + { + if (!out) { + return lib::error_code(error::invalid_arguments); + } + + std::string val; + val.append(1,'\xff'); + val.append(1,'\x00'); + out->set_payload(val); + out->set_prepared(true); + + return lib::error_code(); + } +private: + void decode_client_key(std::string const & key, char * result) const { + unsigned int spaces = 0; + std::string digits; + uint32_t num; + + // key2 + for (size_t i = 0; i < key.size(); i++) { + if (key[i] == ' ') { + spaces++; + } else if (key[i] >= '0' && key[i] <= '9') { + digits += key[i]; + } + } + + num = static_cast(strtoul(digits.c_str(), NULL, 10)); + if (spaces > 0 && num > 0) { + num = htonl(num/spaces); + std::copy(reinterpret_cast(&num), + reinterpret_cast(&num)+4, + result); + } else { + std::fill(result,result+4,0); + } + } + + enum state { + HEADER = 0, + PAYLOAD = 1, + READY = 2, + FATAL_ERROR = 3 + }; + + uint8_t const msg_hdr; + uint8_t const msg_ftr; + + state m_state; + + msg_manager_ptr m_msg_manager; + message_ptr m_msg_ptr; + utf8_validator::validator m_validator; +}; + +} // namespace processor +} // namespace websocketpp + +#endif //WEBSOCKETPP_PROCESSOR_HYBI00_HPP diff --git a/websocketpp/processors/hybi07.hpp b/websocketpp/processors/hybi07.hpp new file mode 100644 index 00000000..14b67c21 --- /dev/null +++ b/websocketpp/processors/hybi07.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_HYBI07_HPP +#define WEBSOCKETPP_PROCESSOR_HYBI07_HPP + +#include + +#include +#include + +namespace websocketpp { +namespace processor { + +/// Processor for Hybi Draft version 07 +/** + * The primary difference between 07 and 08 is a version number. + */ +template +class hybi07 : public hybi08 { +public: + typedef typename config::request_type request_type; + + typedef typename config::con_msg_manager_type::ptr msg_manager_ptr; + typedef typename config::rng_type rng_type; + + explicit hybi07(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng) + : hybi08(secure, p_is_server, manager, rng) {} + + /// Fill in a set of request headers for a client connection request + /** + * The Hybi 07 processor only implements incoming connections so this will + * always return an error. + * + * @param [out] req Set of headers to fill in + * @param [in] uri The uri being connected to + * @param [in] subprotocols The list of subprotocols to request + */ + lib::error_code client_handshake_request(request_type &, uri_ptr, + std::vector const &) const + { + return error::make_error_code(error::no_protocol_support); + } + + int get_version() const { + return 7; + } +private: +}; + +} // namespace processor +} // namespace websocketpp + +#endif //WEBSOCKETPP_PROCESSOR_HYBI07_HPP diff --git a/websocketpp/processors/hybi08.hpp b/websocketpp/processors/hybi08.hpp new file mode 100644 index 00000000..15f6e658 --- /dev/null +++ b/websocketpp/processors/hybi08.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_HYBI08_HPP +#define WEBSOCKETPP_PROCESSOR_HYBI08_HPP + +#include + +#include +#include + +namespace websocketpp { +namespace processor { + +/// Processor for Hybi Draft version 08 +/** + * The primary difference between 08 and 13 is a different origin header name + */ +template +class hybi08 : public hybi13 { +public: + typedef hybi08 type; + typedef typename config::request_type request_type; + + typedef typename config::con_msg_manager_type::ptr msg_manager_ptr; + typedef typename config::rng_type rng_type; + + explicit hybi08(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng) + : hybi13(secure, p_is_server, manager, rng) {} + + /// Fill in a set of request headers for a client connection request + /** + * The Hybi 08 processor only implements incoming connections so this will + * always return an error. + * + * @param [out] req Set of headers to fill in + * @param [in] uri The uri being connected to + * @param [in] subprotocols The list of subprotocols to request + */ + lib::error_code client_handshake_request(request_type &, uri_ptr, + std::vector const &) const + { + return error::make_error_code(error::no_protocol_support); + } + + int get_version() const { + return 8; + } + + std::string const & get_origin(request_type const & r) const { + return r.get_header("Sec-WebSocket-Origin"); + } +private: +}; + +} // namespace processor +} // namespace websocketpp + +#endif //WEBSOCKETPP_PROCESSOR_HYBI08_HPP diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp new file mode 100644 index 00000000..79486654 --- /dev/null +++ b/websocketpp/processors/hybi13.hpp @@ -0,0 +1,1056 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_HYBI13_HPP +#define WEBSOCKETPP_PROCESSOR_HYBI13_HPP + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace websocketpp { +namespace processor { + +/// Processor for Hybi version 13 (RFC6455) +template +class hybi13 : public processor { +public: + typedef processor base; + + typedef typename config::request_type request_type; + typedef typename config::response_type response_type; + + typedef typename config::message_type message_type; + typedef typename message_type::ptr message_ptr; + + typedef typename config::con_msg_manager_type msg_manager_type; + typedef typename msg_manager_type::ptr msg_manager_ptr; + typedef typename config::rng_type rng_type; + + typedef typename config::permessage_deflate_type permessage_deflate_type; + + typedef std::pair err_str_pair; + + explicit hybi13(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng) + : processor(secure, p_is_server) + , m_msg_manager(manager) + , m_rng(rng) + { + reset_headers(); + } + + int get_version() const { + return 13; + } + + bool has_permessage_deflate() const { + return m_permessage_deflate.is_implemented(); + } + + err_str_pair negotiate_extensions(request_type const & request) { + return negotiate_extensions_helper(request); + } + + err_str_pair negotiate_extensions(response_type const & response) { + return negotiate_extensions_helper(response); + } + + /// Extension negotiation helper function + /** + * This exists mostly because the code for requests and responses is + * identical and I can't have virtual template methods. + */ + template + err_str_pair negotiate_extensions_helper(header_type const & header) { + err_str_pair ret; + + // Respect blanket disabling of all extensions and don't even parse + // the extension header + if (!config::enable_extensions) { + ret.first = make_error_code(error::extensions_disabled); + return ret; + } + + http::parameter_list p; + + bool error = header.get_header_as_plist("Sec-WebSocket-Extensions",p); + + if (error) { + ret.first = make_error_code(error::extension_parse_error); + return ret; + } + + // If there are no extensions parsed then we are done! + if (p.size() == 0) { + return ret; + } + + http::parameter_list::const_iterator it; + + if (m_permessage_deflate.is_implemented()) { + err_str_pair neg_ret; + for (it = p.begin(); it != p.end(); ++it) { + // look through each extension, if the key is permessage-deflate + if (it->first == "permessage-deflate") { + // if we have already successfully negotiated this extension + // then skip any other requests to negotiate the same one + // with different parameters + if (m_permessage_deflate.is_enabled()) { + continue; + } + + + neg_ret = m_permessage_deflate.negotiate(it->second); + + if (neg_ret.first) { + // Figure out if this is an error that should halt all + // extension negotiations or simply cause negotiation of + // this specific extension to fail. + //std::cout << "permessage-compress negotiation failed: " + // << neg_ret.first.message() << std::endl; + } else { + // Note: this list will need commas if WebSocket++ ever + // supports more than one extension + ret.second += neg_ret.second; + m_permessage_deflate.init(base::m_server); + continue; + } + } + } + } + + return ret; + } + + lib::error_code validate_handshake(request_type const & r) const { + if (r.get_method() != "GET") { + return make_error_code(error::invalid_http_method); + } + + if (r.get_version() != "HTTP/1.1") { + return make_error_code(error::invalid_http_version); + } + + // required headers + // Host is required by HTTP/1.1 + // Connection is required by is_websocket_handshake + // Upgrade is required by is_websocket_handshake + if (r.get_header("Sec-WebSocket-Key").empty()) { + return make_error_code(error::missing_required_header); + } + + return lib::error_code(); + } + + /* TODO: the 'subprotocol' parameter may need to be expanded into a more + * generic struct if other user input parameters to the processed handshake + * are found. + */ + lib::error_code process_handshake(request_type const & request, + std::string const & subprotocol, response_type & response) const + { + std::string server_key = request.get_header("Sec-WebSocket-Key"); + + lib::error_code ec = process_handshake_key(server_key); + + if (ec) { + return ec; + } + + response.replace_header("Sec-WebSocket-Accept",server_key); + response.append_header("Upgrade",constants::upgrade_token); + response.append_header("Connection",constants::connection_token); + + if (!subprotocol.empty()) { + response.replace_header("Sec-WebSocket-Protocol",subprotocol); + } + + return lib::error_code(); + } + + /// Fill in a set of request headers for a client connection request + /** + * @param [out] req Set of headers to fill in + * @param [in] uri The uri being connected to + * @param [in] subprotocols The list of subprotocols to request + */ + lib::error_code client_handshake_request(request_type & req, uri_ptr + uri, std::vector const & subprotocols) const + { + req.set_method("GET"); + req.set_uri(uri->get_resource()); + req.set_version("HTTP/1.1"); + + req.append_header("Upgrade","websocket"); + req.append_header("Connection","Upgrade"); + req.replace_header("Sec-WebSocket-Version","13"); + req.replace_header("Host",uri->get_host_port()); + + if (!subprotocols.empty()) { + std::ostringstream result; + std::vector::const_iterator it = subprotocols.begin(); + result << *it++; + while (it != subprotocols.end()) { + result << ", " << *it++; + } + + req.replace_header("Sec-WebSocket-Protocol",result.str()); + } + + // Generate handshake key + frame::uint32_converter conv; + unsigned char raw_key[16]; + + for (int i = 0; i < 4; i++) { + conv.i = m_rng(); + std::copy(conv.c,conv.c+4,&raw_key[i*4]); + } + + req.replace_header("Sec-WebSocket-Key",base64_encode(raw_key, 16)); + + if (m_permessage_deflate.is_implemented()) { + std::string offer = m_permessage_deflate.generate_offer(); + if (!offer.empty()) { + req.replace_header("Sec-WebSocket-Extensions",offer); + } + } + + return lib::error_code(); + } + + /// Validate the server's response to an outgoing handshake request + /** + * @param req The original request sent + * @param res The reponse to generate + * @return An error code, 0 on success, non-zero for other errors + */ + lib::error_code validate_server_handshake_response(request_type const & req, + response_type& res) const + { + // A valid response has an HTTP 101 switching protocols code + if (res.get_status_code() != http::status_code::switching_protocols) { + return error::make_error_code(error::invalid_http_status); + } + + // And the upgrade token in an upgrade header + std::string const & upgrade_header = res.get_header("Upgrade"); + if (utility::ci_find_substr(upgrade_header, constants::upgrade_token, + sizeof(constants::upgrade_token)-1) == upgrade_header.end()) + { + return error::make_error_code(error::missing_required_header); + } + + // And the websocket token in the connection header + std::string const & con_header = res.get_header("Connection"); + if (utility::ci_find_substr(con_header, constants::connection_token, + sizeof(constants::connection_token)-1) == con_header.end()) + { + return error::make_error_code(error::missing_required_header); + } + + // And has a valid Sec-WebSocket-Accept value + std::string key = req.get_header("Sec-WebSocket-Key"); + lib::error_code ec = process_handshake_key(key); + + if (ec || key != res.get_header("Sec-WebSocket-Accept")) { + return error::make_error_code(error::missing_required_header); + } + + // check extensions + + return lib::error_code(); + } + + std::string get_raw(response_type const & res) const { + return res.raw(); + } + + std::string const & get_origin(request_type const & r) const { + return r.get_header("Origin"); + } + + lib::error_code extract_subprotocols(request_type const & req, + std::vector & subprotocol_list) + { + if (!req.get_header("Sec-WebSocket-Protocol").empty()) { + http::parameter_list p; + + if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) { + http::parameter_list::const_iterator it; + + for (it = p.begin(); it != p.end(); ++it) { + subprotocol_list.push_back(it->first); + } + } else { + return error::make_error_code(error::subprotocol_parse_error); + } + } + return lib::error_code(); + } + + uri_ptr get_uri(request_type const & request) const { + return get_uri_from_host(request,(base::m_secure ? "wss" : "ws")); + } + + /// Process new websocket connection bytes + /** + * + * Hybi 13 data streams represent a series of variable length frames. Each + * frame is made up of a series of fixed length fields. The lengths of later + * fields are contained in earlier fields. The first field length is fixed + * by the spec. + * + * This processor represents a state machine that keeps track of what field + * is presently being read and how many more bytes are needed to complete it + * + * + * + * + * Read two header bytes + * Extract full frame length. + * Read extra header bytes + * Validate frame header (including extension validate) + * Read extension data into extension message state object + * Read payload data into payload + * + * @param buf Input buffer + * + * @param len Length of input buffer + * + * @return Number of bytes processed or zero on error + */ + size_t consume(uint8_t * buf, size_t len, lib::error_code & ec) { + size_t p = 0; + + ec = lib::error_code(); + + //std::cout << "consume: " << utility::to_hex(buf,len) << std::endl; + + // Loop while we don't have a message ready and we still have bytes + // left to process. + while (m_state != READY && m_state != FATAL_ERROR && + (p < len || m_bytes_needed == 0)) + { + if (m_state == HEADER_BASIC) { + p += this->copy_basic_header_bytes(buf+p,len-p); + + if (m_bytes_needed > 0) { + continue; + } + + ec = this->validate_incoming_basic_header( + m_basic_header, base::m_server, !m_data_msg.msg_ptr + ); + if (ec) {break;} + + // extract full header size and adjust consume state accordingly + m_state = HEADER_EXTENDED; + m_cursor = 0; + m_bytes_needed = frame::get_header_len(m_basic_header) - + frame::BASIC_HEADER_LENGTH; + } else if (m_state == HEADER_EXTENDED) { + p += this->copy_extended_header_bytes(buf+p,len-p); + + if (m_bytes_needed > 0) { + continue; + } + + ec = validate_incoming_extended_header(m_basic_header,m_extended_header); + if (ec){break;} + + m_state = APPLICATION; + m_bytes_needed = static_cast(get_payload_size(m_basic_header,m_extended_header)); + + // check if this frame is the start of a new message and set up + // the appropriate message metadata. + frame::opcode::value op = frame::get_opcode(m_basic_header); + + // TODO: get_message failure conditions + + if (frame::opcode::is_control(op)) { + m_control_msg = msg_metadata( + m_msg_manager->get_message(op,m_bytes_needed), + frame::get_masking_key(m_basic_header,m_extended_header) + ); + + m_current_msg = &m_control_msg; + } else { + if (!m_data_msg.msg_ptr) { + if (m_bytes_needed > base::m_max_message_size) { + ec = make_error_code(error::message_too_big); + break; + } + + m_data_msg = msg_metadata( + m_msg_manager->get_message(op,m_bytes_needed), + frame::get_masking_key(m_basic_header,m_extended_header) + ); + + if (m_permessage_deflate.is_enabled()) { + m_data_msg.msg_ptr->set_compressed(frame::get_rsv1(m_basic_header)); + } + } else { + // Fetch the underlying payload buffer from the data message we + // are writing into. + std::string & out = m_data_msg.msg_ptr->get_raw_payload(); + + if (out.size() + m_bytes_needed > base::m_max_message_size) { + ec = make_error_code(error::message_too_big); + break; + } + + // Each frame starts a new masking key. All other state + // remains between frames. + m_data_msg.prepared_key = prepare_masking_key( + frame::get_masking_key( + m_basic_header, + m_extended_header + ) + ); + + out.reserve(out.size() + m_bytes_needed); + } + m_current_msg = &m_data_msg; + } + } else if (m_state == EXTENSION) { + m_state = APPLICATION; + } else if (m_state == APPLICATION) { + size_t bytes_to_process = (std::min)(m_bytes_needed,len-p); + + if (bytes_to_process > 0) { + p += this->process_payload_bytes(buf+p,bytes_to_process,ec); + + if (ec) {break;} + } + + if (m_bytes_needed > 0) { + continue; + } + + // If this was the last frame in the message set the ready flag. + // Otherwise, reset processor state to read additional frames. + if (frame::get_fin(m_basic_header)) { + ec = finalize_message(); + if (ec) { + break; + } + } else { + this->reset_headers(); + } + } else { + // shouldn't be here + ec = make_error_code(error::general); + return 0; + } + } + + return p; + } + + /// Perform any finalization actions on an incoming message + /** + * Called after the full message is received. Provides the opportunity for + * extensions to complete any data post processing as well as final UTF8 + * validation checks for text messages. + * + * @return A code indicating errors, if any + */ + lib::error_code finalize_message() { + std::string & out = m_current_msg->msg_ptr->get_raw_payload(); + + // if the frame is compressed, append the compression + // trailer and flush the compression buffer. + if (m_permessage_deflate.is_enabled() + && m_current_msg->msg_ptr->get_compressed()) + { + uint8_t trailer[4] = {0x00, 0x00, 0xff, 0xff}; + + // Decompress current buffer into the message buffer + lib::error_code ec; + ec = m_permessage_deflate.decompress(trailer,4,out); + if (ec) { + return ec; + } + } + + // ensure that text messages end on a valid UTF8 code point + if (frame::get_opcode(m_basic_header) == frame::opcode::TEXT) { + if (!m_current_msg->validator.complete()) { + return make_error_code(error::invalid_utf8); + } + } + + m_state = READY; + + return lib::error_code(); + } + + void reset_headers() { + m_state = HEADER_BASIC; + m_bytes_needed = frame::BASIC_HEADER_LENGTH; + + m_basic_header.b0 = 0x00; + m_basic_header.b1 = 0x00; + + std::fill_n( + m_extended_header.bytes, + frame::MAX_EXTENDED_HEADER_LENGTH, + 0x00 + ); + } + + /// Test whether or not the processor has a message ready + bool ready() const { + return (m_state == READY); + } + + message_ptr get_message() { + if (!ready()) { + return message_ptr(); + } + message_ptr ret = m_current_msg->msg_ptr; + m_current_msg->msg_ptr.reset(); + + if (frame::opcode::is_control(ret->get_opcode())) { + m_control_msg.msg_ptr.reset(); + } else { + m_data_msg.msg_ptr.reset(); + } + + this->reset_headers(); + + return ret; + } + + /// Test whether or not the processor is in a fatal error state. + bool get_error() const { + return m_state == FATAL_ERROR; + } + + size_t get_bytes_needed() const { + return m_bytes_needed; + } + + /// Prepare a user data message for writing + /** + * Performs validation, masking, compression, etc. will return an error if + * there was an error, otherwise msg will be ready to be written + * + * TODO: tests + * + * @param in An unprepared message to prepare + * @param out A message to be overwritten with the prepared message + * @return error code + */ + virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) + { + if (!in || !out) { + return make_error_code(error::invalid_arguments); + } + + frame::opcode::value op = in->get_opcode(); + + // validate opcode: only regular data frames + if (frame::opcode::is_control(op)) { + return make_error_code(error::invalid_opcode); + } + + std::string& i = in->get_raw_payload(); + std::string& o = out->get_raw_payload(); + + // validate payload utf8 + if (op == frame::opcode::TEXT && !utf8_validator::validate(i)) { + return make_error_code(error::invalid_payload); + } + + frame::masking_key_type key; + bool masked = !base::m_server; + bool compressed = m_permessage_deflate.is_enabled() + && in->get_compressed(); + bool fin = in->get_fin(); + + if (masked) { + // Generate masking key. + key.i = m_rng(); + } else { + key.i = 0; + } + + // prepare payload + if (compressed) { + // compress and store in o after header. + m_permessage_deflate.compress(i,o); + + if (o.size() < 4) { + return make_error_code(error::general); + } + + // Strip trailing 4 0x00 0x00 0xff 0xff bytes before writing to the + // wire + o.resize(o.size()-4); + + // mask in place if necessary + if (masked) { + this->masked_copy(o,o,key); + } + } else { + // no compression, just copy data into the output buffer + o.resize(i.size()); + + // if we are masked, have the masking function write to the output + // buffer directly to avoid another copy. If not masked, copy + // directly without masking. + if (masked) { + this->masked_copy(i,o,key); + } else { + std::copy(i.begin(),i.end(),o.begin()); + } + } + + // generate header + frame::basic_header h(op,o.size(),fin,masked,compressed); + + if (masked) { + frame::extended_header e(o.size(),key.i); + out->set_header(frame::prepare_header(h,e)); + } else { + frame::extended_header e(o.size()); + out->set_header(frame::prepare_header(h,e)); + } + + out->set_prepared(true); + out->set_opcode(op); + + return lib::error_code(); + } + + /// Get URI + lib::error_code prepare_ping(std::string const & in, message_ptr out) const { + return this->prepare_control(frame::opcode::PING,in,out); + } + + lib::error_code prepare_pong(std::string const & in, message_ptr out) const { + return this->prepare_control(frame::opcode::PONG,in,out); + } + + virtual lib::error_code prepare_close(close::status::value code, + std::string const & reason, message_ptr out) const + { + if (close::status::reserved(code)) { + return make_error_code(error::reserved_close_code); + } + + if (close::status::invalid(code) && code != close::status::no_status) { + return make_error_code(error::invalid_close_code); + } + + if (code == close::status::no_status && reason.size() > 0) { + return make_error_code(error::reason_requires_code); + } + + if (reason.size() > frame:: limits::payload_size_basic-2) { + return make_error_code(error::control_too_big); + } + + std::string payload; + + if (code != close::status::no_status) { + close::code_converter val; + val.i = htons(code); + + payload.resize(reason.size()+2); + + payload[0] = val.c[0]; + payload[1] = val.c[1]; + + std::copy(reason.begin(),reason.end(),payload.begin()+2); + } + + return this->prepare_control(frame::opcode::CLOSE,payload,out); + } +protected: + /// Convert a client handshake key into a server response key in place + lib::error_code process_handshake_key(std::string & key) const { + key.append(constants::handshake_guid); + + unsigned char message_digest[20]; + sha1::calc(key.c_str(),key.length(),message_digest); + key = base64_encode(message_digest,20); + + return lib::error_code(); + } + + /// Reads bytes from buf into m_basic_header + size_t copy_basic_header_bytes(uint8_t const * buf, size_t len) { + if (len == 0 || m_bytes_needed == 0) { + return 0; + } + + if (len > 1) { + // have at least two bytes + if (m_bytes_needed == 2) { + m_basic_header.b0 = buf[0]; + m_basic_header.b1 = buf[1]; + m_bytes_needed -= 2; + return 2; + } else { + m_basic_header.b1 = buf[0]; + m_bytes_needed--; + return 1; + } + } else { + // have exactly one byte + if (m_bytes_needed == 2) { + m_basic_header.b0 = buf[0]; + m_bytes_needed--; + return 1; + } else { + m_basic_header.b1 = buf[0]; + m_bytes_needed--; + return 1; + } + } + } + + /// Reads bytes from buf into m_extended_header + size_t copy_extended_header_bytes(uint8_t const * buf, size_t len) { + size_t bytes_to_read = (std::min)(m_bytes_needed,len); + + std::copy(buf,buf+bytes_to_read,m_extended_header.bytes+m_cursor); + m_cursor += bytes_to_read; + m_bytes_needed -= bytes_to_read; + + return bytes_to_read; + } + + /// Reads bytes from buf into message payload + /** + * This function performs unmasking and uncompression, validates the + * decoded bytes, and writes them to the appropriate message buffer. + * + * This member function will use the input buffer as stratch space for its + * work. The raw input bytes will not be preserved. This applies only to the + * bytes actually needed. At most min(m_bytes_needed,len) will be processed. + * + * @param buf Input/working buffer + * @param len Length of buf + * @return Number of bytes processed or zero in case of an error + */ + size_t process_payload_bytes(uint8_t * buf, size_t len, lib::error_code& ec) + { + // unmask if masked + if (frame::get_masked(m_basic_header)) { + m_current_msg->prepared_key = frame::byte_mask_circ( + buf, len, m_current_msg->prepared_key); + // TODO: SIMD masking + } + + std::string & out = m_current_msg->msg_ptr->get_raw_payload(); + size_t offset = out.size(); + + // decompress message if needed. + if (m_permessage_deflate.is_enabled() + && m_current_msg->msg_ptr->get_compressed()) + { + // Decompress current buffer into the message buffer + ec = m_permessage_deflate.decompress(buf,len,out); + if (ec) { + return 0; + } + } else { + // No compression, straight copy + out.append(reinterpret_cast(buf),len); + } + + // validate unmasked, decompressed values + if (m_current_msg->msg_ptr->get_opcode() == frame::opcode::TEXT) { + if (!m_current_msg->validator.decode(out.begin()+offset,out.end())) { + ec = make_error_code(error::invalid_utf8); + return 0; + } + } + + m_bytes_needed -= len; + + return len; + } + + /// Validate an incoming basic header + /** + * Validates an incoming hybi13 basic header. + * + * @param h The basic header to validate + * @param is_server Whether or not the endpoint that received this frame + * is a server. + * @param new_msg Whether or not this is the first frame of the message + * @return 0 on success or a non-zero error code on failure + */ + lib::error_code validate_incoming_basic_header(frame::basic_header const & h, + bool is_server, bool new_msg) const + { + frame::opcode::value op = frame::get_opcode(h); + + // Check control frame size limit + if (frame::opcode::is_control(op) && + frame::get_basic_size(h) > frame::limits::payload_size_basic) + { + return make_error_code(error::control_too_big); + } + + // Check that RSV bits are clear + // The only RSV bits allowed are rsv1 if the permessage_compress + // extension is enabled for this connection and the message is not + // a control message. + // + // TODO: unit tests for this + if (frame::get_rsv1(h) && (!m_permessage_deflate.is_enabled() + || frame::opcode::is_control(op))) + { + return make_error_code(error::invalid_rsv_bit); + } + + if (frame::get_rsv2(h) || frame::get_rsv3(h)) { + return make_error_code(error::invalid_rsv_bit); + } + + // Check for reserved opcodes + if (frame::opcode::reserved(op)) { + return make_error_code(error::invalid_opcode); + } + + // Check for invalid opcodes + // TODO: unit tests for this? + if (frame::opcode::invalid(op)) { + return make_error_code(error::invalid_opcode); + } + + // Check for fragmented control message + if (frame::opcode::is_control(op) && !frame::get_fin(h)) { + return make_error_code(error::fragmented_control); + } + + // Check for continuation without an active message + if (new_msg && op == frame::opcode::CONTINUATION) { + return make_error_code(error::invalid_continuation); + } + + // Check for new data frame when expecting continuation + if (!new_msg && !frame::opcode::is_control(op) && + op != frame::opcode::CONTINUATION) + { + return make_error_code(error::invalid_continuation); + } + + // Servers should reject any unmasked frames from clients. + // Clients should reject any masked frames from servers. + if (is_server && !frame::get_masked(h)) { + return make_error_code(error::masking_required); + } else if (!is_server && frame::get_masked(h)) { + return make_error_code(error::masking_forbidden); + } + + return lib::error_code(); + } + + /// Validate an incoming extended header + /** + * Validates an incoming hybi13 full header. + * + * @todo unit test for the >32 bit frames on 32 bit systems case + * + * @param h The basic header to validate + * @param e The extended header to validate + * @return An error_code, non-zero values indicate why the validation + * failed + */ + lib::error_code validate_incoming_extended_header(frame::basic_header h, + frame::extended_header e) const + { + uint8_t basic_size = frame::get_basic_size(h); + uint64_t payload_size = frame::get_payload_size(h,e); + + // Check for non-minimally encoded payloads + if (basic_size == frame::payload_size_code_16bit && + payload_size <= frame::limits::payload_size_basic) + { + return make_error_code(error::non_minimal_encoding); + } + + if (basic_size == frame::payload_size_code_64bit && + payload_size <= frame::limits::payload_size_extended) + { + return make_error_code(error::non_minimal_encoding); + } + + // Check for >32bit frames on 32 bit systems + if (sizeof(size_t) == 4 && (payload_size >> 32)) { + return make_error_code(error::requires_64bit); + } + + return lib::error_code(); + } + + /// Copy and mask/unmask in one operation + /** + * Reads input from one string and writes unmasked output to another. + * + * @param [in] i The input string. + * @param [out] o The output string. + * @param [in] key The masking key to use for masking/unmasking + */ + void masked_copy (std::string const & i, std::string & o, + frame::masking_key_type key) const + { + frame::byte_mask(i.begin(),i.end(),o.begin(),key); + // TODO: SIMD masking + } + + /// Generic prepare control frame with opcode and payload. + /** + * Internal control frame building method. Handles validation, masking, etc + * + * @param op The control opcode to use + * @param payload The payload to use + * @param out The message buffer to store the prepared frame in + * @return Status code, zero on success, non-zero on error + */ + lib::error_code prepare_control(frame::opcode::value op, + std::string const & payload, message_ptr out) const + { + if (!out) { + return make_error_code(error::invalid_arguments); + } + + if (!frame::opcode::is_control(op)) { + return make_error_code(error::invalid_opcode); + } + + if (payload.size() > frame::limits::payload_size_basic) { + return make_error_code(error::control_too_big); + } + + frame::masking_key_type key; + bool masked = !base::m_server; + + frame::basic_header h(op,payload.size(),true,masked); + + std::string & o = out->get_raw_payload(); + o.resize(payload.size()); + + if (masked) { + // Generate masking key. + key.i = m_rng(); + + frame::extended_header e(payload.size(),key.i); + out->set_header(frame::prepare_header(h,e)); + this->masked_copy(payload,o,key); + } else { + frame::extended_header e(payload.size()); + out->set_header(frame::prepare_header(h,e)); + std::copy(payload.begin(),payload.end(),o.begin()); + } + + out->set_opcode(op); + out->set_prepared(true); + + return lib::error_code(); + } + + enum state { + HEADER_BASIC = 0, + HEADER_EXTENDED = 1, + EXTENSION = 2, + APPLICATION = 3, + READY = 4, + FATAL_ERROR = 5 + }; + + /// This data structure holds data related to processing a message, such as + /// the buffer it is being written to, its masking key, its UTF8 validation + /// state, and sometimes its compression state. + struct msg_metadata { + msg_metadata() {} + msg_metadata(message_ptr m, size_t p) : msg_ptr(m),prepared_key(p) {} + msg_metadata(message_ptr m, frame::masking_key_type p) + : msg_ptr(m) + , prepared_key(prepare_masking_key(p)) {} + + message_ptr msg_ptr; // pointer to the message data buffer + size_t prepared_key; // prepared masking key + utf8_validator::validator validator; // utf8 validation state + }; + + // Basic header of the frame being read + frame::basic_header m_basic_header; + + // Pointer to a manager that can create message buffers for us. + msg_manager_ptr m_msg_manager; + + // Number of bytes needed to complete the current operation + size_t m_bytes_needed; + + // Number of extended header bytes read + size_t m_cursor; + + // Metadata for the current data msg + msg_metadata m_data_msg; + // Metadata for the current control msg + msg_metadata m_control_msg; + + // Pointer to the metadata associated with the frame being read + msg_metadata * m_current_msg; + + // Extended header of current frame + frame::extended_header m_extended_header; + + rng_type & m_rng; + + // Overall state of the processor + state m_state; + + // Extensions + permessage_deflate_type m_permessage_deflate; +}; + +} // namespace processor +} // namespace websocketpp + +#endif //WEBSOCKETPP_PROCESSOR_HYBI13_HPP diff --git a/websocketpp/processors/processor.hpp b/websocketpp/processors/processor.hpp new file mode 100644 index 00000000..5131cc45 --- /dev/null +++ b/websocketpp/processors/processor.hpp @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_PROCESSOR_HPP +#define WEBSOCKETPP_PROCESSOR_HPP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace websocketpp { +/// Processors encapsulate the protocol rules specific to each WebSocket version +/** + * The processors namespace includes a number of free functions that operate on + * various WebSocket related data structures and perform processing that is not + * related to specific versions of the protocol. + * + * It also includes the abstract interface for the protocol specific processing + * engines. These engines wrap all of the logic necessary for parsing and + * validating WebSocket handshakes and messages of specific protocol version + * and set of allowed extensions. + * + * An instance of a processor represents the state of a single WebSocket + * connection of the associated version. One processor instance is needed per + * logical WebSocket connection. + */ +namespace processor { + +/// Determine whether or not a generic HTTP request is a WebSocket handshake +/** + * @param r The HTTP request to read. + * + * @return True if the request is a WebSocket handshake, false otherwise + */ +template +bool is_websocket_handshake(request_type& r) { + using utility::ci_find_substr; + + std::string const & upgrade_header = r.get_header("Upgrade"); + + if (ci_find_substr(upgrade_header, constants::upgrade_token, + sizeof(constants::upgrade_token)-1) == upgrade_header.end()) + { + return false; + } + + std::string const & con_header = r.get_header("Connection"); + + if (ci_find_substr(con_header, constants::connection_token, + sizeof(constants::connection_token)-1) == con_header.end()) + { + return false; + } + + return true; +} + +/// Extract the version from a WebSocket handshake request +/** + * A blank version header indicates a spec before versions were introduced. + * The only such versions in shipping products are Hixie Draft 75 and Hixie + * Draft 76. Draft 75 is present in Chrome 4-5 and Safari 5.0.0, Draft 76 (also + * known as hybi 00 is present in Chrome 6-13 and Safari 5.0.1+. As + * differentiating between these two sets of browsers is very difficult and + * Safari 5.0.1+ accounts for the vast majority of cases in the wild this + * function assumes that all handshakes without a valid version header are + * Hybi 00. + * + * @param r The WebSocket handshake request to read. + * + * @return The WebSocket handshake version or -1 if there was an extraction + * error. + */ +template +int get_websocket_version(request_type& r) { + if (!r.ready()) { + return -2; + } + + if (r.get_header("Sec-WebSocket-Version").empty()) { + return 0; + } + + int version; + std::istringstream ss(r.get_header("Sec-WebSocket-Version")); + + if ((ss >> version).fail()) { + return -1; + } + + return version; +} + +/// Extract a URI ptr from the host header of the request +/** + * @param request The request to extract the Host header from. + * + * @param scheme The scheme under which this request was received (ws, wss, + * http, https, etc) + * + * @return A uri_pointer that encodes the value of the host header. + */ +template +uri_ptr get_uri_from_host(request_type & request, std::string scheme) { + std::string h = request.get_header("Host"); + + size_t last_colon = h.rfind(":"); + size_t last_sbrace = h.rfind("]"); + + // no : = hostname with no port + // last : before ] = ipv6 literal with no port + // : with no ] = hostname with port + // : after ] = ipv6 literal with port + if (last_colon == std::string::npos || + (last_sbrace != std::string::npos && last_sbrace > last_colon)) + { + return lib::make_shared(scheme, h, request.get_uri()); + } else { + return lib::make_shared(scheme, + h.substr(0,last_colon), + h.substr(last_colon+1), + request.get_uri()); + } +} + +/// WebSocket protocol processor abstract base class +template +class processor { +public: + typedef processor type; + typedef typename config::request_type request_type; + typedef typename config::response_type response_type; + typedef typename config::message_type::ptr message_ptr; + typedef std::pair err_str_pair; + + explicit processor(bool secure, bool p_is_server) + : m_secure(secure) + , m_server(p_is_server) + , m_max_message_size(config::max_message_size) + {} + + virtual ~processor() {} + + /// Get the protocol version of this processor + virtual int get_version() const = 0; + + /// Get maximum message size + /** + * Get maximum message size. Maximum message size determines the point at which the + * processor will fail a connection with the message_too_big protocol error. + * + * The default is retrieved from the max_message_size value from the template config + * + * @since 0.3.0 + */ + size_t get_max_message_size() const { + return m_max_message_size; + } + + /// Set maximum message size + /** + * Set maximum message size. Maximum message size determines the point at which the + * processor will fail a connection with the message_too_big protocol error. + * + * The default is retrieved from the max_message_size value from the template config + * + * @since 0.3.0 + * + * @param new_value The value to set as the maximum message size. + */ + void set_max_message_size(size_t new_value) { + m_max_message_size = new_value; + } + + /// Returns whether or not the permessage_compress extension is implemented + /** + * Compile time flag that indicates whether this processor has implemented + * the permessage_compress extension. By default this is false. + */ + virtual bool has_permessage_compress() const { + return false; + } + + /// Initializes extensions based on the Sec-WebSocket-Extensions header + /** + * Reads the Sec-WebSocket-Extensions header and determines if any of the + * requested extensions are supported by this processor. If they are their + * settings data is initialized and an extension string to send to the + * is returned. + * + * @param request The request or response headers to look at. + */ + virtual err_str_pair negotiate_extensions(request_type const &) { + return err_str_pair(); + } + + /// Initializes extensions based on the Sec-WebSocket-Extensions header + /** + * Reads the Sec-WebSocket-Extensions header and determines if any of the + * requested extensions were accepted by the server. If they are their + * settings data is initialized. If they are not a list of required + * extensions (if any) is returned. This list may be sent back to the server + * as a part of the 1010/Extension required close code. + * + * @param response The request or response headers to look at. + */ + virtual err_str_pair negotiate_extensions(response_type const &) { + return err_str_pair(); + } + + /// validate a WebSocket handshake request for this version + /** + * @param request The WebSocket handshake request to validate. + * is_websocket_handshake(request) must be true and + * get_websocket_version(request) must equal this->get_version(). + * + * @return A status code, 0 on success, non-zero for specific sorts of + * failure + */ + virtual lib::error_code validate_handshake(request_type const & request) const = 0; + + /// Calculate the appropriate response for this websocket request + /** + * @param req The request to process + * + * @param subprotocol The subprotocol in use + * + * @param res The response to store the processed response in + * + * @return An error code, 0 on success, non-zero for other errors + */ + virtual lib::error_code process_handshake(request_type const & req, + std::string const & subprotocol, response_type& res) const = 0; + + /// Fill in an HTTP request for an outgoing connection handshake + /** + * @param req The request to process. + * + * @return An error code, 0 on success, non-zero for other errors + */ + virtual lib::error_code client_handshake_request(request_type & req, + uri_ptr uri, std::vector const & subprotocols) const = 0; + + /// Validate the server's response to an outgoing handshake request + /** + * @param req The original request sent + * @param res The reponse to generate + * @return An error code, 0 on success, non-zero for other errors + */ + virtual lib::error_code validate_server_handshake_response(request_type + const & req, response_type & res) const = 0; + + /// Given a completed response, get the raw bytes to put on the wire + virtual std::string get_raw(response_type const & request) const = 0; + + /// Return the value of the header containing the CORS origin. + virtual std::string const & get_origin(request_type const & request) const = 0; + + /// Extracts requested subprotocols from a handshake request + /** + * Extracts a list of all subprotocols that the client has requested in the + * given opening handshake request. + * + * @param [in] req The request to extract from + * @param [out] subprotocol_list A reference to a vector of strings to store + * the results in. + */ + virtual lib::error_code extract_subprotocols(const request_type & req, + std::vector & subprotocol_list) = 0; + + /// Extracts client uri from a handshake request + virtual uri_ptr get_uri(request_type const & request) const = 0; + + /// process new websocket connection bytes + /** + * WebSocket connections are a continous stream of bytes that must be + * interpreted by a protocol processor into discrete frames. + * + * @param buf Buffer from which bytes should be read. + * @param len Length of buffer + * @param ec Reference to an error code to return any errors in + * @return Number of bytes processed + */ + virtual size_t consume(uint8_t *buf, size_t len, lib::error_code & ec) = 0; + + /// Checks if there is a message ready + /** + * Checks if the most recent consume operation processed enough bytes to + * complete a new WebSocket message. The message can be retrieved by calling + * get_message() which will reset the internal state to not-ready and allow + * consume to read more bytes. + * + * @return Whether or not a message is ready. + */ + virtual bool ready() const = 0; + + /// Retrieves the most recently processed message + /** + * Retrieves a shared pointer to the recently completed message if there is + * one. If ready() returns true then there is a message available. + * Retrieving the message with get_message will reset the state of ready. + * As such, each new message may be retrieved only once. Calling get_message + * when there is no message available will result in a null pointer being + * returned. + * + * @return A pointer to the most recently processed message or a null shared + * pointer. + */ + virtual message_ptr get_message() = 0; + + /// Tests whether the processor is in a fatal error state + virtual bool get_error() const = 0; + + /// Retrieves the number of bytes presently needed by the processor + /// This value may be used as a hint to the transport layer as to how many + /// bytes to wait for before running consume again. + virtual size_t get_bytes_needed() const { + return 1; + } + + /// Prepare a data message for writing + /** + * Performs validation, masking, compression, etc. will return an error if + * there was an error, otherwise msg will be ready to be written + */ + virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) = 0; + + /// Prepare a ping frame + /** + * Ping preparation is entirely state free. There is no payload validation + * other than length. Payload need not be UTF-8. + * + * @param in The string to use for the ping payload + * @param out The message buffer to prepare the ping in. + * @return Status code, zero on success, non-zero on failure + */ + virtual lib::error_code prepare_ping(std::string const & in, message_ptr out) const + = 0; + + /// Prepare a pong frame + /** + * Pong preparation is entirely state free. There is no payload validation + * other than length. Payload need not be UTF-8. + * + * @param in The string to use for the pong payload + * @param out The message buffer to prepare the pong in. + * @return Status code, zero on success, non-zero on failure + */ + virtual lib::error_code prepare_pong(std::string const & in, message_ptr out) const + = 0; + + /// Prepare a close frame + /** + * Close preparation is entirely state free. The code and reason are both + * subject to validation. Reason must be valid UTF-8. Code must be a valid + * un-reserved WebSocket close code. Use close::status::no_status to + * indicate no code. If no code is supplied a reason may not be specified. + * + * @param code The close code to send + * @param reason The reason string to send + * @param out The message buffer to prepare the fame in + * @return Status code, zero on success, non-zero on failure + */ + virtual lib::error_code prepare_close(close::status::value code, + std::string const & reason, message_ptr out) const = 0; +protected: + bool const m_secure; + bool const m_server; + size_t m_max_message_size; +}; + +} // namespace processor +} // namespace websocketpp + +#endif //WEBSOCKETPP_PROCESSOR_HPP diff --git a/websocketpp/random/none.hpp b/websocketpp/random/none.hpp new file mode 100644 index 00000000..2163e6f4 --- /dev/null +++ b/websocketpp/random/none.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_RANDOM_NONE_HPP +#define WEBSOCKETPP_RANDOM_NONE_HPP + +namespace websocketpp { +/// Random number generation policies +namespace random { +/// Stub RNG policy that always returns 0 +namespace none { + +/// Thread safe stub "random" integer generator. +/** + * This template class provides a random integer stub. The interface mimics the + * WebSocket++ RNG generator classes but the generater function always returns + * zero. This can be used to stub out the RNG for unit and performance testing. + * + * Call operator() to generate the next number + */ +template +class int_generator { + public: + int_generator() {} + + /// advances the engine's state and returns the generated value + int_type operator()() { + return 0; + } +}; + +} // namespace none +} // namespace random +} // namespace websocketpp + +#endif //WEBSOCKETPP_RANDOM_NONE_HPP diff --git a/websocketpp/random/random_device.hpp b/websocketpp/random/random_device.hpp new file mode 100644 index 00000000..91e7dd16 --- /dev/null +++ b/websocketpp/random/random_device.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_RANDOM_RANDOM_DEVICE_HPP +#define WEBSOCKETPP_RANDOM_RANDOM_DEVICE_HPP + +#include + +namespace websocketpp { +namespace random { +/// RNG policy based on std::random_device or boost::random_device +namespace random_device { + +/// Thread safe non-deterministic random integer generator. +/** + * This template class provides thread safe non-deterministic random integer + * generation. Numbers are produced in a uniformly distributed range from the + * smallest to largest value that int_type can store. + * + * Thread-safety is provided via locking based on the concurrency template + * parameter. + * + * Non-deterministic RNG is provided via websocketpp::lib which uses either + * C++11 or Boost 1.47+'s random_device class. + * + * Call operator() to generate the next number + */ +template +class int_generator { + public: + typedef typename concurrency::scoped_lock_type scoped_lock_type; + typedef typename concurrency::mutex_type mutex_type; + + /// constructor + //mac TODO: figure out if signed types present a range problem + int_generator() {} + + /// advances the engine's state and returns the generated value + int_type operator()() { + scoped_lock_type guard(m_lock); + return m_dis(m_rng); + } + private: + + + lib::random_device m_rng; + lib::uniform_int_distribution m_dis; + + mutex_type m_lock; +}; + +} // namespace random_device +} // namespace random +} // namespace websocketpp + +#endif //WEBSOCKETPP_RANDOM_RANDOM_DEVICE_HPP diff --git a/websocketpp/roles/client_endpoint.hpp b/websocketpp/roles/client_endpoint.hpp new file mode 100644 index 00000000..2de1a10a --- /dev/null +++ b/websocketpp/roles/client_endpoint.hpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CLIENT_ENDPOINT_HPP +#define WEBSOCKETPP_CLIENT_ENDPOINT_HPP + +#include +#include + +#include + +#include + +#include + +namespace websocketpp { + +/// Client endpoint role based on the given config +/** + * + */ +template +class client : public endpoint,config> { +public: + /// Type of this endpoint + typedef client type; + + /// Type of the endpoint concurrency component + typedef typename config::concurrency_type concurrency_type; + /// Type of the endpoint transport component + typedef typename config::transport_type transport_type; + + /// Type of the connections this server will create + typedef connection connection_type; + /// Type of a shared pointer to the connections this server will create + typedef typename connection_type::ptr connection_ptr; + + /// Type of the connection transport component + typedef typename transport_type::transport_con_type transport_con_type; + /// Type of a shared pointer to the connection transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + /// Type of the endpoint component of this server + typedef endpoint endpoint_type; + + friend class connection; + + explicit client() : endpoint_type(false) + { + endpoint_type::m_alog.write(log::alevel::devel, "client constructor"); + } + + /// Get a new connection + /** + * Creates and returns a pointer to a new connection to the given URI + * suitable for passing to connect(connection_ptr). This method allows + * applying connection specific settings before performing the opening + * handshake. + * + * @param [in] location URI to open the connection to as a uri_ptr + * @param [out] ec An status code indicating failure reasons, if any + * + * @return A connection_ptr to the new connection + */ + connection_ptr get_connection(uri_ptr location, lib::error_code & ec) { + if (location->get_secure() && !transport_type::is_secure()) { + ec = error::make_error_code(error::endpoint_not_secure); + return connection_ptr(); + } + + connection_ptr con = endpoint_type::create_connection(); + + if (!con) { + ec = error::make_error_code(error::con_creation_failed); + return con; + } + + con->set_uri(location); + + ec = lib::error_code(); + return con; + } + + /// Get a new connection (string version) + /** + * Creates and returns a pointer to a new connection to the given URI + * suitable for passing to connect(connection_ptr). This overload allows + * default construction of the uri_ptr from a standard string. + * + * @param [in] u URI to open the connection to as a string + * @param [out] ec An status code indicating failure reasons, if any + * + * @return A connection_ptr to the new connection + */ + connection_ptr get_connection(std::string const & u, lib::error_code & ec) { + uri_ptr location = lib::make_shared(u); + + if (!location->get_valid()) { + ec = error::make_error_code(error::invalid_uri); + return connection_ptr(); + } + + return get_connection(location, ec); + } + + /// Begin the connection process for the given connection + /** + * Initiates the opening connection handshake for connection con. Exact + * behavior depends on the underlying transport policy. + * + * @param con The connection to connect + * + * @return The pointer to the connection originally passed in. + */ + connection_ptr connect(connection_ptr con) { + // Ask transport to perform a connection + transport_type::async_connect( + lib::static_pointer_cast(con), + con->get_uri(), + lib::bind( + &type::handle_connect, + this, + con, + lib::placeholders::_1 + ) + ); + + return con; + } +private: + // handle_connect + void handle_connect(connection_ptr con, lib::error_code const & ec) { + if (ec) { + con->terminate(ec); + + endpoint_type::m_elog.write(log::elevel::rerror, + "handle_connect error: "+ec.message()); + } else { + endpoint_type::m_alog.write(log::alevel::connect, + "Successful connection"); + + con->start(); + } + } +}; + +} // namespace websocketpp + +#endif //WEBSOCKETPP_CLIENT_ENDPOINT_HPP diff --git a/websocketpp/roles/server_endpoint.hpp b/websocketpp/roles/server_endpoint.hpp new file mode 100644 index 00000000..d76eea8a --- /dev/null +++ b/websocketpp/roles/server_endpoint.hpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_SERVER_ENDPOINT_HPP +#define WEBSOCKETPP_SERVER_ENDPOINT_HPP + +#include + +#include + +#include + +namespace websocketpp { + +/// Server endpoint role based on the given config +/** + * + */ +template +class server : public endpoint,config> { +public: + /// Type of this endpoint + typedef server type; + + /// Type of the endpoint concurrency component + typedef typename config::concurrency_type concurrency_type; + /// Type of the endpoint transport component + typedef typename config::transport_type transport_type; + + /// Type of the connections this server will create + typedef connection connection_type; + /// Type of a shared pointer to the connections this server will create + typedef typename connection_type::ptr connection_ptr; + + /// Type of the connection transport component + typedef typename transport_type::transport_con_type transport_con_type; + /// Type of a shared pointer to the connection transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + /// Type of the endpoint component of this server + typedef endpoint endpoint_type; + + friend class connection; + + explicit server() : endpoint_type(true) + { + endpoint_type::m_alog.write(log::alevel::devel, "server constructor"); + } + + /// Destructor + ~server() {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no copy constructor because endpoints are not copyable + server(server &) = delete; + + // no copy assignment operator because endpoints are not copyable + server & operator=(server const &) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + server(server && o) : endpoint,config>(std::move(o)) {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no move assignment operator because of const member variables + server & operator=(server &&) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + + /// Create and initialize a new connection + /** + * The connection will be initialized and ready to begin. Call its start() + * method to begin the processing loop. + * + * Note: The connection must either be started or terminated using + * connection::terminate in order to avoid memory leaks. + * + * @return A pointer to the new connection. + */ + connection_ptr get_connection() { + return endpoint_type::create_connection(); + } + + /// Starts the server's async connection acceptance loop (exception free) + /** + * Initiates the server connection acceptance loop. Must be called after + * listen. This method will have no effect until the underlying io_service + * starts running. It may be called after the io_service is already running. + * + * Refer to documentation for the transport policy you are using for + * instructions on how to stop this acceptance loop. + * + * @param [out] ec A status code indicating an error, if any. + */ + void start_accept(lib::error_code & ec) { + if (!transport_type::is_listening()) { + ec = error::make_error_code(error::async_accept_not_listening); + return; + } + + ec = lib::error_code(); + connection_ptr con = get_connection(); + + transport_type::async_accept( + lib::static_pointer_cast(con), + lib::bind(&type::handle_accept,this,con,lib::placeholders::_1), + ec + ); + + if (ec && con) { + // If the connection was constructed but the accept failed, + // terminate the connection to prevent memory leaks + con->terminate(lib::error_code()); + } + } + + /// Starts the server's async connection acceptance loop + /** + * Initiates the server connection acceptance loop. Must be called after + * listen. This method will have no effect until the underlying io_service + * starts running. It may be called after the io_service is already running. + * + * Refer to documentation for the transport policy you are using for + * instructions on how to stop this acceptance loop. + */ + void start_accept() { + lib::error_code ec; + start_accept(ec); + if (ec) { + throw exception(ec); + } + } + + /// Handler callback for start_accept + void handle_accept(connection_ptr con, lib::error_code const & ec) { + if (ec) { + con->terminate(ec); + + if (ec == error::operation_canceled) { + endpoint_type::m_elog.write(log::elevel::info, + "handle_accept error: "+ec.message()); + } else { + endpoint_type::m_elog.write(log::elevel::rerror, + "handle_accept error: "+ec.message()); + } + } else { + con->start(); + } + + lib::error_code start_ec; + start_accept(start_ec); + if (start_ec == error::async_accept_not_listening) { + endpoint_type::m_elog.write(log::elevel::info, + "Stopping acceptance of new connections because the underlying transport is no longer listening."); + } else if (start_ec) { + endpoint_type::m_elog.write(log::elevel::rerror, + "Restarting async_accept loop failed: "+ec.message()); + } + } +}; + +} // namespace websocketpp + +#endif //WEBSOCKETPP_SERVER_ENDPOINT_HPP diff --git a/websocketpp/server.hpp b/websocketpp/server.hpp new file mode 100644 index 00000000..342fa8c5 --- /dev/null +++ b/websocketpp/server.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_SERVER_HPP +#define WEBSOCKETPP_SERVER_HPP + +#include + +#endif //WEBSOCKETPP_SERVER_HPP diff --git a/websocketpp/sha1/sha1.hpp b/websocketpp/sha1/sha1.hpp new file mode 100644 index 00000000..43a84338 --- /dev/null +++ b/websocketpp/sha1/sha1.hpp @@ -0,0 +1,189 @@ +/* +***** +sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the smallsha1 +library (http://code.google.com/p/smallsha1/) into a single header suitable for +use as a header only library. This conversion was done by Peter Thorson +(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed +under the same license as the original, which is listed below. +***** + + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SHA1_DEFINED +#define SHA1_DEFINED + +namespace websocketpp { +namespace sha1 { + +namespace { // local + +// Rotate an integer value to left. +inline unsigned int rol(unsigned int value, unsigned int steps) { + return ((value << steps) | (value >> (32 - steps))); +} + +// Sets the first 16 integers in the buffert to zero. +// Used for clearing the W buffert. +inline void clearWBuffert(unsigned int * buffert) +{ + for (int pos = 16; --pos >= 0;) + { + buffert[pos] = 0; + } +} + +inline void innerHash(unsigned int * result, unsigned int * w) +{ + unsigned int a = result[0]; + unsigned int b = result[1]; + unsigned int c = result[2]; + unsigned int d = result[3]; + unsigned int e = result[4]; + + int round = 0; + + #define sha1macro(func,val) \ + { \ + const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ + e = d; \ + d = c; \ + c = rol(b, 30); \ + b = a; \ + a = t; \ + } + + while (round < 16) + { + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 20) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 40) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0x6ed9eba1) + ++round; + } + while (round < 60) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) + ++round; + } + while (round < 80) + { + w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0xca62c1d6) + ++round; + } + + #undef sha1macro + + result[0] += a; + result[1] += b; + result[2] += c; + result[3] += d; + result[4] += e; +} + +} // namespace + +/// Calculate a SHA1 hash +/** + * @param src points to any kind of data to be hashed. + * @param bytelength the number of bytes to hash from the src pointer. + * @param hash should point to a buffer of at least 20 bytes of size for storing + * the sha1 result in. + */ +inline void calc(void const * src, size_t bytelength, unsigned char * hash) { + // Init the result array. + unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, + 0x10325476, 0xc3d2e1f0 }; + + // Cast the void src pointer to be the byte array we can work with. + unsigned char const * sarray = (unsigned char const *) src; + + // The reusable round buffer + unsigned int w[80]; + + // Loop through all complete 64byte blocks. + + size_t endCurrentBlock; + size_t currentBlock = 0; + + if (bytelength >= 64) { + size_t const endOfFullBlocks = bytelength - 64; + + while (currentBlock <= endOfFullBlocks) { + endCurrentBlock = currentBlock + 64; + + // Init the round buffer with the 64 byte block data. + for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) + { + // This line will swap endian on big endian and keep endian on + // little endian. + w[roundPos++] = (unsigned int) sarray[currentBlock + 3] + | (((unsigned int) sarray[currentBlock + 2]) << 8) + | (((unsigned int) sarray[currentBlock + 1]) << 16) + | (((unsigned int) sarray[currentBlock]) << 24); + } + innerHash(result, w); + } + } + + // Handle the last and not full 64 byte block if existing. + endCurrentBlock = bytelength - currentBlock; + clearWBuffert(w); + size_t lastBlockBytes = 0; + for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes) { + w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3); + } + + w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); + if (endCurrentBlock >= 56) { + innerHash(result, w); + clearWBuffert(w); + } + w[15] = bytelength << 3; + innerHash(result, w); + + // Store hash in result pointer, and make sure we get in in the correct + // order on both endian models. + for (int hashByte = 20; --hashByte >= 0;) { + hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff; + } +} + +} // namespace sha1 +} // namespace websocketpp + +#endif // SHA1_DEFINED diff --git a/websocketpp/transport/asio/base.hpp b/websocketpp/transport/asio/base.hpp new file mode 100644 index 00000000..b945fe11 --- /dev/null +++ b/websocketpp/transport/asio/base.hpp @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP + +#include +#include +#include +#include +#include + +#include + +namespace websocketpp { +namespace transport { +/// Transport policy that uses asio +/** + * This policy uses a single asio io_service to provide transport + * services to a WebSocket++ endpoint. + */ +namespace asio { + +// Class to manage the memory to be used for handler-based custom allocation. +// It contains a single block of memory which may be returned for allocation +// requests. If the memory is in use when an allocation request is made, the +// allocator delegates allocation to the global heap. +class handler_allocator { +public: + static const size_t size = 1024; + + handler_allocator() : m_in_use(false) {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + handler_allocator(handler_allocator const & cpy) = delete; + handler_allocator & operator =(handler_allocator const &) = delete; +#endif + + void * allocate(std::size_t memsize) { + if (!m_in_use && memsize < size) { + m_in_use = true; + return static_cast(&m_storage); + } else { + return ::operator new(memsize); + } + } + + void deallocate(void * pointer) { + if (pointer == &m_storage) { + m_in_use = false; + } else { + ::operator delete(pointer); + } + } + +private: + // Storage space used for handler-based custom memory allocation. + lib::aligned_storage::type m_storage; + + // Whether the handler-based custom allocation storage has been used. + bool m_in_use; +}; + +// Wrapper class template for handler objects to allow handler memory +// allocation to be customised. Calls to operator() are forwarded to the +// encapsulated handler. +template +class custom_alloc_handler { +public: + custom_alloc_handler(handler_allocator& a, Handler h) + : allocator_(a), + handler_(h) + {} + + template + void operator()(Arg1 arg1) { + handler_(arg1); + } + + template + void operator()(Arg1 arg1, Arg2 arg2) { + handler_(arg1, arg2); + } + + friend void* asio_handler_allocate(std::size_t size, + custom_alloc_handler * this_handler) + { + return this_handler->allocator_.allocate(size); + } + + friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/, + custom_alloc_handler * this_handler) + { + this_handler->allocator_.deallocate(pointer); + } + +private: + handler_allocator & allocator_; + Handler handler_; +}; + +// Helper function to wrap a handler object to add custom allocation. +template +inline custom_alloc_handler make_custom_alloc_handler( + handler_allocator & a, Handler h) +{ + return custom_alloc_handler(a, h); +} + + + + + + + +// Forward declaration of class endpoint so that it can be friended/referenced +// before being included. +template +class endpoint; + +typedef lib::function async_read_handler; + +typedef lib::function async_write_handler; + +typedef lib::function pre_init_handler; + +// handle_timer: dynamic parameters, multiple copies +// handle_proxy_write +// handle_proxy_read +// handle_async_write +// handle_pre_init + + +/// Asio transport errors +namespace error { +enum value { + /// Catch-all error for transport policy errors that don't fit in other + /// categories + general = 1, + + /// async_read_at_least call requested more bytes than buffer can store + invalid_num_bytes, + + /// there was an error in the underlying transport library + pass_through, + + /// The connection to the requested proxy server failed + proxy_failed, + + /// Invalid Proxy URI + proxy_invalid, + + /// Invalid host or service + invalid_host_service +}; + +/// Asio transport error category +class category : public lib::error_category { +public: + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport.asio"; + } + + std::string message(int value) const { + switch(value) { + case error::general: + return "Generic asio transport policy error"; + case error::invalid_num_bytes: + return "async_read_at_least call requested more bytes than buffer can store"; + case error::pass_through: + return "Underlying Transport Error"; + case error::proxy_failed: + return "Proxy connection failed"; + case error::proxy_invalid: + return "Invalid proxy URI"; + case error::invalid_host_service: + return "Invalid host or service"; + default: + return "Unknown"; + } + } +}; + +/// Get a reference to a static copy of the asio transport error category +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +/// Create an error code with the given value and the asio transport category +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace asio +} // namespace transport +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ +#endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP diff --git a/websocketpp/transport/asio/connection.hpp b/websocketpp/transport/asio/connection.hpp new file mode 100644 index 00000000..8eb8c759 --- /dev/null +++ b/websocketpp/transport/asio/connection.hpp @@ -0,0 +1,1204 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP +#define WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace websocketpp { +namespace transport { +namespace asio { + +typedef lib::function tcp_init_handler; + +/// Asio based connection transport component +/** + * transport::asio::connection implements a connection transport component using + * Asio that works with the transport::asio::endpoint endpoint transport + * component. + */ +template +class connection : public config::socket_type::socket_con_type { +public: + /// Type of this connection transport component + typedef connection type; + /// Type of a shared pointer to this connection transport component + typedef lib::shared_ptr ptr; + + /// Type of the socket connection component + typedef typename config::socket_type::socket_con_type socket_con_type; + /// Type of a shared pointer to the socket connection component + typedef typename socket_con_type::ptr socket_con_ptr; + /// Type of this transport's access logging policy + typedef typename config::alog_type alog_type; + /// Type of this transport's error logging policy + typedef typename config::elog_type elog_type; + + typedef typename config::request_type request_type; + typedef typename request_type::ptr request_ptr; + typedef typename config::response_type response_type; + typedef typename response_type::ptr response_ptr; + + /// Type of a pointer to the Asio io_service being used + typedef lib::asio::io_service * io_service_ptr; + /// Type of a pointer to the Asio io_service::strand being used + typedef lib::shared_ptr strand_ptr; + /// Type of a pointer to the Asio timer class + typedef lib::shared_ptr timer_ptr; + + // connection is friends with its associated endpoint to allow the endpoint + // to call private/protected utility methods that we don't want to expose + // to the public api. + friend class endpoint; + + // generate and manage our own io_service + explicit connection(bool is_server, alog_type & alog, elog_type & elog) + : m_is_server(is_server) + , m_alog(alog) + , m_elog(elog) + { + m_alog.write(log::alevel::devel,"asio con transport constructor"); + } + + /// Get a shared pointer to this component + ptr get_shared() { + return lib::static_pointer_cast(socket_con_type::get_shared()); + } + + bool is_secure() const { + return socket_con_type::is_secure(); + } + + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * This transport policy doesn't use the uri except to forward it to the + * socket layer. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr u) { + socket_con_type::set_uri(u); + } + + /// Sets the tcp pre init handler + /** + * The tcp pre init handler is called after the raw tcp connection has been + * established but before any additional wrappers (proxy connects, TLS + * handshakes, etc) have been performed. + * + * @since 0.3.0 + * + * @param h The handler to call on tcp pre init. + */ + void set_tcp_pre_init_handler(tcp_init_handler h) { + m_tcp_pre_init_handler = h; + } + + /// Sets the tcp pre init handler (deprecated) + /** + * The tcp pre init handler is called after the raw tcp connection has been + * established but before any additional wrappers (proxy connects, TLS + * handshakes, etc) have been performed. + * + * @deprecated Use set_tcp_pre_init_handler instead + * + * @param h The handler to call on tcp pre init. + */ + void set_tcp_init_handler(tcp_init_handler h) { + set_tcp_pre_init_handler(h); + } + + /// Sets the tcp post init handler + /** + * The tcp post init handler is called after the tcp connection has been + * established and all additional wrappers (proxy connects, TLS handshakes, + * etc have been performed. This is fired before any bytes are read or any + * WebSocket specific handshake logic has been performed. + * + * @since 0.3.0 + * + * @param h The handler to call on tcp post init. + */ + void set_tcp_post_init_handler(tcp_init_handler h) { + m_tcp_post_init_handler = h; + } + + /// Set the proxy to connect through (exception free) + /** + * The URI passed should be a complete URI including scheme. For example: + * http://proxy.example.com:8080/ + * + * The proxy must be set up as an explicit (CONNECT) proxy allowed to + * connect to the port you specify. Traffic to the proxy is not encrypted. + * + * @param uri The full URI of the proxy to connect to. + * + * @param ec A status value + */ + void set_proxy(std::string const & uri, lib::error_code & ec) { + // TODO: return errors for illegal URIs here? + // TODO: should https urls be illegal for the moment? + m_proxy = uri; + m_proxy_data = lib::make_shared(); + ec = lib::error_code(); + } + + /// Set the proxy to connect through (exception) + void set_proxy(std::string const & uri) { + lib::error_code ec; + set_proxy(uri,ec); + if (ec) { throw exception(ec); } + } + + /// Set the basic auth credentials to use (exception free) + /** + * The URI passed should be a complete URI including scheme. For example: + * http://proxy.example.com:8080/ + * + * The proxy must be set up as an explicit proxy + * + * @param username The username to send + * + * @param password The password to send + * + * @param ec A status value + */ + void set_proxy_basic_auth(std::string const & username, std::string const & + password, lib::error_code & ec) + { + if (!m_proxy_data) { + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + // TODO: username can't contain ':' + std::string val = "Basic "+base64_encode(username + ":" + password); + m_proxy_data->req.replace_header("Proxy-Authorization",val); + ec = lib::error_code(); + } + + /// Set the basic auth credentials to use (exception) + void set_proxy_basic_auth(std::string const & username, std::string const & + password) + { + lib::error_code ec; + set_proxy_basic_auth(username,password,ec); + if (ec) { throw exception(ec); } + } + + /// Set the proxy timeout duration (exception free) + /** + * Duration is in milliseconds. Default value is based on the transport + * config + * + * @param duration The number of milliseconds to wait before aborting the + * proxy connection. + * + * @param ec A status value + */ + void set_proxy_timeout(long duration, lib::error_code & ec) { + if (!m_proxy_data) { + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + m_proxy_data->timeout_proxy = duration; + ec = lib::error_code(); + } + + /// Set the proxy timeout duration (exception) + void set_proxy_timeout(long duration) { + lib::error_code ec; + set_proxy_timeout(duration,ec); + if (ec) { throw exception(ec); } + } + + std::string const & get_proxy() const { + return m_proxy; + } + + /// Get the remote endpoint address + /** + * The iostream transport has no information about the ultimate remote + * endpoint. It will return the string "iostream transport". To indicate + * this. + * + * TODO: allow user settable remote endpoint addresses if this seems useful + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint() const { + lib::error_code ec; + + std::string ret = socket_con_type::get_remote_endpoint(ec); + + if (ec) { + m_elog.write(log::elevel::info,ret); + return "Unknown"; + } else { + return ret; + } + } + + /// Get the connection handle + connection_hdl get_handle() const { + return m_connection_hdl; + } + + /// Call back a function after a period of time. + /** + * Sets a timer that calls back a function after the specified period of + * milliseconds. Returns a handle that can be used to cancel the timer. + * A cancelled timer will return the error code error::operation_aborted + * A timer that expired will return no error. + * + * @param duration Length of time to wait in milliseconds + * + * @param callback The function to call back when the timer has expired + * + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long duration, timer_handler callback) { + timer_ptr new_timer = lib::make_shared( + lib::ref(*m_io_service), + lib::asio::milliseconds(duration) + ); + + if (config::enable_multithreading) { + new_timer->async_wait(m_strand->wrap(lib::bind( + &type::handle_timer, get_shared(), + new_timer, + callback, + lib::placeholders::_1 + ))); + } else { + new_timer->async_wait(lib::bind( + &type::handle_timer, get_shared(), + new_timer, + callback, + lib::placeholders::_1 + )); + } + + return new_timer; + } + + /// Timer callback + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * TODO: candidate for protected status + * + * @param post_timer Pointer to the timer in question + * @param callback The function to call back + * @param ec The status code + */ + void handle_timer(timer_ptr, timer_handler callback, + lib::asio::error_code const & ec) + { + if (ec) { + if (ec == lib::asio::error::operation_aborted) { + callback(make_error_code(transport::error::operation_aborted)); + } else { + log_err(log::elevel::info,"asio handle_timer",ec); + callback(make_error_code(error::pass_through)); + } + } else { + callback(lib::error_code()); + } + } + + /// Get a pointer to this connection's strand + strand_ptr get_strand() { + return m_strand; + } + + /// Get the internal transport error code for a closed/failed connection + /** + * Retrieves a machine readable detailed error code indicating the reason + * that the connection was closed or failed. Valid only after the close or + * fail handler is called. + * + * Primarily used if you are using mismatched asio / system_error + * implementations such as `boost::asio` with `std::system_error`. In these + * cases the transport error type is different than the library error type + * and some WebSocket++ functions that return transport errors via the + * library error code type will be coerced into a catch all `pass_through` + * or `tls_error` error. This method will return the original machine + * readable transport error in the native type. + * + * @since 0.7.0 + * + * @return Error code indicating the reason the connection was closed or + * failed + */ + lib::asio::error_code get_transport_ec() const { + return m_tec; + } + + /// Initialize transport for reading + /** + * init_asio is called once immediately after construction to initialize + * Asio components to the io_service + * + * The transport initialization sequence consists of the following steps: + * - Pre-init: the underlying socket is initialized to the point where + * bytes may be written. No bytes are actually written in this stage + * - Proxy negotiation: if a proxy is set, a request is made to it to start + * a tunnel to the final destination. This stage ends when the proxy is + * ready to forward the + * next byte to the remote endpoint. + * - Post-init: Perform any i/o with the remote endpoint, such as setting up + * tunnels for encryption. This stage ends when the connection is ready to + * read or write the WebSocket handshakes. At this point the original + * callback function is called. + */ +protected: + void init(init_handler callback) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"asio connection init"); + } + + // TODO: pre-init timeout. Right now no implemented socket policies + // actually have an asyncronous pre-init + + socket_con_type::pre_init( + lib::bind( + &type::handle_pre_init, + get_shared(), + callback, + lib::placeholders::_1 + ) + ); + } + + /// initialize the proxy buffers and http parsers + /** + * + * @param authority The address of the server we want the proxy to tunnel to + * in the format of a URI authority (host:port) + * + * @return Status code indicating what errors occurred, if any + */ + lib::error_code proxy_init(std::string const & authority) { + if (!m_proxy_data) { + return websocketpp::error::make_error_code( + websocketpp::error::invalid_state); + } + m_proxy_data->req.set_version("HTTP/1.1"); + m_proxy_data->req.set_method("CONNECT"); + + m_proxy_data->req.set_uri(authority); + m_proxy_data->req.replace_header("Host",authority); + + return lib::error_code(); + } + + /// Finish constructing the transport + /** + * init_asio is called once immediately after construction to initialize + * Asio components to the io_service. + * + * @param io_service A pointer to the io_service to register with this + * connection + * + * @return Status code for the success or failure of the initialization + */ + lib::error_code init_asio (io_service_ptr io_service) { + m_io_service = io_service; + + if (config::enable_multithreading) { + m_strand = lib::make_shared( + lib::ref(*io_service)); + } + + lib::error_code ec = socket_con_type::init_asio(io_service, m_strand, + m_is_server); + + return ec; + } + + void handle_pre_init(init_handler callback, lib::error_code const & ec) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"asio connection handle pre_init"); + } + + if (m_tcp_pre_init_handler) { + m_tcp_pre_init_handler(m_connection_hdl); + } + + if (ec) { + callback(ec); + } + + // If we have a proxy set issue a proxy connect, otherwise skip to + // post_init + if (!m_proxy.empty()) { + proxy_write(callback); + } else { + post_init(callback); + } + } + + void post_init(init_handler callback) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"asio connection post_init"); + } + + timer_ptr post_timer; + + if (config::timeout_socket_post_init > 0) { + post_timer = set_timer( + config::timeout_socket_post_init, + lib::bind( + &type::handle_post_init_timeout, + get_shared(), + post_timer, + callback, + lib::placeholders::_1 + ) + ); + } + + socket_con_type::post_init( + lib::bind( + &type::handle_post_init, + get_shared(), + post_timer, + callback, + lib::placeholders::_1 + ) + ); + } + + /// Post init timeout callback + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param post_timer Pointer to the timer in question + * @param callback The function to call back + * @param ec The status code + */ + void handle_post_init_timeout(timer_ptr, init_handler callback, + lib::error_code const & ec) + { + lib::error_code ret_ec; + + if (ec) { + if (ec == transport::error::operation_aborted) { + m_alog.write(log::alevel::devel, + "asio post init timer cancelled"); + return; + } + + log_err(log::elevel::devel,"asio handle_post_init_timeout",ec); + ret_ec = ec; + } else { + if (socket_con_type::get_ec()) { + ret_ec = socket_con_type::get_ec(); + } else { + ret_ec = make_error_code(transport::error::timeout); + } + } + + m_alog.write(log::alevel::devel, "Asio transport post-init timed out"); + cancel_socket_checked(); + callback(ret_ec); + } + + /// Post init timeout callback + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param post_timer Pointer to the timer in question + * @param callback The function to call back + * @param ec The status code + */ + void handle_post_init(timer_ptr post_timer, init_handler callback, + lib::error_code const & ec) + { + if (ec == transport::error::operation_aborted || + (post_timer && lib::asio::is_neg(post_timer->expires_from_now()))) + { + m_alog.write(log::alevel::devel,"post_init cancelled"); + return; + } + + if (post_timer) { + post_timer->cancel(); + } + + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"asio connection handle_post_init"); + } + + if (m_tcp_post_init_handler) { + m_tcp_post_init_handler(m_connection_hdl); + } + + callback(ec); + } + + void proxy_write(init_handler callback) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"asio connection proxy_write"); + } + + if (!m_proxy_data) { + m_elog.write(log::elevel::library, + "assertion failed: !m_proxy_data in asio::connection::proxy_write"); + callback(make_error_code(error::general)); + return; + } + + m_proxy_data->write_buf = m_proxy_data->req.raw(); + + m_bufs.push_back(lib::asio::buffer(m_proxy_data->write_buf.data(), + m_proxy_data->write_buf.size())); + + m_alog.write(log::alevel::devel,m_proxy_data->write_buf); + + // Set a timer so we don't wait forever for the proxy to respond + m_proxy_data->timer = this->set_timer( + m_proxy_data->timeout_proxy, + lib::bind( + &type::handle_proxy_timeout, + get_shared(), + callback, + lib::placeholders::_1 + ) + ); + + // Send proxy request + if (config::enable_multithreading) { + lib::asio::async_write( + socket_con_type::get_next_layer(), + m_bufs, + m_strand->wrap(lib::bind( + &type::handle_proxy_write, get_shared(), + callback, + lib::placeholders::_1 + )) + ); + } else { + lib::asio::async_write( + socket_con_type::get_next_layer(), + m_bufs, + lib::bind( + &type::handle_proxy_write, get_shared(), + callback, + lib::placeholders::_1 + ) + ); + } + } + + void handle_proxy_timeout(init_handler callback, lib::error_code const & ec) + { + if (ec == transport::error::operation_aborted) { + m_alog.write(log::alevel::devel, + "asio handle_proxy_write timer cancelled"); + return; + } else if (ec) { + log_err(log::elevel::devel,"asio handle_proxy_write",ec); + callback(ec); + } else { + m_alog.write(log::alevel::devel, + "asio handle_proxy_write timer expired"); + cancel_socket_checked(); + callback(make_error_code(transport::error::timeout)); + } + } + + void handle_proxy_write(init_handler callback, + lib::asio::error_code const & ec) + { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel, + "asio connection handle_proxy_write"); + } + + m_bufs.clear(); + + // Timer expired or the operation was aborted for some reason. + // Whatever aborted it will be issuing the callback so we are safe to + // return + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(m_proxy_data->timer->expires_from_now())) + { + m_elog.write(log::elevel::devel,"write operation aborted"); + return; + } + + if (ec) { + log_err(log::elevel::info,"asio handle_proxy_write",ec); + m_proxy_data->timer->cancel(); + callback(make_error_code(error::pass_through)); + return; + } + + proxy_read(callback); + } + + void proxy_read(init_handler callback) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"asio connection proxy_read"); + } + + if (!m_proxy_data) { + m_elog.write(log::elevel::library, + "assertion failed: !m_proxy_data in asio::connection::proxy_read"); + m_proxy_data->timer->cancel(); + callback(make_error_code(error::general)); + return; + } + + if (config::enable_multithreading) { + lib::asio::async_read_until( + socket_con_type::get_next_layer(), + m_proxy_data->read_buf, + "\r\n\r\n", + m_strand->wrap(lib::bind( + &type::handle_proxy_read, get_shared(), + callback, + lib::placeholders::_1, lib::placeholders::_2 + )) + ); + } else { + lib::asio::async_read_until( + socket_con_type::get_next_layer(), + m_proxy_data->read_buf, + "\r\n\r\n", + lib::bind( + &type::handle_proxy_read, get_shared(), + callback, + lib::placeholders::_1, lib::placeholders::_2 + ) + ); + } + } + + /// Proxy read callback + /** + * @param init_handler The function to call back + * @param ec The status code + * @param bytes_transferred The number of bytes read + */ + void handle_proxy_read(init_handler callback, + lib::asio::error_code const & ec, size_t) + { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel, + "asio connection handle_proxy_read"); + } + + // Timer expired or the operation was aborted for some reason. + // Whatever aborted it will be issuing the callback so we are safe to + // return + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(m_proxy_data->timer->expires_from_now())) + { + m_elog.write(log::elevel::devel,"read operation aborted"); + return; + } + + // At this point there is no need to wait for the timer anymore + m_proxy_data->timer->cancel(); + + if (ec) { + m_elog.write(log::elevel::info, + "asio handle_proxy_read error: "+ec.message()); + callback(make_error_code(error::pass_through)); + } else { + if (!m_proxy_data) { + m_elog.write(log::elevel::library, + "assertion failed: !m_proxy_data in asio::connection::handle_proxy_read"); + callback(make_error_code(error::general)); + return; + } + + std::istream input(&m_proxy_data->read_buf); + + m_proxy_data->res.consume(input); + + if (!m_proxy_data->res.headers_ready()) { + // we read until the headers were done in theory but apparently + // they aren't. Internal endpoint error. + callback(make_error_code(error::general)); + return; + } + + m_alog.write(log::alevel::devel,m_proxy_data->res.raw()); + + if (m_proxy_data->res.get_status_code() != http::status_code::ok) { + // got an error response back + // TODO: expose this error in a programmatically accessible way? + // if so, see below for an option on how to do this. + std::stringstream s; + s << "Proxy connection error: " + << m_proxy_data->res.get_status_code() + << " (" + << m_proxy_data->res.get_status_msg() + << ")"; + m_elog.write(log::elevel::info,s.str()); + callback(make_error_code(error::proxy_failed)); + return; + } + + // we have successfully established a connection to the proxy, now + // we can continue and the proxy will transparently forward the + // WebSocket connection. + + // TODO: decide if we want an on_proxy callback that would allow + // access to the proxy response. + + // free the proxy buffers and req/res objects as they aren't needed + // anymore + m_proxy_data.reset(); + + // Continue with post proxy initialization + post_init(callback); + } + } + + /// read at least num_bytes bytes into buf and then call handler. + void async_read_at_least(size_t num_bytes, char *buf, size_t len, + read_handler handler) + { + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "asio async_read_at_least: " << num_bytes; + m_alog.write(log::alevel::devel,s.str()); + } + + // TODO: safety vs speed ? + // maybe move into an if devel block + /*if (num_bytes > len) { + m_elog.write(log::elevel::devel, + "asio async_read_at_least error::invalid_num_bytes"); + handler(make_error_code(transport::error::invalid_num_bytes), + size_t(0)); + return; + }*/ + + if (config::enable_multithreading) { + lib::asio::async_read( + socket_con_type::get_socket(), + lib::asio::buffer(buf,len), + lib::asio::transfer_at_least(num_bytes), + m_strand->wrap(make_custom_alloc_handler( + m_read_handler_allocator, + lib::bind( + &type::handle_async_read, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + )) + ); + } else { + lib::asio::async_read( + socket_con_type::get_socket(), + lib::asio::buffer(buf,len), + lib::asio::transfer_at_least(num_bytes), + make_custom_alloc_handler( + m_read_handler_allocator, + lib::bind( + &type::handle_async_read, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + ) + ); + } + + } + + void handle_async_read(read_handler handler, lib::asio::error_code const & ec, + size_t bytes_transferred) + { + m_alog.write(log::alevel::devel, "asio con handle_async_read"); + + // translate asio error codes into more lib::error_codes + lib::error_code tec; + if (ec == lib::asio::error::eof) { + tec = make_error_code(transport::error::eof); + } else if (ec) { + // We don't know much more about the error at this point. As our + // socket/security policy if it knows more: + tec = socket_con_type::translate_ec(ec); + m_tec = ec; + + if (tec == transport::error::tls_error || + tec == transport::error::pass_through) + { + // These are aggregate/catch all errors. Log some human readable + // information to the info channel to give library users some + // more details about why the upstream method may have failed. + log_err(log::elevel::info,"asio async_read_at_least",ec); + } + } + if (handler) { + handler(tec,bytes_transferred); + } else { + // This can happen in cases where the connection is terminated while + // the transport is waiting on a read. + m_alog.write(log::alevel::devel, + "handle_async_read called with null read handler"); + } + } + + /// Initiate a potentially asyncronous write of the given buffer + void async_write(const char* buf, size_t len, write_handler handler) { + m_bufs.push_back(lib::asio::buffer(buf,len)); + + if (config::enable_multithreading) { + lib::asio::async_write( + socket_con_type::get_socket(), + m_bufs, + m_strand->wrap(make_custom_alloc_handler( + m_write_handler_allocator, + lib::bind( + &type::handle_async_write, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + )) + ); + } else { + lib::asio::async_write( + socket_con_type::get_socket(), + m_bufs, + make_custom_alloc_handler( + m_write_handler_allocator, + lib::bind( + &type::handle_async_write, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + ) + ); + } + } + + /// Initiate a potentially asyncronous write of the given buffers + void async_write(std::vector const & bufs, write_handler handler) { + std::vector::const_iterator it; + + for (it = bufs.begin(); it != bufs.end(); ++it) { + m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len)); + } + + if (config::enable_multithreading) { + lib::asio::async_write( + socket_con_type::get_socket(), + m_bufs, + m_strand->wrap(make_custom_alloc_handler( + m_write_handler_allocator, + lib::bind( + &type::handle_async_write, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + )) + ); + } else { + lib::asio::async_write( + socket_con_type::get_socket(), + m_bufs, + make_custom_alloc_handler( + m_write_handler_allocator, + lib::bind( + &type::handle_async_write, get_shared(), + handler, + lib::placeholders::_1, lib::placeholders::_2 + ) + ) + ); + } + } + + /// Async write callback + /** + * @param ec The status code + * @param bytes_transferred The number of bytes read + */ + void handle_async_write(write_handler handler, lib::asio::error_code const & ec, size_t) { + m_bufs.clear(); + lib::error_code tec; + if (ec) { + log_err(log::elevel::info,"asio async_write",ec); + tec = make_error_code(transport::error::pass_through); + } + if (handler) { + handler(tec); + } else { + // This can happen in cases where the connection is terminated while + // the transport is waiting on a read. + m_alog.write(log::alevel::devel, + "handle_async_write called with null write handler"); + } + } + + /// Set Connection Handle + /** + * See common/connection_hdl.hpp for information + * + * @param hdl A connection_hdl that the transport will use to refer + * to itself + */ + void set_handle(connection_hdl hdl) { + m_connection_hdl = hdl; + socket_con_type::set_handle(hdl); + } + + /// Trigger the on_interrupt handler + /** + * This needs to be thread safe + */ + lib::error_code interrupt(interrupt_handler handler) { + if (config::enable_multithreading) { + m_io_service->post(m_strand->wrap(handler)); + } else { + m_io_service->post(handler); + } + return lib::error_code(); + } + + lib::error_code dispatch(dispatch_handler handler) { + if (config::enable_multithreading) { + m_io_service->post(m_strand->wrap(handler)); + } else { + m_io_service->post(handler); + } + return lib::error_code(); + } + + /*void handle_interrupt(interrupt_handler handler) { + handler(); + }*/ + + /// close and clean up the underlying socket + void async_shutdown(shutdown_handler callback) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"asio connection async_shutdown"); + } + + timer_ptr shutdown_timer; + shutdown_timer = set_timer( + config::timeout_socket_shutdown, + lib::bind( + &type::handle_async_shutdown_timeout, + get_shared(), + shutdown_timer, + callback, + lib::placeholders::_1 + ) + ); + + socket_con_type::async_shutdown( + lib::bind( + &type::handle_async_shutdown, + get_shared(), + shutdown_timer, + callback, + lib::placeholders::_1 + ) + ); + } + + /// Async shutdown timeout handler + /** + * @param shutdown_timer A pointer to the timer to keep it in scope + * @param callback The function to call back + * @param ec The status code + */ + void handle_async_shutdown_timeout(timer_ptr, init_handler callback, + lib::error_code const & ec) + { + lib::error_code ret_ec; + + if (ec) { + if (ec == transport::error::operation_aborted) { + m_alog.write(log::alevel::devel, + "asio socket shutdown timer cancelled"); + return; + } + + log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec); + ret_ec = ec; + } else { + ret_ec = make_error_code(transport::error::timeout); + } + + m_alog.write(log::alevel::devel, + "Asio transport socket shutdown timed out"); + cancel_socket_checked(); + callback(ret_ec); + } + + void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler + callback, lib::asio::error_code const & ec) + { + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(shutdown_timer->expires_from_now())) + { + m_alog.write(log::alevel::devel,"async_shutdown cancelled"); + return; + } + + shutdown_timer->cancel(); + + lib::error_code tec; + if (ec) { + if (ec == lib::asio::error::not_connected) { + // The socket was already closed when we tried to close it. This + // happens periodically (usually if a read or write fails + // earlier and if it is a real error will be caught at another + // level of the stack. + } else { + // We don't know anything more about this error, give our + // socket/security policy a crack at it. + tec = socket_con_type::translate_ec(ec); + m_tec = ec; + + if (tec == transport::error::tls_short_read) { + // TLS short read at this point is somewhat expected if both + // sides try and end the connection at the same time or if + // SSLv2 is being used. In general there is nothing that can + // be done here other than a low level development log. + } else { + // all other errors are effectively pass through errors of + // some sort so print some detail on the info channel for + // library users to look up if needed. + log_err(log::elevel::info,"asio async_shutdown",ec); + } + } + } else { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel, + "asio con handle_async_shutdown"); + } + } + callback(tec); + } + + /// Cancel the underlying socket and log any errors + void cancel_socket_checked() { + lib::asio::error_code cec = socket_con_type::cancel_socket(); + if (cec) { + if (cec == lib::asio::error::operation_not_supported) { + // cancel not supported on this OS, ignore and log at dev level + m_alog.write(log::alevel::devel, "socket cancel not supported"); + } else { + log_err(log::elevel::warn, "socket cancel failed", cec); + } + } + } + +private: + /// Convenience method for logging the code and message for an error_code + template + void log_err(log::level l, const char * msg, const error_type & ec) { + std::stringstream s; + s << msg << " error: " << ec << " (" << ec.message() << ")"; + m_elog.write(l,s.str()); + } + + // static settings + const bool m_is_server; + alog_type& m_alog; + elog_type& m_elog; + + struct proxy_data { + proxy_data() : timeout_proxy(config::timeout_proxy) {} + + request_type req; + response_type res; + std::string write_buf; + lib::asio::streambuf read_buf; + long timeout_proxy; + timer_ptr timer; + }; + + std::string m_proxy; + lib::shared_ptr m_proxy_data; + + // transport resources + io_service_ptr m_io_service; + strand_ptr m_strand; + connection_hdl m_connection_hdl; + + std::vector m_bufs; + + /// Detailed internal error code + lib::asio::error_code m_tec; + + // Handlers + tcp_init_handler m_tcp_pre_init_handler; + tcp_init_handler m_tcp_post_init_handler; + + handler_allocator m_read_handler_allocator; + handler_allocator m_write_handler_allocator; +}; + + +} // namespace asio +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP diff --git a/websocketpp/transport/asio/endpoint.hpp b/websocketpp/transport/asio/endpoint.hpp new file mode 100644 index 00000000..46ff24c0 --- /dev/null +++ b/websocketpp/transport/asio/endpoint.hpp @@ -0,0 +1,1147 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_ASIO_HPP +#define WEBSOCKETPP_TRANSPORT_ASIO_HPP + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace asio { + +/// Asio based endpoint transport component +/** + * transport::asio::endpoint implements an endpoint transport component using + * Asio. + */ +template +class endpoint : public config::socket_type { +public: + /// Type of this endpoint transport component + typedef endpoint type; + + /// Type of the concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of the socket policy + typedef typename config::socket_type socket_type; + /// Type of the error logging policy + typedef typename config::elog_type elog_type; + /// Type of the access logging policy + typedef typename config::alog_type alog_type; + + /// Type of the socket connection component + typedef typename socket_type::socket_con_type socket_con_type; + /// Type of a shared pointer to the socket connection component + typedef typename socket_con_type::ptr socket_con_ptr; + + /// Type of the connection transport component associated with this + /// endpoint transport component + typedef asio::connection transport_con_type; + /// Type of a shared pointer to the connection transport component + /// associated with this endpoint transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + /// Type of a pointer to the ASIO io_service being used + typedef lib::asio::io_service * io_service_ptr; + /// Type of a shared pointer to the acceptor being used + typedef lib::shared_ptr acceptor_ptr; + /// Type of a shared pointer to the resolver being used + typedef lib::shared_ptr resolver_ptr; + /// Type of timer handle + typedef lib::shared_ptr timer_ptr; + /// Type of a shared pointer to an io_service work object + typedef lib::shared_ptr work_ptr; + + // generate and manage our own io_service + explicit endpoint() + : m_io_service(NULL) + , m_external_io_service(false) + , m_listen_backlog(0) + , m_reuse_addr(false) + , m_state(UNINITIALIZED) + { + //std::cout << "transport::asio::endpoint constructor" << std::endl; + } + + ~endpoint() { + // clean up our io_service if we were initialized with an internal one. + + // Explicitly destroy local objects + m_acceptor.reset(); + m_resolver.reset(); + m_work.reset(); + if (m_state != UNINITIALIZED && !m_external_io_service) { + delete m_io_service; + } + } + + /// transport::asio objects are moveable but not copyable or assignable. + /// The following code sets this situation up based on whether or not we + /// have C++11 support or not +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + endpoint(const endpoint & src) = delete; + endpoint& operator= (const endpoint & rhs) = delete; +#else +private: + endpoint(const endpoint & src); + endpoint & operator= (const endpoint & rhs); +public: +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + endpoint (endpoint && src) + : config::socket_type(std::move(src)) + , m_tcp_pre_init_handler(src.m_tcp_pre_init_handler) + , m_tcp_post_init_handler(src.m_tcp_post_init_handler) + , m_io_service(src.m_io_service) + , m_external_io_service(src.m_external_io_service) + , m_acceptor(src.m_acceptor) + , m_listen_backlog(lib::asio::socket_base::max_connections) + , m_reuse_addr(src.m_reuse_addr) + , m_elog(src.m_elog) + , m_alog(src.m_alog) + , m_state(src.m_state) + { + src.m_io_service = NULL; + src.m_external_io_service = false; + src.m_acceptor = NULL; + src.m_state = UNINITIALIZED; + } + + /*endpoint & operator= (const endpoint && rhs) { + if (this != &rhs) { + m_io_service = rhs.m_io_service; + m_external_io_service = rhs.m_external_io_service; + m_acceptor = rhs.m_acceptor; + m_listen_backlog = rhs.m_listen_backlog; + m_reuse_addr = rhs.m_reuse_addr; + m_state = rhs.m_state; + + rhs.m_io_service = NULL; + rhs.m_external_io_service = false; + rhs.m_acceptor = NULL; + rhs.m_listen_backlog = lib::asio::socket_base::max_connections; + rhs.m_state = UNINITIALIZED; + + // TODO: this needs to be updated + } + return *this; + }*/ +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + + /// Return whether or not the endpoint produces secure connections. + bool is_secure() const { + return socket_type::is_secure(); + } + + /// initialize asio transport with external io_service (exception free) + /** + * Initialize the ASIO transport policy for this endpoint using the provided + * io_service object. asio_init must be called exactly once on any endpoint + * that uses transport::asio before it can be used. + * + * @param ptr A pointer to the io_service to use for asio events + * @param ec Set to indicate what error occurred, if any. + */ + void init_asio(io_service_ptr ptr, lib::error_code & ec) { + if (m_state != UNINITIALIZED) { + m_elog->write(log::elevel::library, + "asio::init_asio called from the wrong state"); + using websocketpp::error::make_error_code; + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + m_alog->write(log::alevel::devel,"asio::init_asio"); + + m_io_service = ptr; + m_external_io_service = true; + m_acceptor = lib::make_shared( + lib::ref(*m_io_service)); + + m_state = READY; + ec = lib::error_code(); + } + + /// initialize asio transport with external io_service + /** + * Initialize the ASIO transport policy for this endpoint using the provided + * io_service object. asio_init must be called exactly once on any endpoint + * that uses transport::asio before it can be used. + * + * @param ptr A pointer to the io_service to use for asio events + */ + void init_asio(io_service_ptr ptr) { + lib::error_code ec; + init_asio(ptr,ec); + if (ec) { throw exception(ec); } + } + + /// Initialize asio transport with internal io_service (exception free) + /** + * This method of initialization will allocate and use an internally managed + * io_service. + * + * @see init_asio(io_service_ptr ptr) + * + * @param ec Set to indicate what error occurred, if any. + */ + void init_asio(lib::error_code & ec) { + // Use a smart pointer until the call is successful and ownership has + // successfully been taken. Use unique_ptr when available. + // TODO: remove the use of auto_ptr when C++98/03 support is no longer + // necessary. +#ifdef _WEBSOCKETPP_CPP11_MEMORY_ + lib::unique_ptr service(new lib::asio::io_service()); +#else + lib::auto_ptr service(new lib::asio::io_service()); +#endif + init_asio(service.get(), ec); + if( !ec ) service.release(); // Call was successful, transfer ownership + m_external_io_service = false; + } + + /// Initialize asio transport with internal io_service + /** + * This method of initialization will allocate and use an internally managed + * io_service. + * + * @see init_asio(io_service_ptr ptr) + */ + void init_asio() { + // Use a smart pointer until the call is successful and ownership has + // successfully been taken. Use unique_ptr when available. + // TODO: remove the use of auto_ptr when C++98/03 support is no longer + // necessary. +#ifdef _WEBSOCKETPP_CPP11_MEMORY_ + lib::unique_ptr service(new lib::asio::io_service()); +#else + lib::auto_ptr service(new lib::asio::io_service()); +#endif + init_asio( service.get() ); + // If control got this far without an exception, then ownership has successfully been taken + service.release(); + m_external_io_service = false; + } + + /// Sets the tcp pre init handler + /** + * The tcp pre init handler is called after the raw tcp connection has been + * established but before any additional wrappers (proxy connects, TLS + * handshakes, etc) have been performed. + * + * @since 0.3.0 + * + * @param h The handler to call on tcp pre init. + */ + void set_tcp_pre_init_handler(tcp_init_handler h) { + m_tcp_pre_init_handler = h; + } + + /// Sets the tcp pre init handler (deprecated) + /** + * The tcp pre init handler is called after the raw tcp connection has been + * established but before any additional wrappers (proxy connects, TLS + * handshakes, etc) have been performed. + * + * @deprecated Use set_tcp_pre_init_handler instead + * + * @param h The handler to call on tcp pre init. + */ + void set_tcp_init_handler(tcp_init_handler h) { + set_tcp_pre_init_handler(h); + } + + /// Sets the tcp post init handler + /** + * The tcp post init handler is called after the tcp connection has been + * established and all additional wrappers (proxy connects, TLS handshakes, + * etc have been performed. This is fired before any bytes are read or any + * WebSocket specific handshake logic has been performed. + * + * @since 0.3.0 + * + * @param h The handler to call on tcp post init. + */ + void set_tcp_post_init_handler(tcp_init_handler h) { + m_tcp_post_init_handler = h; + } + + /// Sets the maximum length of the queue of pending connections. + /** + * Sets the maximum length of the queue of pending connections. Increasing + * this will allow WebSocket++ to queue additional incoming connections. + * Setting it higher may prevent failed connections at high connection rates + * but may cause additional latency. + * + * For this value to take effect you may need to adjust operating system + * settings. + * + * New values affect future calls to listen only. + * + * A value of zero will use the operating system default. This is the + * default value. + * + * @since 0.3.0 + * + * @param backlog The maximum length of the queue of pending connections + */ + void set_listen_backlog(int backlog) { + m_listen_backlog = backlog; + } + + /// Sets whether to use the SO_REUSEADDR flag when opening listening sockets + /** + * Specifies whether or not to use the SO_REUSEADDR TCP socket option. What + * this flag does depends on your operating system. Please consult operating + * system documentation for more details. + * + * New values affect future calls to listen only. + * + * The default is false. + * + * @since 0.3.0 + * + * @param value Whether or not to use the SO_REUSEADDR option + */ + void set_reuse_addr(bool value) { + m_reuse_addr = value; + } + + /// Retrieve a reference to the endpoint's io_service + /** + * The io_service may be an internal or external one. This may be used to + * call methods of the io_service that are not explicitly wrapped by the + * endpoint. + * + * This method is only valid after the endpoint has been initialized with + * `init_asio`. No error will be returned if it isn't. + * + * @return A reference to the endpoint's io_service + */ + lib::asio::io_service & get_io_service() { + return *m_io_service; + } + + /// Get local TCP endpoint + /** + * Extracts the local endpoint from the acceptor. This represents the + * address that WebSocket++ is listening on. + * + * Sets a bad_descriptor error if the acceptor is not currently listening + * or otherwise unavailable. + * + * @since 0.7.0 + * + * @param ec Set to indicate what error occurred, if any. + * @return The local endpoint + */ + lib::asio::ip::tcp::endpoint get_local_endpoint(lib::asio::error_code & ec) { + if (m_acceptor) { + return m_acceptor->local_endpoint(ec); + } else { + ec = lib::asio::error::make_error_code(lib::asio::error::bad_descriptor); + return lib::asio::ip::tcp::endpoint(); + } + } + + /// Set up endpoint for listening manually (exception free) + /** + * Bind the internal acceptor using the specified settings. The endpoint + * must have been initialized by calling init_asio before listening. + * + * @param ep An endpoint to read settings from + * @param ec Set to indicate what error occurred, if any. + */ + void listen(lib::asio::ip::tcp::endpoint const & ep, lib::error_code & ec) + { + if (m_state != READY) { + m_elog->write(log::elevel::library, + "asio::listen called from the wrong state"); + using websocketpp::error::make_error_code; + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + m_alog->write(log::alevel::devel,"asio::listen"); + + lib::asio::error_code bec; + + m_acceptor->open(ep.protocol(),bec); + if (!bec) { + m_acceptor->set_option(lib::asio::socket_base::reuse_address(m_reuse_addr),bec); + } + if (!bec) { + m_acceptor->bind(ep,bec); + } + if (!bec) { + m_acceptor->listen(m_listen_backlog,bec); + } + if (bec) { + if (m_acceptor->is_open()) { + m_acceptor->close(); + } + log_err(log::elevel::info,"asio listen",bec); + ec = make_error_code(error::pass_through); + } else { + m_state = LISTENING; + ec = lib::error_code(); + } + } + + /// Set up endpoint for listening manually + /** + * Bind the internal acceptor using the settings specified by the endpoint e + * + * @param ep An endpoint to read settings from + */ + void listen(lib::asio::ip::tcp::endpoint const & ep) { + lib::error_code ec; + listen(ep,ec); + if (ec) { throw exception(ec); } + } + + /// Set up endpoint for listening with protocol and port (exception free) + /** + * Bind the internal acceptor using the given internet protocol and port. + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * Common options include: + * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6() + * - IPv4 only: lib::asio::ip::tcp::v4() + * + * @param internet_protocol The internet protocol to use. + * @param port The port to listen on. + * @param ec Set to indicate what error occurred, if any. + */ + template + void listen(InternetProtocol const & internet_protocol, uint16_t port, + lib::error_code & ec) + { + lib::asio::ip::tcp::endpoint ep(internet_protocol, port); + listen(ep,ec); + } + + /// Set up endpoint for listening with protocol and port + /** + * Bind the internal acceptor using the given internet protocol and port. + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * Common options include: + * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6() + * - IPv4 only: lib::asio::ip::tcp::v4() + * + * @param internet_protocol The internet protocol to use. + * @param port The port to listen on. + */ + template + void listen(InternetProtocol const & internet_protocol, uint16_t port) + { + lib::asio::ip::tcp::endpoint ep(internet_protocol, port); + listen(ep); + } + + /// Set up endpoint for listening on a port (exception free) + /** + * Bind the internal acceptor using the given port. The IPv6 protocol with + * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use + * the overload that allows specifying the protocol explicitly. + * + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * @param port The port to listen on. + * @param ec Set to indicate what error occurred, if any. + */ + void listen(uint16_t port, lib::error_code & ec) { + listen(lib::asio::ip::tcp::v6(), port, ec); + } + + /// Set up endpoint for listening on a port + /** + * Bind the internal acceptor using the given port. The IPv6 protocol with + * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use + * the overload that allows specifying the protocol explicitly. + * + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * @param port The port to listen on. + * @param ec Set to indicate what error occurred, if any. + */ + void listen(uint16_t port) { + listen(lib::asio::ip::tcp::v6(), port); + } + + /// Set up endpoint for listening on a host and service (exception free) + /** + * Bind the internal acceptor using the given host and service. More details + * about what host and service can be are available in the Asio + * documentation for ip::basic_resolver_query::basic_resolver_query's + * constructors. + * + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. + * @param ec Set to indicate what error occurred, if any. + */ + void listen(std::string const & host, std::string const & service, + lib::error_code & ec) + { + using lib::asio::ip::tcp; + tcp::resolver r(*m_io_service); + tcp::resolver::query query(host, service); + tcp::resolver::iterator endpoint_iterator = r.resolve(query); + tcp::resolver::iterator end; + if (endpoint_iterator == end) { + m_elog->write(log::elevel::library, + "asio::listen could not resolve the supplied host or service"); + ec = make_error_code(error::invalid_host_service); + return; + } + listen(*endpoint_iterator,ec); + } + + /// Set up endpoint for listening on a host and service + /** + * Bind the internal acceptor using the given host and service. More details + * about what host and service can be are available in the Asio + * documentation for ip::basic_resolver_query::basic_resolver_query's + * constructors. + * + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * @param host A string identifying a location. May be a descriptive name or + * a numeric address string. + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. + * @param ec Set to indicate what error occurred, if any. + */ + void listen(std::string const & host, std::string const & service) + { + lib::error_code ec; + listen(host,service,ec); + if (ec) { throw exception(ec); } + } + + /// Stop listening (exception free) + /** + * Stop listening and accepting new connections. This will not end any + * existing connections. + * + * @since 0.3.0-alpha4 + * @param ec A status code indicating an error, if any. + */ + void stop_listening(lib::error_code & ec) { + if (m_state != LISTENING) { + m_elog->write(log::elevel::library, + "asio::listen called from the wrong state"); + using websocketpp::error::make_error_code; + ec = make_error_code(websocketpp::error::invalid_state); + return; + } + + m_acceptor->close(); + m_state = READY; + ec = lib::error_code(); + } + + /// Stop listening + /** + * Stop listening and accepting new connections. This will not end any + * existing connections. + * + * @since 0.3.0-alpha4 + */ + void stop_listening() { + lib::error_code ec; + stop_listening(ec); + if (ec) { throw exception(ec); } + } + + /// Check if the endpoint is listening + /** + * @return Whether or not the endpoint is listening. + */ + bool is_listening() const { + return (m_state == LISTENING); + } + + /// wraps the run method of the internal io_service object + std::size_t run() { + return m_io_service->run(); + } + + /// wraps the run_one method of the internal io_service object + /** + * @since 0.3.0-alpha4 + */ + std::size_t run_one() { + return m_io_service->run_one(); + } + + /// wraps the stop method of the internal io_service object + void stop() { + m_io_service->stop(); + } + + /// wraps the poll method of the internal io_service object + std::size_t poll() { + return m_io_service->poll(); + } + + /// wraps the poll_one method of the internal io_service object + std::size_t poll_one() { + return m_io_service->poll_one(); + } + + /// wraps the reset method of the internal io_service object + void reset() { + m_io_service->reset(); + } + + /// wraps the stopped method of the internal io_service object + bool stopped() const { + return m_io_service->stopped(); + } + + /// Marks the endpoint as perpetual, stopping it from exiting when empty + /** + * Marks the endpoint as perpetual. Perpetual endpoints will not + * automatically exit when they run out of connections to process. To stop + * a perpetual endpoint call `end_perpetual`. + * + * An endpoint may be marked perpetual at any time by any thread. It must be + * called either before the endpoint has run out of work or before it was + * started + * + * @since 0.3.0 + */ + void start_perpetual() { + m_work = lib::make_shared( + lib::ref(*m_io_service) + ); + } + + /// Clears the endpoint's perpetual flag, allowing it to exit when empty + /** + * Clears the endpoint's perpetual flag. This will cause the endpoint's run + * method to exit normally when it runs out of connections. If there are + * currently active connections it will not end until they are complete. + * + * @since 0.3.0 + */ + void stop_perpetual() { + m_work.reset(); + } + + /// Call back a function after a period of time. + /** + * Sets a timer that calls back a function after the specified period of + * milliseconds. Returns a handle that can be used to cancel the timer. + * A cancelled timer will return the error code error::operation_aborted + * A timer that expired will return no error. + * + * @param duration Length of time to wait in milliseconds + * @param callback The function to call back when the timer has expired + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long duration, timer_handler callback) { + timer_ptr new_timer = lib::make_shared( + *m_io_service, + lib::asio::milliseconds(duration) + ); + + new_timer->async_wait( + lib::bind( + &type::handle_timer, + this, + new_timer, + callback, + lib::placeholders::_1 + ) + ); + + return new_timer; + } + + /// Timer handler + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param t Pointer to the timer in question + * @param callback The function to call back + * @param ec A status code indicating an error, if any. + */ + void handle_timer(timer_ptr, timer_handler callback, + lib::asio::error_code const & ec) + { + if (ec) { + if (ec == lib::asio::error::operation_aborted) { + callback(make_error_code(transport::error::operation_aborted)); + } else { + m_elog->write(log::elevel::info, + "asio handle_timer error: "+ec.message()); + log_err(log::elevel::info,"asio handle_timer",ec); + callback(make_error_code(error::pass_through)); + } + } else { + callback(lib::error_code()); + } + } + + /// Accept the next connection attempt and assign it to con (exception free) + /** + * @param tcon The connection to accept into. + * @param callback The function to call when the operation is complete. + * @param ec A status code indicating an error, if any. + */ + void async_accept(transport_con_ptr tcon, accept_handler callback, + lib::error_code & ec) + { + if (m_state != LISTENING) { + using websocketpp::error::make_error_code; + ec = make_error_code(websocketpp::error::async_accept_not_listening); + return; + } + + m_alog->write(log::alevel::devel, "asio::async_accept"); + + if (config::enable_multithreading) { + m_acceptor->async_accept( + tcon->get_raw_socket(), + tcon->get_strand()->wrap(lib::bind( + &type::handle_accept, + this, + callback, + lib::placeholders::_1 + )) + ); + } else { + m_acceptor->async_accept( + tcon->get_raw_socket(), + lib::bind( + &type::handle_accept, + this, + callback, + lib::placeholders::_1 + ) + ); + } + } + + /// Accept the next connection attempt and assign it to con. + /** + * @param tcon The connection to accept into. + * @param callback The function to call when the operation is complete. + */ + void async_accept(transport_con_ptr tcon, accept_handler callback) { + lib::error_code ec; + async_accept(tcon,callback,ec); + if (ec) { throw exception(ec); } + } +protected: + /// Initialize logging + /** + * The loggers are located in the main endpoint class. As such, the + * transport doesn't have direct access to them. This method is called + * by the endpoint constructor to allow shared logging from the transport + * component. These are raw pointers to member variables of the endpoint. + * In particular, they cannot be used in the transport constructor as they + * haven't been constructed yet, and cannot be used in the transport + * destructor as they will have been destroyed by then. + */ + void init_logging(alog_type* a, elog_type* e) { + m_alog = a; + m_elog = e; + } + + void handle_accept(accept_handler callback, lib::asio::error_code const & + asio_ec) + { + lib::error_code ret_ec; + + m_alog->write(log::alevel::devel, "asio::handle_accept"); + + if (asio_ec) { + if (asio_ec == lib::asio::errc::operation_canceled) { + ret_ec = make_error_code(websocketpp::error::operation_canceled); + } else { + log_err(log::elevel::info,"asio handle_accept",asio_ec); + ret_ec = make_error_code(error::pass_through); + } + } + + callback(ret_ec); + } + + /// Initiate a new connection + // TODO: there have to be some more failure conditions here + void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) { + using namespace lib::asio::ip; + + // Create a resolver + if (!m_resolver) { + m_resolver = lib::make_shared( + lib::ref(*m_io_service)); + } + + tcon->set_uri(u); + + std::string proxy = tcon->get_proxy(); + std::string host; + std::string port; + + if (proxy.empty()) { + host = u->get_host(); + port = u->get_port_str(); + } else { + lib::error_code ec; + + uri_ptr pu = lib::make_shared(proxy); + + if (!pu->get_valid()) { + cb(make_error_code(error::proxy_invalid)); + return; + } + + ec = tcon->proxy_init(u->get_authority()); + if (ec) { + cb(ec); + return; + } + + host = pu->get_host(); + port = pu->get_port_str(); + } + + tcp::resolver::query query(host,port); + + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel, + "starting async DNS resolve for "+host+":"+port); + } + + timer_ptr dns_timer; + + dns_timer = tcon->set_timer( + config::timeout_dns_resolve, + lib::bind( + &type::handle_resolve_timeout, + this, + dns_timer, + cb, + lib::placeholders::_1 + ) + ); + + if (config::enable_multithreading) { + m_resolver->async_resolve( + query, + tcon->get_strand()->wrap(lib::bind( + &type::handle_resolve, + this, + tcon, + dns_timer, + cb, + lib::placeholders::_1, + lib::placeholders::_2 + )) + ); + } else { + m_resolver->async_resolve( + query, + lib::bind( + &type::handle_resolve, + this, + tcon, + dns_timer, + cb, + lib::placeholders::_1, + lib::placeholders::_2 + ) + ); + } + } + + /// DNS resolution timeout handler + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param dns_timer Pointer to the timer in question + * @param callback The function to call back + * @param ec A status code indicating an error, if any. + */ + void handle_resolve_timeout(timer_ptr, connect_handler callback, + lib::error_code const & ec) + { + lib::error_code ret_ec; + + if (ec) { + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel, + "asio handle_resolve_timeout timer cancelled"); + return; + } + + log_err(log::elevel::devel,"asio handle_resolve_timeout",ec); + ret_ec = ec; + } else { + ret_ec = make_error_code(transport::error::timeout); + } + + m_alog->write(log::alevel::devel,"DNS resolution timed out"); + m_resolver->cancel(); + callback(ret_ec); + } + + void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer, + connect_handler callback, lib::asio::error_code const & ec, + lib::asio::ip::tcp::resolver::iterator iterator) + { + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(dns_timer->expires_from_now())) + { + m_alog->write(log::alevel::devel,"async_resolve cancelled"); + return; + } + + dns_timer->cancel(); + + if (ec) { + log_err(log::elevel::info,"asio async_resolve",ec); + callback(make_error_code(error::pass_through)); + return; + } + + if (m_alog->static_test(log::alevel::devel)) { + std::stringstream s; + s << "Async DNS resolve successful. Results: "; + + lib::asio::ip::tcp::resolver::iterator it, end; + for (it = iterator; it != end; ++it) { + s << (*it).endpoint() << " "; + } + + m_alog->write(log::alevel::devel,s.str()); + } + + m_alog->write(log::alevel::devel,"Starting async connect"); + + timer_ptr con_timer; + + con_timer = tcon->set_timer( + config::timeout_connect, + lib::bind( + &type::handle_connect_timeout, + this, + tcon, + con_timer, + callback, + lib::placeholders::_1 + ) + ); + + if (config::enable_multithreading) { + lib::asio::async_connect( + tcon->get_raw_socket(), + iterator, + tcon->get_strand()->wrap(lib::bind( + &type::handle_connect, + this, + tcon, + con_timer, + callback, + lib::placeholders::_1 + )) + ); + } else { + lib::asio::async_connect( + tcon->get_raw_socket(), + iterator, + lib::bind( + &type::handle_connect, + this, + tcon, + con_timer, + callback, + lib::placeholders::_1 + ) + ); + } + } + + /// Asio connect timeout handler + /** + * The timer pointer is included to ensure the timer isn't destroyed until + * after it has expired. + * + * @param tcon Pointer to the transport connection that is being connected + * @param con_timer Pointer to the timer in question + * @param callback The function to call back + * @param ec A status code indicating an error, if any. + */ + void handle_connect_timeout(transport_con_ptr tcon, timer_ptr, + connect_handler callback, lib::error_code const & ec) + { + lib::error_code ret_ec; + + if (ec) { + if (ec == transport::error::operation_aborted) { + m_alog->write(log::alevel::devel, + "asio handle_connect_timeout timer cancelled"); + return; + } + + log_err(log::elevel::devel,"asio handle_connect_timeout",ec); + ret_ec = ec; + } else { + ret_ec = make_error_code(transport::error::timeout); + } + + m_alog->write(log::alevel::devel,"TCP connect timed out"); + tcon->cancel_socket_checked(); + callback(ret_ec); + } + + void handle_connect(transport_con_ptr tcon, timer_ptr con_timer, + connect_handler callback, lib::asio::error_code const & ec) + { + if (ec == lib::asio::error::operation_aborted || + lib::asio::is_neg(con_timer->expires_from_now())) + { + m_alog->write(log::alevel::devel,"async_connect cancelled"); + return; + } + + con_timer->cancel(); + + if (ec) { + log_err(log::elevel::info,"asio async_connect",ec); + callback(make_error_code(error::pass_through)); + return; + } + + if (m_alog->static_test(log::alevel::devel)) { + m_alog->write(log::alevel::devel, + "Async connect to "+tcon->get_remote_endpoint()+" successful."); + } + + callback(lib::error_code()); + } + + /// Initialize a connection + /** + * init is called by an endpoint once for each newly created connection. + * It's purpose is to give the transport policy the chance to perform any + * transport specific initialization that couldn't be done via the default + * constructor. + * + * @param tcon A pointer to the transport portion of the connection. + * + * @return A status code indicating the success or failure of the operation + */ + lib::error_code init(transport_con_ptr tcon) { + m_alog->write(log::alevel::devel, "transport::asio::init"); + + // Initialize the connection socket component + socket_type::init(lib::static_pointer_cast(tcon)); + + lib::error_code ec; + + ec = tcon->init_asio(m_io_service); + if (ec) {return ec;} + + tcon->set_tcp_pre_init_handler(m_tcp_pre_init_handler); + tcon->set_tcp_post_init_handler(m_tcp_post_init_handler); + + return lib::error_code(); + } +private: + /// Convenience method for logging the code and message for an error_code + template + void log_err(log::level l, char const * msg, error_type const & ec) { + std::stringstream s; + s << msg << " error: " << ec << " (" << ec.message() << ")"; + m_elog->write(l,s.str()); + } + + enum state { + UNINITIALIZED = 0, + READY = 1, + LISTENING = 2 + }; + + // Handlers + tcp_init_handler m_tcp_pre_init_handler; + tcp_init_handler m_tcp_post_init_handler; + + // Network Resources + io_service_ptr m_io_service; + bool m_external_io_service; + acceptor_ptr m_acceptor; + resolver_ptr m_resolver; + work_ptr m_work; + + // Network constants + int m_listen_backlog; + bool m_reuse_addr; + + elog_type* m_elog; + alog_type* m_alog; + + // Transport state + state m_state; +}; + +} // namespace asio +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP diff --git a/websocketpp/transport/asio/security/base.hpp b/websocketpp/transport/asio/security/base.hpp new file mode 100644 index 00000000..0f08f404 --- /dev/null +++ b/websocketpp/transport/asio/security/base.hpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP + +#include +#include +#include +#include +#include +#include + +#include + +// Interface that sockets/security policies must implement + +/* + * Endpoint Interface + * + * bool is_secure() const; + * @return Whether or not the endpoint creates secure connections + * + * lib::error_code init(socket_con_ptr scon); + * Called by the transport after a new connection is created to initialize + * the socket component of the connection. + * @param scon Pointer to the socket component of the connection + * @return Error code (empty on success) + */ + + +// Connection +// TODO +// set_hostname(std::string hostname) +// pre_init(init_handler); +// post_init(init_handler); + +namespace websocketpp { +namespace transport { +namespace asio { +namespace socket { + +typedef lib::function shutdown_handler; + +/** + * The transport::asio::socket::* classes are a set of security/socket related + * policies and support code for the ASIO transport types. + */ + +/// Errors related to asio transport sockets +namespace error { + enum value { + /// Catch-all error for security policy errors that don't fit in other + /// categories + security = 1, + + /// Catch-all error for socket component errors that don't fit in other + /// categories + socket, + + /// A function was called in a state that it was illegal to do so. + invalid_state, + + /// The application was prompted to provide a TLS context and it was + /// empty or otherwise invalid + invalid_tls_context, + + /// TLS Handshake Timeout + tls_handshake_timeout, + + /// pass_through from underlying library + pass_through, + + /// Required tls_init handler not present + missing_tls_init_handler, + + /// TLS Handshake Failed + tls_handshake_failed, + + /// Failed to set TLS SNI hostname + tls_failed_sni_hostname + }; +} // namespace error + +/// Error category related to asio transport socket policies +class socket_category : public lib::error_category { +public: + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport.asio.socket"; + } + + std::string message(int value) const { + switch(value) { + case error::security: + return "Security policy error"; + case error::socket: + return "Socket component error"; + case error::invalid_state: + return "Invalid state"; + case error::invalid_tls_context: + return "Invalid or empty TLS context supplied"; + case error::tls_handshake_timeout: + return "TLS handshake timed out"; + case error::pass_through: + return "Pass through from socket policy"; + case error::missing_tls_init_handler: + return "Required tls_init handler not present."; + case error::tls_handshake_failed: + return "TLS handshake failed"; + case error::tls_failed_sni_hostname: + return "Failed to set TLS SNI hostname"; + default: + return "Unknown"; + } + } +}; + +inline lib::error_category const & get_socket_category() { + static socket_category instance; + return instance; +} + +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_socket_category()); +} + +/// Type of asio transport socket policy initialization handlers +typedef lib::function init_handler; + +} // namespace socket +} // namespace asio +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP diff --git a/websocketpp/transport/asio/security/none.hpp b/websocketpp/transport/asio/security/none.hpp new file mode 100644 index 00000000..0e68a65c --- /dev/null +++ b/websocketpp/transport/asio/security/none.hpp @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP +#define WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP + +#include + +#include +#include + +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace asio { +/// A socket policy for the asio transport that implements a plain, unencrypted +/// socket +namespace basic_socket { + +/// The signature of the socket init handler for this socket policy +typedef lib::function + socket_init_handler; + +/// Basic Asio connection socket component +/** + * transport::asio::basic_socket::connection implements a connection socket + * component using Asio ip::tcp::socket. + */ +class connection : public lib::enable_shared_from_this { +public: + /// Type of this connection socket component + typedef connection type; + /// Type of a shared pointer to this connection socket component + typedef lib::shared_ptr ptr; + + /// Type of a pointer to the Asio io_service being used + typedef lib::asio::io_service* io_service_ptr; + /// Type of a pointer to the Asio io_service strand being used + typedef lib::shared_ptr strand_ptr; + /// Type of the ASIO socket being used + typedef lib::asio::ip::tcp::socket socket_type; + /// Type of a shared pointer to the socket being used. + typedef lib::shared_ptr socket_ptr; + + explicit connection() : m_state(UNINITIALIZED) { + //std::cout << "transport::asio::basic_socket::connection constructor" + // << std::endl; + } + + /// Get a shared pointer to this component + ptr get_shared() { + return shared_from_this(); + } + + /// Check whether or not this connection is secure + /** + * @return Whether or not this connection is secure + */ + bool is_secure() const { + return false; + } + + /// Set the socket initialization handler + /** + * The socket initialization handler is called after the socket object is + * created but before it is used. This gives the application a chance to + * set any Asio socket options it needs. + * + * @param h The new socket_init_handler + */ + void set_socket_init_handler(socket_init_handler h) { + m_socket_init_handler = h; + } + + /// Retrieve a pointer to the underlying socket + /** + * This is used internally. It can also be used to set socket options, etc + */ + lib::asio::ip::tcp::socket & get_socket() { + return *m_socket; + } + + /// Retrieve a pointer to the underlying socket + /** + * This is used internally. + */ + lib::asio::ip::tcp::socket & get_next_layer() { + return *m_socket; + } + + /// Retrieve a pointer to the underlying socket + /** + * This is used internally. It can also be used to set socket options, etc + */ + lib::asio::ip::tcp::socket & get_raw_socket() { + return *m_socket; + } + + /// Get the remote endpoint address + /** + * The iostream transport has no information about the ultimate remote + * endpoint. It will return the string "iostream transport". To indicate + * this. + * + * TODO: allow user settable remote endpoint addresses if this seems useful + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint(lib::error_code & ec) const { + std::stringstream s; + + lib::asio::error_code aec; + lib::asio::ip::tcp::endpoint ep = m_socket->remote_endpoint(aec); + + if (aec) { + ec = error::make_error_code(error::pass_through); + s << "Error getting remote endpoint: " << aec + << " (" << aec.message() << ")"; + return s.str(); + } else { + ec = lib::error_code(); + s << ep; + return s.str(); + } + } +protected: + /// Perform one time initializations + /** + * init_asio is called once immediately after construction to initialize + * Asio components to the io_service + * + * @param service A pointer to the endpoint's io_service + * @param strand A shared pointer to the connection's asio strand + * @param is_server Whether or not the endpoint is a server or not. + */ + lib::error_code init_asio (io_service_ptr service, strand_ptr, bool) + { + if (m_state != UNINITIALIZED) { + return socket::make_error_code(socket::error::invalid_state); + } + + m_socket = lib::make_shared( + lib::ref(*service)); + + m_state = READY; + + return lib::error_code(); + } + + /// Set uri hook + /** + * Called by the transport as a connection is being established to provide + * the uri being connected to to the security/socket layer. + * + * This socket policy doesn't use the uri so it is ignored. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + + /// Pre-initialize security policy + /** + * Called by the transport after a new connection is created to initialize + * the socket component of the connection. This method is not allowed to + * write any bytes to the wire. This initialization happens before any + * proxies or other intermediate wrappers are negotiated. + * + * @param callback Handler to call back with completion information + */ + void pre_init(init_handler callback) { + if (m_state != READY) { + callback(socket::make_error_code(socket::error::invalid_state)); + return; + } + + if (m_socket_init_handler) { + m_socket_init_handler(m_hdl,*m_socket); + } + + m_state = READING; + + callback(lib::error_code()); + } + + /// Post-initialize security policy + /** + * Called by the transport after all intermediate proxies have been + * negotiated. This gives the security policy the chance to talk with the + * real remote endpoint for a bit before the websocket handshake. + * + * @param callback Handler to call back with completion information + */ + void post_init(init_handler callback) { + callback(lib::error_code()); + } + + /// Sets the connection handle + /** + * The connection handle is passed to any handlers to identify the + * connection + * + * @param hdl The new handle + */ + void set_handle(connection_hdl hdl) { + m_hdl = hdl; + } + + /// Cancel all async operations on this socket + /** + * Attempts to cancel all async operations on this socket and reports any + * failures. + * + * NOTE: Windows XP and earlier do not support socket cancellation. + * + * @return The error that occurred, if any. + */ + lib::asio::error_code cancel_socket() { + lib::asio::error_code ec; + m_socket->cancel(ec); + return ec; + } + + void async_shutdown(socket::shutdown_handler h) { + lib::asio::error_code ec; + m_socket->shutdown(lib::asio::ip::tcp::socket::shutdown_both, ec); + h(ec); + } + + lib::error_code get_ec() const { + return lib::error_code(); + } + + /// Translate any security policy specific information about an error code + /** + * Translate_ec takes an Asio error code and attempts to convert its value + * to an appropriate websocketpp error code. In the case that the Asio and + * Websocketpp error types are the same (such as using boost::asio and + * boost::system_error or using standalone asio and std::system_error the + * code will be passed through natively. + * + * In the case of a mismatch (boost::asio with std::system_error) a + * translated code will be returned. The plain socket policy does not have + * any additional information so all such errors will be reported as the + * generic transport pass_through error. + * + * @since 0.3.0 + * + * @param ec The error code to translate_ec + * @return The translated error code + */ + template + lib::error_code translate_ec(ErrorCodeType) { + // We don't know any more information about this error so pass through + return make_error_code(transport::error::pass_through); + } + + /// Overload of translate_ec to catch cases where lib::error_code is the + /// same type as lib::asio::error_code + lib::error_code translate_ec(lib::error_code ec) { + // We don't know any more information about this error, but the error is + // the same type as the one we are translating to, so pass through + // untranslated. + return ec; + } +private: + enum state { + UNINITIALIZED = 0, + READY = 1, + READING = 2 + }; + + socket_ptr m_socket; + state m_state; + + connection_hdl m_hdl; + socket_init_handler m_socket_init_handler; +}; + +/// Basic ASIO endpoint socket component +/** + * transport::asio::basic_socket::endpoint implements an endpoint socket + * component that uses Boost ASIO's ip::tcp::socket. + */ +class endpoint { +public: + /// The type of this endpoint socket component + typedef endpoint type; + + /// The type of the corresponding connection socket component + typedef connection socket_con_type; + /// The type of a shared pointer to the corresponding connection socket + /// component. + typedef socket_con_type::ptr socket_con_ptr; + + explicit endpoint() {} + + /// Checks whether the endpoint creates secure connections + /** + * @return Whether or not the endpoint creates secure connections + */ + bool is_secure() const { + return false; + } + + /// Set socket init handler + /** + * The socket init handler is called after a connection's socket is created + * but before it is used. This gives the end application an opportunity to + * set asio socket specific parameters. + * + * @param h The new socket_init_handler + */ + void set_socket_init_handler(socket_init_handler h) { + m_socket_init_handler = h; + } +protected: + /// Initialize a connection + /** + * Called by the transport after a new connection is created to initialize + * the socket component of the connection. + * + * @param scon Pointer to the socket component of the connection + * + * @return Error code (empty on success) + */ + lib::error_code init(socket_con_ptr scon) { + scon->set_socket_init_handler(m_socket_init_handler); + return lib::error_code(); + } +private: + socket_init_handler m_socket_init_handler; +}; + +} // namespace basic_socket +} // namespace asio +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp new file mode 100644 index 00000000..7b32db81 --- /dev/null +++ b/websocketpp/transport/asio/security/tls.hpp @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP +#define WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace asio { +/// A socket policy for the asio transport that implements a TLS encrypted +/// socket by wrapping with an asio::ssl::stream +namespace tls_socket { + +/// The signature of the socket_init_handler for this socket policy +typedef lib::function&)> socket_init_handler; +/// The signature of the tls_init_handler for this socket policy +typedef lib::function(connection_hdl)> + tls_init_handler; + +/// TLS enabled Asio connection socket component +/** + * transport::asio::tls_socket::connection implements a secure connection socket + * component that uses Asio's ssl::stream to wrap an ip::tcp::socket. + */ +class connection : public lib::enable_shared_from_this { +public: + /// Type of this connection socket component + typedef connection type; + /// Type of a shared pointer to this connection socket component + typedef lib::shared_ptr ptr; + + /// Type of the ASIO socket being used + typedef lib::asio::ssl::stream socket_type; + /// Type of a shared pointer to the ASIO socket being used + typedef lib::shared_ptr socket_ptr; + /// Type of a pointer to the ASIO io_service being used + typedef lib::asio::io_service * io_service_ptr; + /// Type of a pointer to the ASIO io_service strand being used + typedef lib::shared_ptr strand_ptr; + /// Type of a shared pointer to the ASIO TLS context being used + typedef lib::shared_ptr context_ptr; + + explicit connection() { + //std::cout << "transport::asio::tls_socket::connection constructor" + // << std::endl; + } + + /// Get a shared pointer to this component + ptr get_shared() { + return shared_from_this(); + } + + /// Check whether or not this connection is secure + /** + * @return Whether or not this connection is secure + */ + bool is_secure() const { + return true; + } + + /// Retrieve a pointer to the underlying socket + /** + * This is used internally. It can also be used to set socket options, etc + */ + socket_type::lowest_layer_type & get_raw_socket() { + return m_socket->lowest_layer(); + } + + /// Retrieve a pointer to the layer below the ssl stream + /** + * This is used internally. + */ + socket_type::next_layer_type & get_next_layer() { + return m_socket->next_layer(); + } + + /// Retrieve a pointer to the wrapped socket + /** + * This is used internally. + */ + socket_type & get_socket() { + return *m_socket; + } + + /// Set the socket initialization handler + /** + * The socket initialization handler is called after the socket object is + * created but before it is used. This gives the application a chance to + * set any ASIO socket options it needs. + * + * @param h The new socket_init_handler + */ + void set_socket_init_handler(socket_init_handler h) { + m_socket_init_handler = h; + } + + /// Set TLS init handler + /** + * The tls init handler is called when needed to request a TLS context for + * the library to use. A TLS init handler must be set and it must return a + * valid TLS context in order for this endpoint to be able to initialize + * TLS connections + * + * @param h The new tls_init_handler + */ + void set_tls_init_handler(tls_init_handler h) { + m_tls_init_handler = h; + } + + /// Get the remote endpoint address + /** + * The iostream transport has no information about the ultimate remote + * endpoint. It will return the string "iostream transport". To indicate + * this. + * + * TODO: allow user settable remote endpoint addresses if this seems useful + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint(lib::error_code & ec) const { + std::stringstream s; + + lib::asio::error_code aec; + lib::asio::ip::tcp::endpoint ep = m_socket->lowest_layer().remote_endpoint(aec); + + if (aec) { + ec = error::make_error_code(error::pass_through); + s << "Error getting remote endpoint: " << aec + << " (" << aec.message() << ")"; + return s.str(); + } else { + ec = lib::error_code(); + s << ep; + return s.str(); + } + } +protected: + /// Perform one time initializations + /** + * init_asio is called once immediately after construction to initialize + * Asio components to the io_service + * + * @param service A pointer to the endpoint's io_service + * @param strand A pointer to the connection's strand + * @param is_server Whether or not the endpoint is a server or not. + */ + lib::error_code init_asio (io_service_ptr service, strand_ptr strand, + bool is_server) + { + if (!m_tls_init_handler) { + return socket::make_error_code(socket::error::missing_tls_init_handler); + } + m_context = m_tls_init_handler(m_hdl); + + if (!m_context) { + return socket::make_error_code(socket::error::invalid_tls_context); + } + m_socket = lib::make_shared( + _WEBSOCKETPP_REF(*service),lib::ref(*m_context)); + + m_io_service = service; + m_strand = strand; + m_is_server = is_server; + + return lib::error_code(); + } + + /// Set hostname hook + /** + * Called by the transport as a connection is being established to provide + * the hostname being connected to to the security/socket layer. + * + * This socket policy uses the hostname to set the appropriate TLS SNI + * header. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr u) { + m_uri = u; + } + + /// Pre-initialize security policy + /** + * Called by the transport after a new connection is created to initialize + * the socket component of the connection. This method is not allowed to + * write any bytes to the wire. This initialization happens before any + * proxies or other intermediate wrappers are negotiated. + * + * @param callback Handler to call back with completion information + */ + void pre_init(init_handler callback) { + // TODO: is this the best way to check whether this function is + // available in the version of OpenSSL being used? + // TODO: consider case where host is an IP address +#if OPENSSL_VERSION_NUMBER >= 0x90812f + if (!m_is_server) { + // For clients on systems with a suitable OpenSSL version, set the + // TLS SNI hostname header so connecting to TLS servers using SNI + // will work. + long res = SSL_set_tlsext_host_name( + get_socket().native_handle(), m_uri->get_host().c_str()); + if (!(1 == res)) { + callback(socket::make_error_code(socket::error::tls_failed_sni_hostname)); + } + } +#endif + + if (m_socket_init_handler) { + m_socket_init_handler(m_hdl,get_socket()); + } + + callback(lib::error_code()); + } + + /// Post-initialize security policy + /** + * Called by the transport after all intermediate proxies have been + * negotiated. This gives the security policy the chance to talk with the + * real remote endpoint for a bit before the websocket handshake. + * + * @param callback Handler to call back with completion information + */ + void post_init(init_handler callback) { + m_ec = socket::make_error_code(socket::error::tls_handshake_timeout); + + // TLS handshake + if (m_strand) { + m_socket->async_handshake( + get_handshake_type(), + m_strand->wrap(lib::bind( + &type::handle_init, get_shared(), + callback, + lib::placeholders::_1 + )) + ); + } else { + m_socket->async_handshake( + get_handshake_type(), + lib::bind( + &type::handle_init, get_shared(), + callback, + lib::placeholders::_1 + ) + ); + } + } + + /// Sets the connection handle + /** + * The connection handle is passed to any handlers to identify the + * connection + * + * @param hdl The new handle + */ + void set_handle(connection_hdl hdl) { + m_hdl = hdl; + } + + void handle_init(init_handler callback,lib::asio::error_code const & ec) { + if (ec) { + m_ec = socket::make_error_code(socket::error::tls_handshake_failed); + } else { + m_ec = lib::error_code(); + } + + callback(m_ec); + } + + lib::error_code get_ec() const { + return m_ec; + } + + /// Cancel all async operations on this socket + /** + * Attempts to cancel all async operations on this socket and reports any + * failures. + * + * NOTE: Windows XP and earlier do not support socket cancellation. + * + * @return The error that occurred, if any. + */ + lib::asio::error_code cancel_socket() { + lib::asio::error_code ec; + get_raw_socket().cancel(ec); + return ec; + } + + void async_shutdown(socket::shutdown_handler callback) { + if (m_strand) { + m_socket->async_shutdown(m_strand->wrap(callback)); + } else { + m_socket->async_shutdown(callback); + } + } + + /// Translate any security policy specific information about an error code + /** + * Translate_ec takes an Asio error code and attempts to convert its value + * to an appropriate websocketpp error code. In the case that the Asio and + * Websocketpp error types are the same (such as using boost::asio and + * boost::system_error or using standalone asio and std::system_error the + * code will be passed through natively. + * + * In the case of a mismatch (boost::asio with std::system_error) a + * translated code will be returned. Any error that is determined to be + * related to TLS but does not have a more specific websocketpp error code + * is returned under the catch all error `tls_error`. Non-TLS related errors + * are returned as the transport generic error `pass_through` + * + * @since 0.3.0 + * + * @param ec The error code to translate_ec + * @return The translated error code + */ + template + lib::error_code translate_ec(ErrorCodeType ec) { + if (ec.category() == lib::asio::error::get_ssl_category()) { + if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) { + return make_error_code(transport::error::tls_short_read); + } else { + // We know it is a TLS related error, but otherwise don't know + // more. Pass through as TLS generic. + return make_error_code(transport::error::tls_error); + } + } else { + // We don't know any more information about this error so pass + // through + return make_error_code(transport::error::pass_through); + } + } + + /// Overload of translate_ec to catch cases where lib::error_code is the + /// same type as lib::asio::error_code + lib::error_code translate_ec(lib::error_code ec) { + // Normalize the tls_short_read error as it is used by the library and + // needs a consistent value. All other errors pass through natively. + // TODO: how to get the SSL category from std::error? + /*if (ec.category() == lib::asio::error::get_ssl_category()) { + if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) { + return make_error_code(transport::error::tls_short_read); + } + }*/ + return ec; + } +private: + socket_type::handshake_type get_handshake_type() { + if (m_is_server) { + return lib::asio::ssl::stream_base::server; + } else { + return lib::asio::ssl::stream_base::client; + } + } + + io_service_ptr m_io_service; + strand_ptr m_strand; + context_ptr m_context; + socket_ptr m_socket; + uri_ptr m_uri; + bool m_is_server; + + lib::error_code m_ec; + + connection_hdl m_hdl; + socket_init_handler m_socket_init_handler; + tls_init_handler m_tls_init_handler; +}; + +/// TLS enabled Asio endpoint socket component +/** + * transport::asio::tls_socket::endpoint implements a secure endpoint socket + * component that uses Asio's ssl::stream to wrap an ip::tcp::socket. + */ +class endpoint { +public: + /// The type of this endpoint socket component + typedef endpoint type; + + /// The type of the corresponding connection socket component + typedef connection socket_con_type; + /// The type of a shared pointer to the corresponding connection socket + /// component. + typedef socket_con_type::ptr socket_con_ptr; + + explicit endpoint() {} + + /// Checks whether the endpoint creates secure connections + /** + * @return Whether or not the endpoint creates secure connections + */ + bool is_secure() const { + return true; + } + + /// Set socket init handler + /** + * The socket init handler is called after a connection's socket is created + * but before it is used. This gives the end application an opportunity to + * set asio socket specific parameters. + * + * @param h The new socket_init_handler + */ + void set_socket_init_handler(socket_init_handler h) { + m_socket_init_handler = h; + } + + /// Set TLS init handler + /** + * The tls init handler is called when needed to request a TLS context for + * the library to use. A TLS init handler must be set and it must return a + * valid TLS context in order for this endpoint to be able to initialize + * TLS connections + * + * @param h The new tls_init_handler + */ + void set_tls_init_handler(tls_init_handler h) { + m_tls_init_handler = h; + } +protected: + /// Initialize a connection + /** + * Called by the transport after a new connection is created to initialize + * the socket component of the connection. + * + * @param scon Pointer to the socket component of the connection + * + * @return Error code (empty on success) + */ + lib::error_code init(socket_con_ptr scon) { + scon->set_socket_init_handler(m_socket_init_handler); + scon->set_tls_init_handler(m_tls_init_handler); + return lib::error_code(); + } + +private: + socket_init_handler m_socket_init_handler; + tls_init_handler m_tls_init_handler; +}; + +} // namespace tls_socket +} // namespace asio +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP diff --git a/websocketpp/transport/base/connection.hpp b/websocketpp/transport/base/connection.hpp new file mode 100644 index 00000000..f76d4091 --- /dev/null +++ b/websocketpp/transport/base/connection.hpp @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_BASE_CON_HPP +#define WEBSOCKETPP_TRANSPORT_BASE_CON_HPP + +#include +#include +#include +#include + +#include + +namespace websocketpp { +/// Transport policies provide network connectivity and timers +/** + * ### Connection Interface + * + * Transport connection components needs to provide: + * + * **init**\n + * `void init(init_handler handler)`\n + * Called once shortly after construction to give the policy the chance to + * perform one time initialization. When complete, the policy must call the + * supplied `init_handler` to continue setup. The handler takes one argument + * with the error code if any. If an error is returned here setup will fail and + * the connection will be aborted or terminated. + * + * WebSocket++ will call init only once. The transport must call `handler` + * exactly once. + * + * **async_read_at_least**\n + * `void async_read_at_least(size_t num_bytes, char *buf, size_t len, + * read_handler handler)`\n + * start an async read for at least num_bytes and at most len + * bytes into buf. Call handler when done with number of bytes read. + * + * WebSocket++ promises to have only one async_read_at_least in flight at a + * time. The transport must promise to only call read_handler once per async + * read. + * + * **async_write**\n + * `void async_write(const char* buf, size_t len, write_handler handler)`\n + * `void async_write(std::vector & bufs, write_handler handler)`\n + * Start a write of all of the data in buf or bufs. In second case data is + * written sequentially and in place without copying anything to a temporary + * location. + * + * Websocket++ promises to have only one async_write in flight at a time. + * The transport must promise to only call the write_handler once per async + * write + * + * **set_handle**\n + * `void set_handle(connection_hdl hdl)`\n + * Called by WebSocket++ to let this policy know the hdl to the connection. It + * may be stored for later use or ignored/discarded. This handle should be used + * if the policy adds any connection handlers. Connection handlers must be + * called with the handle as the first argument so that the handler code knows + * which connection generated the callback. + * + * **set_timer**\n + * `timer_ptr set_timer(long duration, timer_handler handler)`\n + * WebSocket++ uses the timers provided by the transport policy as the + * implementation of timers is often highly coupled with the implementation of + * the networking event loops. + * + * Transport timer support is an optional feature. A transport method may elect + * to implement a dummy timer object and have this method return an empty + * pointer. If so, all timer related features of WebSocket++ core will be + * disabled. This includes many security features designed to prevent denial of + * service attacks. Use timer-free transport policies with caution. + * + * **get_remote_endpoint**\n + * `std::string get_remote_endpoint()`\n + * retrieve address of remote endpoint + * + * **is_secure**\n + * `void is_secure()`\n + * whether or not the connection to the remote endpoint is secure + * + * **dispatch**\n + * `lib::error_code dispatch(dispatch_handler handler)`: invoke handler within + * the transport's event system if it uses one. Otherwise, this method should + * simply call `handler` immediately. + * + * **async_shutdown**\n + * `void async_shutdown(shutdown_handler handler)`\n + * Perform any cleanup necessary (if any). Call `handler` when complete. + */ +namespace transport { + +/// The type and signature of the callback passed to the init hook +typedef lib::function init_handler; + +/// The type and signature of the callback passed to the read method +typedef lib::function read_handler; + +/// The type and signature of the callback passed to the write method +typedef lib::function write_handler; + +/// The type and signature of the callback passed to the read method +typedef lib::function timer_handler; + +/// The type and signature of the callback passed to the shutdown method +typedef lib::function shutdown_handler; + +/// The type and signature of the callback passed to the interrupt method +typedef lib::function interrupt_handler; + +/// The type and signature of the callback passed to the dispatch method +typedef lib::function dispatch_handler; + +/// A simple utility buffer class +struct buffer { + buffer(char const * b, size_t l) : buf(b),len(l) {} + + char const * buf; + size_t len; +}; + +/// Generic transport related errors +namespace error { +enum value { + /// Catch-all error for transport policy errors that don't fit in other + /// categories + general = 1, + + /// underlying transport pass through + pass_through, + + /// async_read_at_least call requested more bytes than buffer can store + invalid_num_bytes, + + /// async_read called while another async_read was in progress + double_read, + + /// Operation aborted + operation_aborted, + + /// Operation not supported + operation_not_supported, + + /// End of file + eof, + + /// TLS short read + tls_short_read, + + /// Timer expired + timeout, + + /// read or write after shutdown + action_after_shutdown, + + /// Other TLS error + tls_error +}; + +class category : public lib::error_category { + public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic transport policy error"; + case pass_through: + return "Underlying Transport Error"; + case invalid_num_bytes: + return "async_read_at_least call requested more bytes than buffer can store"; + case operation_aborted: + return "The operation was aborted"; + case operation_not_supported: + return "The operation is not supported by this transport"; + case eof: + return "End of File"; + case tls_short_read: + return "TLS Short Read"; + case timeout: + return "Timer Expired"; + case action_after_shutdown: + return "A transport action was requested after shutdown"; + case tls_error: + return "Generic TLS related error"; + default: + return "Unknown"; + } + } +}; + +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace transport +} // namespace websocketpp +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif // WEBSOCKETPP_TRANSPORT_BASE_CON_HPP diff --git a/websocketpp/transport/base/endpoint.hpp b/websocketpp/transport/base/endpoint.hpp new file mode 100644 index 00000000..3b4b0d6d --- /dev/null +++ b/websocketpp/transport/base/endpoint.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_BASE_HPP + +#include +#include + +namespace websocketpp { +/// Transport policies provide network connectivity and timers +/** + * ### Endpoint Interface + * + * Transport endpoint components needs to provide: + * + * **init**\n + * `lib::error_code init(transport_con_ptr tcon)`\n + * init is called by an endpoint once for each newly created connection. + * It's purpose is to give the transport policy the chance to perform any + * transport specific initialization that couldn't be done via the default + * constructor. + * + * **is_secure**\n + * `bool is_secure() const`\n + * Test whether the transport component of this endpoint is capable of secure + * connections. + * + * **async_connect**\n + * `void async_connect(transport_con_ptr tcon, uri_ptr location, + * connect_handler handler)`\n + * Initiate a connection to `location` using the given connection `tcon`. `tcon` + * is a pointer to the transport connection component of the connection. When + * complete, `handler` should be called with the the connection's + * `connection_hdl` and any error that occurred. + * + * **init_logging** + * `void init_logging(alog_type * a, elog_type * e)`\n + * Called once after construction to provide pointers to the endpoint's access + * and error loggers. These may be stored and used to log messages or ignored. + */ +namespace transport { + +/// The type and signature of the callback passed to the accept method +typedef lib::function accept_handler; + +/// The type and signature of the callback passed to the connect method +typedef lib::function connect_handler; + +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_BASE_HPP diff --git a/websocketpp/transport/debug/base.hpp b/websocketpp/transport/debug/base.hpp new file mode 100644 index 00000000..2e477b50 --- /dev/null +++ b/websocketpp/transport/debug/base.hpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP + +#include +#include + +#include + +namespace websocketpp { +namespace transport { +/// Debug transport policy that is used for various mocking and stubbing duties +/// in unit tests. +namespace debug { + +/// debug transport errors +namespace error { +enum value { + /// Catch-all error for transport policy errors that don't fit in other + /// categories + general = 1, + + /// not implemented + not_implemented, + + invalid_num_bytes, + + double_read +}; + +/// debug transport error category +class category : public lib::error_category { + public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport.debug"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic stub transport policy error"; + case not_implemented: + return "feature not implemented"; + case invalid_num_bytes: + return "Invalid number of bytes"; + case double_read: + return "Read while another read was outstanding"; + default: + return "Unknown"; + } + } +}; + +/// Get a reference to a static copy of the debug transport error category +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +/// Get an error code with the given value and the debug transport category +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace debug +} // namespace transport +} // namespace websocketpp +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif // WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP diff --git a/websocketpp/transport/debug/connection.hpp b/websocketpp/transport/debug/connection.hpp new file mode 100644 index 00000000..36b282a2 --- /dev/null +++ b/websocketpp/transport/debug/connection.hpp @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP +#define WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace debug { + +/// Empty timer class to stub out for timer functionality that stub +/// transport doesn't support +struct timer { + void cancel() {} +}; + +template +class connection : public lib::enable_shared_from_this< connection > { +public: + /// Type of this connection transport component + typedef connection type; + /// Type of a shared pointer to this connection transport component + typedef lib::shared_ptr ptr; + + /// transport concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of this transport's access logging policy + typedef typename config::alog_type alog_type; + /// Type of this transport's error logging policy + typedef typename config::elog_type elog_type; + + // Concurrency policy types + typedef typename concurrency_type::scoped_lock_type scoped_lock_type; + typedef typename concurrency_type::mutex_type mutex_type; + + typedef lib::shared_ptr timer_ptr; + + explicit connection(bool is_server, alog_type & alog, elog_type & elog) + : m_reading(false), m_is_server(is_server), m_alog(alog), m_elog(elog) + { + m_alog.write(log::alevel::devel,"debug con transport constructor"); + } + + /// Get a shared pointer to this component + ptr get_shared() { + return type::shared_from_this(); + } + + /// Set whether or not this connection is secure + /** + * Todo: docs + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not this connection is secure. + */ + void set_secure(bool) {} + + /// Tests whether or not the underlying transport is secure + /** + * TODO: docs + * + * @return Whether or not the underlying transport is secure + */ + bool is_secure() const { + return false; + } + + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * Implementation is optional and can be ignored if the transport has no + * need for this information. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + + /// Set human readable remote endpoint address + /** + * Sets the remote endpoint address returned by `get_remote_endpoint`. This + * value should be a human readable string that describes the remote + * endpoint. Typically an IP address or hostname, perhaps with a port. But + * may be something else depending on the nature of the underlying + * transport. + * + * If none is set a default is returned. + * + * @since 0.3.0-alpha4 + * + * @param value The remote endpoint address to set. + */ + void set_remote_endpoint(std::string) {} + + /// Get human readable remote endpoint address + /** + * TODO: docs + * + * This value is used in access and error logs and is available to the end + * application for including in user facing interfaces and messages. + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint() const { + return "unknown (debug transport)"; + } + + /// Get the connection handle + /** + * @return The handle for this connection. + */ + connection_hdl get_handle() const { + return connection_hdl(); + } + + /// Call back a function after a period of time. + /** + * Timers are not implemented in this transport. The timer pointer will + * always be empty. The handler will never be called. + * + * @param duration Length of time to wait in milliseconds + * @param callback The function to call back when the timer has expired + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long, timer_handler handler) { + m_alog.write(log::alevel::devel,"debug connection set timer"); + m_timer_handler = handler; + return timer_ptr(); + } + + /// Manual input supply (read all) + /** + * Similar to read_some, but continues to read until all bytes in the + * supplied buffer have been read or the connection runs out of read + * requests. + * + * This method still may not read all of the bytes in the input buffer. if + * it doesn't it indicates that the connection was most likely closed or + * is in an error state where it is no longer accepting new input. + * + * @since 0.3.0 + * + * @param buf Char buffer to read into the websocket + * @param len Length of buf + * @return The number of characters from buf actually read. + */ + size_t read_all(char const * buf, size_t len) { + size_t total_read = 0; + size_t temp_read = 0; + + do { + temp_read = this->read_some_impl(buf+total_read,len-total_read); + total_read += temp_read; + } while (temp_read != 0 && total_read < len); + + return total_read; + } + + // debug stuff to invoke the async handlers + void expire_timer(lib::error_code const & ec) { + m_timer_handler(ec); + } + + void fullfil_write() { + m_write_handler(lib::error_code()); + } +protected: + /// Initialize the connection transport + /** + * Initialize the connection's transport component. + * + * @param handler The `init_handler` to call when initialization is done + */ + void init(init_handler handler) { + m_alog.write(log::alevel::devel,"debug connection init"); + handler(lib::error_code()); + } + + /// Initiate an async_read for at least num_bytes bytes into buf + /** + * Initiates an async_read request for at least num_bytes bytes. The input + * will be read into buf. A maximum of len bytes will be input. When the + * operation is complete, handler will be called with the status and number + * of bytes read. + * + * This method may or may not call handler from within the initial call. The + * application should be prepared to accept either. + * + * The application should never call this method a second time before it has + * been called back for the first read. If this is done, the second read + * will be called back immediately with a double_read error. + * + * If num_bytes or len are zero handler will be called back immediately + * indicating success. + * + * @param num_bytes Don't call handler until at least this many bytes have + * been read. + * @param buf The buffer to read bytes into + * @param len The size of buf. At maximum, this many bytes will be read. + * @param handler The callback to invoke when the operation is complete or + * ends in an error + */ + void async_read_at_least(size_t num_bytes, char * buf, size_t len, + read_handler handler) + { + std::stringstream s; + s << "debug_con async_read_at_least: " << num_bytes; + m_alog.write(log::alevel::devel,s.str()); + + if (num_bytes > len) { + handler(make_error_code(error::invalid_num_bytes),size_t(0)); + return; + } + + if (m_reading == true) { + handler(make_error_code(error::double_read),size_t(0)); + return; + } + + if (num_bytes == 0 || len == 0) { + handler(lib::error_code(),size_t(0)); + return; + } + + m_buf = buf; + m_len = len; + m_bytes_needed = num_bytes; + m_read_handler = handler; + m_cursor = 0; + m_reading = true; + } + + /// Asyncronous Transport Write + /** + * Write len bytes in buf to the output stream. Call handler to report + * success or failure. handler may or may not be called during async_write, + * but it must be safe for this to happen. + * + * Will return 0 on success. + * + * @param buf buffer to read bytes from + * @param len number of bytes to write + * @param handler Callback to invoke with operation status. + */ + void async_write(char const *, size_t, write_handler handler) { + m_alog.write(log::alevel::devel,"debug_con async_write"); + m_write_handler = handler; + } + + /// Asyncronous Transport Write (scatter-gather) + /** + * Write a sequence of buffers to the output stream. Call handler to report + * success or failure. handler may or may not be called during async_write, + * but it must be safe for this to happen. + * + * Will return 0 on success. + * + * @param bufs vector of buffers to write + * @param handler Callback to invoke with operation status. + */ + void async_write(std::vector const &, write_handler handler) { + m_alog.write(log::alevel::devel,"debug_con async_write buffer list"); + m_write_handler = handler; + } + + /// Set Connection Handle + /** + * @param hdl The new handle + */ + void set_handle(connection_hdl) {} + + /// Call given handler back within the transport's event system (if present) + /** + * Invoke a callback within the transport's event system if it has one. If + * it doesn't, the handler will be invoked immediately before this function + * returns. + * + * @param handler The callback to invoke + * + * @return Whether or not the transport was able to register the handler for + * callback. + */ + lib::error_code dispatch(dispatch_handler handler) { + handler(); + return lib::error_code(); + } + + /// Perform cleanup on socket shutdown_handler + /** + * @param h The `shutdown_handler` to call back when complete + */ + void async_shutdown(shutdown_handler handler) { + handler(lib::error_code()); + } + + size_t read_some_impl(char const * buf, size_t len) { + m_alog.write(log::alevel::devel,"debug_con read_some"); + + if (!m_reading) { + m_elog.write(log::elevel::devel,"write while not reading"); + return 0; + } + + size_t bytes_to_copy = (std::min)(len,m_len-m_cursor); + + std::copy(buf,buf+bytes_to_copy,m_buf+m_cursor); + + m_cursor += bytes_to_copy; + + if (m_cursor >= m_bytes_needed) { + complete_read(lib::error_code()); + } + + return bytes_to_copy; + } + + /// Signal that a requested read is complete + /** + * Sets the reading flag to false and returns the handler that should be + * called back with the result of the read. The cursor position that is sent + * is whatever the value of m_cursor is. + * + * It MUST NOT be called when m_reading is false. + * it MUST be called while holding the read lock + * + * It is important to use this method rather than directly setting/calling + * m_read_handler back because this function makes sure to delete the + * locally stored handler which contains shared pointers that will otherwise + * cause circular reference based memory leaks. + * + * @param ec The error code to forward to the read handler + */ + void complete_read(lib::error_code const & ec) { + m_reading = false; + + read_handler handler = m_read_handler; + m_read_handler = read_handler(); + + handler(ec,m_cursor); + } +private: + timer_handler m_timer_handler; + + // Read space (Protected by m_read_mutex) + char * m_buf; + size_t m_len; + size_t m_bytes_needed; + read_handler m_read_handler; + size_t m_cursor; + + // transport resources + connection_hdl m_connection_hdl; + write_handler m_write_handler; + shutdown_handler m_shutdown_handler; + + bool m_reading; + bool const m_is_server; + bool m_is_secure; + alog_type & m_alog; + elog_type & m_elog; + std::string m_remote_endpoint; +}; + + +} // namespace debug +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP diff --git a/websocketpp/transport/debug/endpoint.hpp b/websocketpp/transport/debug/endpoint.hpp new file mode 100644 index 00000000..1cca70c5 --- /dev/null +++ b/websocketpp/transport/debug/endpoint.hpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_DEBUG_HPP +#define WEBSOCKETPP_TRANSPORT_DEBUG_HPP + +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace debug { + +template +class endpoint { +public: + /// Type of this endpoint transport component + typedef endpoint type; + /// Type of a pointer to this endpoint transport component + typedef lib::shared_ptr ptr; + + /// Type of this endpoint's concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of this endpoint's error logging policy + typedef typename config::elog_type elog_type; + /// Type of this endpoint's access logging policy + typedef typename config::alog_type alog_type; + + /// Type of this endpoint transport component's associated connection + /// transport component. + typedef debug::connection transport_con_type; + /// Type of a shared pointer to this endpoint transport component's + /// associated connection transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + // generate and manage our own io_service + explicit endpoint() + { + //std::cout << "transport::iostream::endpoint constructor" << std::endl; + } + + /// Set whether or not endpoint can create secure connections + /** + * TODO: docs + * + * Setting this value only indicates whether or not the endpoint is capable + * of producing and managing secure connections. Connections produced by + * this endpoint must also be individually flagged as secure if they are. + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not the endpoint can create secure connections. + */ + void set_secure(bool) {} + + /// Tests whether or not the underlying transport is secure + /** + * TODO: docs + * + * @return Whether or not the underlying transport is secure + */ + bool is_secure() const { + return false; + } +protected: + /// Initialize logging + /** + * The loggers are located in the main endpoint class. As such, the + * transport doesn't have direct access to them. This method is called + * by the endpoint constructor to allow shared logging from the transport + * component. These are raw pointers to member variables of the endpoint. + * In particular, they cannot be used in the transport constructor as they + * haven't been constructed yet, and cannot be used in the transport + * destructor as they will have been destroyed by then. + * + * @param a A pointer to the access logger to use. + * @param e A pointer to the error logger to use. + */ + void init_logging(alog_type *, elog_type *) {} + + /// Initiate a new connection + /** + * @param tcon A pointer to the transport connection component of the + * connection to connect. + * @param u A URI pointer to the URI to connect to. + * @param cb The function to call back with the results when complete. + */ + void async_connect(transport_con_ptr, uri_ptr, connect_handler cb) { + cb(lib::error_code()); + } + + /// Initialize a connection + /** + * Init is called by an endpoint once for each newly created connection. + * It's purpose is to give the transport policy the chance to perform any + * transport specific initialization that couldn't be done via the default + * constructor. + * + * @param tcon A pointer to the transport portion of the connection. + * @return A status code indicating the success or failure of the operation + */ + lib::error_code init(transport_con_ptr) { + return lib::error_code(); + } +private: + +}; + +} // namespace debug +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_DEBUG_HPP diff --git a/websocketpp/transport/iostream/base.hpp b/websocketpp/transport/iostream/base.hpp new file mode 100644 index 00000000..f8783987 --- /dev/null +++ b/websocketpp/transport/iostream/base.hpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP + +#include +#include +#include +#include + +#include + +#include +#include + +namespace websocketpp { +namespace transport { +/// Transport policy that uses STL iostream for I/O and does not support timers +namespace iostream { + +/// The type and signature of the callback used by iostream transport to write +typedef lib::function + write_handler; + +/// The type and signature of the callback used by iostream transport to perform +/// vectored writes. +/** + * If a vectored write handler is not set the standard write handler will be + * called multiple times. + */ +typedef lib::function const + & bufs)> vector_write_handler; + +/// The type and signature of the callback used by iostream transport to signal +/// a transport shutdown. +typedef lib::function shutdown_handler; + +/// iostream transport errors +namespace error { +enum value { + /// Catch-all error for transport policy errors that don't fit in other + /// categories + general = 1, + + /// async_read_at_least call requested more bytes than buffer can store + invalid_num_bytes, + + /// async_read called while another async_read was in progress + double_read, + + /// An operation that requires an output stream was attempted before + /// setting one. + output_stream_required, + + /// stream error + bad_stream +}; + +/// iostream transport error category +class category : public lib::error_category { + public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport.iostream"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic iostream transport policy error"; + case invalid_num_bytes: + return "async_read_at_least call requested more bytes than buffer can store"; + case double_read: + return "Async read already in progress"; + case output_stream_required: + return "An output stream to be set before async_write can be used"; + case bad_stream: + return "A stream operation returned ios::bad"; + default: + return "Unknown"; + } + } +}; + +/// Get a reference to a static copy of the iostream transport error category +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +/// Get an error code with the given value and the iostream transport category +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace iostream +} // namespace transport +} // namespace websocketpp +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP diff --git a/websocketpp/transport/iostream/connection.hpp b/websocketpp/transport/iostream/connection.hpp new file mode 100644 index 00000000..81c4f411 --- /dev/null +++ b/websocketpp/transport/iostream/connection.hpp @@ -0,0 +1,714 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP +#define WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP + +#include + +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace websocketpp { +namespace transport { +namespace iostream { + +/// Empty timer class to stub out for timer functionality that iostream +/// transport doesn't support +struct timer { + void cancel() {} +}; + +template +class connection : public lib::enable_shared_from_this< connection > { +public: + /// Type of this connection transport component + typedef connection type; + /// Type of a shared pointer to this connection transport component + typedef lib::shared_ptr ptr; + + /// transport concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of this transport's access logging policy + typedef typename config::alog_type alog_type; + /// Type of this transport's error logging policy + typedef typename config::elog_type elog_type; + + // Concurrency policy types + typedef typename concurrency_type::scoped_lock_type scoped_lock_type; + typedef typename concurrency_type::mutex_type mutex_type; + + typedef lib::shared_ptr timer_ptr; + + explicit connection(bool is_server, alog_type & alog, elog_type & elog) + : m_output_stream(NULL) + , m_reading(false) + , m_is_server(is_server) + , m_is_secure(false) + , m_alog(alog) + , m_elog(elog) + , m_remote_endpoint("iostream transport") + { + m_alog.write(log::alevel::devel,"iostream con transport constructor"); + } + + /// Get a shared pointer to this component + ptr get_shared() { + return type::shared_from_this(); + } + + /// Register a std::ostream with the transport for writing output + /** + * Register a std::ostream with the transport. All future writes will be + * done to this output stream. + * + * @param o A pointer to the ostream to use for output. + */ + void register_ostream(std::ostream * o) { + // TODO: lock transport state? + scoped_lock_type lock(m_read_mutex); + m_output_stream = o; + } + + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * This transport policy doesn't use the uri so it is ignored. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + + /// Overloaded stream input operator + /** + * Attempts to read input from the given stream into the transport. Bytes + * will be extracted from the input stream to fulfill any pending reads. + * Input in this manner will only read until the current read buffer has + * been filled. Then it will signal the library to process the input. If the + * library's input handler adds a new async_read, additional bytes will be + * read, otherwise the input operation will end. + * + * When this function returns one of the following conditions is true: + * - There is no outstanding read operation + * - There are no more bytes available in the input stream + * + * You can use tellg() on the input stream to determine if all of the input + * bytes were read or not. + * + * If there is no pending read operation when the input method is called, it + * will return immediately and tellg() will not have changed. + */ + friend std::istream & operator>> (std::istream & in, type & t) { + // this serializes calls to external read. + scoped_lock_type lock(t.m_read_mutex); + + t.read(in); + + return in; + } + + /// Manual input supply (read some) + /** + * Copies bytes from buf into WebSocket++'s input buffers. Bytes will be + * copied from the supplied buffer to fulfill any pending library reads. It + * will return the number of bytes successfully processed. If there are no + * pending reads read_some will return immediately. Not all of the bytes may + * be able to be read in one call. + * + * @since 0.3.0-alpha4 + * + * @param buf Char buffer to read into the websocket + * @param len Length of buf + * @return The number of characters from buf actually read. + */ + size_t read_some(char const * buf, size_t len) { + // this serializes calls to external read. + scoped_lock_type lock(m_read_mutex); + + return this->read_some_impl(buf,len); + } + + /// Manual input supply (read all) + /** + * Similar to read_some, but continues to read until all bytes in the + * supplied buffer have been read or the connection runs out of read + * requests. + * + * This method still may not read all of the bytes in the input buffer. if + * it doesn't it indicates that the connection was most likely closed or + * is in an error state where it is no longer accepting new input. + * + * @since 0.3.0 + * + * @param buf Char buffer to read into the websocket + * @param len Length of buf + * @return The number of characters from buf actually read. + */ + size_t read_all(char const * buf, size_t len) { + // this serializes calls to external read. + scoped_lock_type lock(m_read_mutex); + + size_t total_read = 0; + size_t temp_read = 0; + + do { + temp_read = this->read_some_impl(buf+total_read,len-total_read); + total_read += temp_read; + } while (temp_read != 0 && total_read < len); + + return total_read; + } + + /// Manual input supply (DEPRECATED) + /** + * @deprecated DEPRECATED in favor of read_some() + * @see read_some() + */ + size_t readsome(char const * buf, size_t len) { + return this->read_some(buf,len); + } + + /// Signal EOF + /** + * Signals to the transport that data stream being read has reached EOF and + * that no more bytes may be read or written to/from the transport. + * + * @since 0.3.0-alpha4 + */ + void eof() { + // this serializes calls to external read. + scoped_lock_type lock(m_read_mutex); + + if (m_reading) { + complete_read(make_error_code(transport::error::eof)); + } + } + + /// Signal transport error + /** + * Signals to the transport that a fatal data stream error has occurred and + * that no more bytes may be read or written to/from the transport. + * + * @since 0.3.0-alpha4 + */ + void fatal_error() { + // this serializes calls to external read. + scoped_lock_type lock(m_read_mutex); + + if (m_reading) { + complete_read(make_error_code(transport::error::pass_through)); + } + } + + /// Set whether or not this connection is secure + /** + * The iostream transport does not provide any security features. As such + * it defaults to returning false when `is_secure` is called. However, the + * iostream transport may be used to wrap an external socket API that may + * provide secure transport. This method allows that external API to flag + * whether or not this connection is secure so that users of the WebSocket++ + * API will get more accurate information. + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not this connection is secure. + */ + void set_secure(bool value) { + m_is_secure = value; + } + + /// Tests whether or not the underlying transport is secure + /** + * iostream transport will return false always because it has no information + * about the ultimate remote endpoint. This may or may not be accurate + * depending on the real source of bytes being input. The `set_secure` + * method may be used to flag connections that are secured by an external + * API + * + * @return Whether or not the underlying transport is secure + */ + bool is_secure() const { + return m_is_secure; + } + + /// Set human readable remote endpoint address + /** + * Sets the remote endpoint address returned by `get_remote_endpoint`. This + * value should be a human readable string that describes the remote + * endpoint. Typically an IP address or hostname, perhaps with a port. But + * may be something else depending on the nature of the underlying + * transport. + * + * If none is set the default is "iostream transport". + * + * @since 0.3.0-alpha4 + * + * @param value The remote endpoint address to set. + */ + void set_remote_endpoint(std::string value) { + m_remote_endpoint = value; + } + + /// Get human readable remote endpoint address + /** + * The iostream transport has no information about the ultimate remote + * endpoint. It will return the string "iostream transport". The + * `set_remote_endpoint` method may be used by external network code to set + * a more accurate value. + * + * This value is used in access and error logs and is available to the end + * application for including in user facing interfaces and messages. + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint() const { + return m_remote_endpoint; + } + + /// Get the connection handle + /** + * @return The handle for this connection. + */ + connection_hdl get_handle() const { + return m_connection_hdl; + } + + /// Call back a function after a period of time. + /** + * Timers are not implemented in this transport. The timer pointer will + * always be empty. The handler will never be called. + * + * @param duration Length of time to wait in milliseconds + * @param callback The function to call back when the timer has expired + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long, timer_handler) { + return timer_ptr(); + } + + /// Sets the write handler + /** + * The write handler is called when the iostream transport receives data + * that needs to be written to the appropriate output location. This handler + * can be used in place of registering an ostream for output. + * + * The signature of the handler is + * `lib::error_code (connection_hdl, char const *, size_t)` The + * code returned will be reported and logged by the core library. + * + * See also, set_vector_write_handler, for an optional write handler that + * allows more efficient handling of multiple writes at once. + * + * @see set_vector_write_handler + * + * @since 0.5.0 + * + * @param h The handler to call when data is to be written. + */ + void set_write_handler(write_handler h) { + m_write_handler = h; + } + + /// Sets the vectored write handler + /** + * The vectored write handler is called when the iostream transport receives + * multiple chunks of data that need to be written to the appropriate output + * location. This handler can be used in conjunction with the write_handler + * in place of registering an ostream for output. + * + * The sequence of buffers represents bytes that should be written + * consecutively and it is suggested to group the buffers into as few next + * layer packets as possible. Vector write is used to allow implementations + * that support it to coalesce writes into a single TCP packet or TLS + * segment for improved efficiency. + * + * This is an optional handler. If it is not defined then multiple calls + * will be made to the standard write handler. + * + * The signature of the handler is + * `lib::error_code (connection_hdl, std::vector + * const & bufs)`. The code returned will be reported and logged by the core + * library. The `websocketpp::transport::buffer` type is a struct with two + * data members. buf (char const *) and len (size_t). + * + * @since 0.6.0 + * + * @param h The handler to call when vectored data is to be written. + */ + void set_vector_write_handler(vector_write_handler h) { + m_vector_write_handler = h; + } + + /// Sets the shutdown handler + /** + * The shutdown handler is called when the iostream transport receives a + * notification from the core library that it is finished with all read and + * write operations and that the underlying transport can be cleaned up. + * + * If you are using iostream transport with another socket library, this is + * a good time to close/shutdown the socket for this connection. + * + * The signature of the handler is `lib::error_code (connection_hdl)`. The + * code returned will be reported and logged by the core library. + * + * @since 0.5.0 + * + * @param h The handler to call on connection shutdown. + */ + void set_shutdown_handler(shutdown_handler h) { + m_shutdown_handler = h; + } +protected: + /// Initialize the connection transport + /** + * Initialize the connection's transport component. + * + * @param handler The `init_handler` to call when initialization is done + */ + void init(init_handler handler) { + m_alog.write(log::alevel::devel,"iostream connection init"); + handler(lib::error_code()); + } + + /// Initiate an async_read for at least num_bytes bytes into buf + /** + * Initiates an async_read request for at least num_bytes bytes. The input + * will be read into buf. A maximum of len bytes will be input. When the + * operation is complete, handler will be called with the status and number + * of bytes read. + * + * This method may or may not call handler from within the initial call. The + * application should be prepared to accept either. + * + * The application should never call this method a second time before it has + * been called back for the first read. If this is done, the second read + * will be called back immediately with a double_read error. + * + * If num_bytes or len are zero handler will be called back immediately + * indicating success. + * + * @param num_bytes Don't call handler until at least this many bytes have + * been read. + * @param buf The buffer to read bytes into + * @param len The size of buf. At maximum, this many bytes will be read. + * @param handler The callback to invoke when the operation is complete or + * ends in an error + */ + void async_read_at_least(size_t num_bytes, char *buf, size_t len, + read_handler handler) + { + std::stringstream s; + s << "iostream_con async_read_at_least: " << num_bytes; + m_alog.write(log::alevel::devel,s.str()); + + if (num_bytes > len) { + handler(make_error_code(error::invalid_num_bytes),size_t(0)); + return; + } + + if (m_reading == true) { + handler(make_error_code(error::double_read),size_t(0)); + return; + } + + if (num_bytes == 0 || len == 0) { + handler(lib::error_code(),size_t(0)); + return; + } + + m_buf = buf; + m_len = len; + m_bytes_needed = num_bytes; + m_read_handler = handler; + m_cursor = 0; + m_reading = true; + } + + /// Asyncronous Transport Write + /** + * Write len bytes in buf to the output method. Call handler to report + * success or failure. handler may or may not be called during async_write, + * but it must be safe for this to happen. + * + * Will return 0 on success. Other possible errors (not exhaustive) + * output_stream_required: No output stream was registered to write to + * bad_stream: a ostream pass through error + * + * This method will attempt to write to the registered ostream first. If an + * ostream is not registered it will use the write handler. If neither are + * registered then an error is passed up to the connection. + * + * @param buf buffer to read bytes from + * @param len number of bytes to write + * @param handler Callback to invoke with operation status. + */ + void async_write(char const * buf, size_t len, transport::write_handler + handler) + { + m_alog.write(log::alevel::devel,"iostream_con async_write"); + // TODO: lock transport state? + + lib::error_code ec; + + if (m_output_stream) { + m_output_stream->write(buf,len); + + if (m_output_stream->bad()) { + ec = make_error_code(error::bad_stream); + } + } else if (m_write_handler) { + ec = m_write_handler(m_connection_hdl, buf, len); + } else { + ec = make_error_code(error::output_stream_required); + } + + handler(ec); + } + + /// Asyncronous Transport Write (scatter-gather) + /** + * Write a sequence of buffers to the output method. Call handler to report + * success or failure. handler may or may not be called during async_write, + * but it must be safe for this to happen. + * + * Will return 0 on success. Other possible errors (not exhaustive) + * output_stream_required: No output stream was registered to write to + * bad_stream: a ostream pass through error + * + * This method will attempt to write to the registered ostream first. If an + * ostream is not registered it will use the write handler. If neither are + * registered then an error is passed up to the connection. + * + * @param bufs vector of buffers to write + * @param handler Callback to invoke with operation status. + */ + void async_write(std::vector const & bufs, transport::write_handler + handler) + { + m_alog.write(log::alevel::devel,"iostream_con async_write buffer list"); + // TODO: lock transport state? + + lib::error_code ec; + + if (m_output_stream) { + std::vector::const_iterator it; + for (it = bufs.begin(); it != bufs.end(); it++) { + m_output_stream->write((*it).buf,(*it).len); + + if (m_output_stream->bad()) { + ec = make_error_code(error::bad_stream); + break; + } + } + } else if (m_vector_write_handler) { + ec = m_vector_write_handler(m_connection_hdl, bufs); + } else if (m_write_handler) { + std::vector::const_iterator it; + for (it = bufs.begin(); it != bufs.end(); it++) { + ec = m_write_handler(m_connection_hdl, (*it).buf, (*it).len); + if (ec) {break;} + } + + } else { + ec = make_error_code(error::output_stream_required); + } + + handler(ec); + } + + /// Set Connection Handle + /** + * @param hdl The new handle + */ + void set_handle(connection_hdl hdl) { + m_connection_hdl = hdl; + } + + /// Call given handler back within the transport's event system (if present) + /** + * Invoke a callback within the transport's event system if it has one. If + * it doesn't, the handler will be invoked immediately before this function + * returns. + * + * @param handler The callback to invoke + * + * @return Whether or not the transport was able to register the handler for + * callback. + */ + lib::error_code dispatch(dispatch_handler handler) { + handler(); + return lib::error_code(); + } + + /// Perform cleanup on socket shutdown_handler + /** + * If a shutdown handler is set, call it and pass through its return error + * code. Otherwise assume there is nothing to do and pass through a success + * code. + * + * @param handler The `shutdown_handler` to call back when complete + */ + void async_shutdown(transport::shutdown_handler handler) { + lib::error_code ec; + + if (m_shutdown_handler) { + ec = m_shutdown_handler(m_connection_hdl); + } + + handler(ec); + } +private: + void read(std::istream &in) { + m_alog.write(log::alevel::devel,"iostream_con read"); + + while (in.good()) { + if (!m_reading) { + m_elog.write(log::elevel::devel,"write while not reading"); + break; + } + + in.read(m_buf+m_cursor,static_cast(m_len-m_cursor)); + + if (in.gcount() == 0) { + m_elog.write(log::elevel::devel,"read zero bytes"); + break; + } + + m_cursor += static_cast(in.gcount()); + + // TODO: error handling + if (in.bad()) { + m_reading = false; + complete_read(make_error_code(error::bad_stream)); + } + + if (m_cursor >= m_bytes_needed) { + m_reading = false; + complete_read(lib::error_code()); + } + } + } + + size_t read_some_impl(char const * buf, size_t len) { + m_alog.write(log::alevel::devel,"iostream_con read_some"); + + if (!m_reading) { + m_elog.write(log::elevel::devel,"write while not reading"); + return 0; + } + + size_t bytes_to_copy = (std::min)(len,m_len-m_cursor); + + std::copy(buf,buf+bytes_to_copy,m_buf+m_cursor); + + m_cursor += bytes_to_copy; + + if (m_cursor >= m_bytes_needed) { + complete_read(lib::error_code()); + } + + return bytes_to_copy; + } + + /// Signal that a requested read is complete + /** + * Sets the reading flag to false and returns the handler that should be + * called back with the result of the read. The cursor position that is sent + * is whatever the value of m_cursor is. + * + * It MUST NOT be called when m_reading is false. + * it MUST be called while holding the read lock + * + * It is important to use this method rather than directly setting/calling + * m_read_handler back because this function makes sure to delete the + * locally stored handler which contains shared pointers that will otherwise + * cause circular reference based memory leaks. + * + * @param ec The error code to forward to the read handler + */ + void complete_read(lib::error_code const & ec) { + m_reading = false; + + read_handler handler = m_read_handler; + m_read_handler = read_handler(); + + handler(ec,m_cursor); + } + + // Read space (Protected by m_read_mutex) + char * m_buf; + size_t m_len; + size_t m_bytes_needed; + read_handler m_read_handler; + size_t m_cursor; + + // transport resources + std::ostream * m_output_stream; + connection_hdl m_connection_hdl; + write_handler m_write_handler; + vector_write_handler m_vector_write_handler; + shutdown_handler m_shutdown_handler; + + bool m_reading; + bool const m_is_server; + bool m_is_secure; + alog_type & m_alog; + elog_type & m_elog; + std::string m_remote_endpoint; + + // This lock ensures that only one thread can edit read data for this + // connection. This is a very coarse lock that is basically locked all the + // time. The nature of the connection is such that it cannot be + // parallelized, the locking is here to prevent intra-connection concurrency + // in order to allow inter-connection concurrency. + mutex_type m_read_mutex; +}; + + +} // namespace iostream +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP diff --git a/websocketpp/transport/iostream/endpoint.hpp b/websocketpp/transport/iostream/endpoint.hpp new file mode 100644 index 00000000..14ec6537 --- /dev/null +++ b/websocketpp/transport/iostream/endpoint.hpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP +#define WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP + +#include +#include + +#include +#include + +#include + +#include + +namespace websocketpp { +namespace transport { +namespace iostream { + +template +class endpoint { +public: + /// Type of this endpoint transport component + typedef endpoint type; + /// Type of a pointer to this endpoint transport component + typedef lib::shared_ptr ptr; + + /// Type of this endpoint's concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of this endpoint's error logging policy + typedef typename config::elog_type elog_type; + /// Type of this endpoint's access logging policy + typedef typename config::alog_type alog_type; + + /// Type of this endpoint transport component's associated connection + /// transport component. + typedef iostream::connection transport_con_type; + /// Type of a shared pointer to this endpoint transport component's + /// associated connection transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + // generate and manage our own io_service + explicit endpoint() : m_output_stream(NULL), m_is_secure(false) + { + //std::cout << "transport::iostream::endpoint constructor" << std::endl; + } + + /// Register a default output stream + /** + * The specified output stream will be assigned to future connections as the + * default output stream. + * + * @param o The ostream to use as the default output stream. + */ + void register_ostream(std::ostream * o) { + m_alog->write(log::alevel::devel,"register_ostream"); + m_output_stream = o; + } + + /// Set whether or not endpoint can create secure connections + /** + * The iostream transport does not provide any security features. As such + * it defaults to returning false when `is_secure` is called. However, the + * iostream transport may be used to wrap an external socket API that may + * provide secure transport. This method allows that external API to flag + * whether or not it can create secure connections so that users of the + * WebSocket++ API will get more accurate information. + * + * Setting this value only indicates whether or not the endpoint is capable + * of producing and managing secure connections. Connections produced by + * this endpoint must also be individually flagged as secure if they are. + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not the endpoint can create secure connections. + */ + void set_secure(bool value) { + m_is_secure = value; + } + + /// Tests whether or not the underlying transport is secure + /** + * iostream transport will return false by default because it has no + * information about the ultimate remote endpoint. This may or may not be + * accurate depending on the real source of bytes being input. `set_secure` + * may be used by a wrapper API to correct the return value in the case that + * secure connections are in fact possible. + * + * @return Whether or not the underlying transport is secure + */ + bool is_secure() const { + return m_is_secure; + } + + /// Sets the write handler + /** + * The write handler is called when the iostream transport receives data + * that needs to be written to the appropriate output location. This handler + * can be used in place of registering an ostream for output. + * + * The signature of the handler is + * `lib::error_code (connection_hdl, char const *, size_t)` The + * code returned will be reported and logged by the core library. + * + * @since 0.5.0 + * + * @param h The handler to call on connection shutdown. + */ + void set_write_handler(write_handler h) { + m_write_handler = h; + } + + /// Sets the shutdown handler + /** + * The shutdown handler is called when the iostream transport receives a + * notification from the core library that it is finished with all read and + * write operations and that the underlying transport can be cleaned up. + * + * If you are using iostream transport with another socket library, this is + * a good time to close/shutdown the socket for this connection. + * + * The signature of the handler is lib::error_code (connection_hdl). The + * code returned will be reported and logged by the core library. + * + * @since 0.5.0 + * + * @param h The handler to call on connection shutdown. + */ + void set_shutdown_handler(shutdown_handler h) { + m_shutdown_handler = h; + } +protected: + /// Initialize logging + /** + * The loggers are located in the main endpoint class. As such, the + * transport doesn't have direct access to them. This method is called + * by the endpoint constructor to allow shared logging from the transport + * component. These are raw pointers to member variables of the endpoint. + * In particular, they cannot be used in the transport constructor as they + * haven't been constructed yet, and cannot be used in the transport + * destructor as they will have been destroyed by then. + * + * @param a A pointer to the access logger to use. + * @param e A pointer to the error logger to use. + */ + void init_logging(alog_type * a, elog_type * e) { + m_elog = e; + m_alog = a; + } + + /// Initiate a new connection + /** + * @param tcon A pointer to the transport connection component of the + * connection to connect. + * @param u A URI pointer to the URI to connect to. + * @param cb The function to call back with the results when complete. + */ + void async_connect(transport_con_ptr, uri_ptr, connect_handler cb) { + cb(lib::error_code()); + } + + /// Initialize a connection + /** + * Init is called by an endpoint once for each newly created connection. + * It's purpose is to give the transport policy the chance to perform any + * transport specific initialization that couldn't be done via the default + * constructor. + * + * @param tcon A pointer to the transport portion of the connection. + * @return A status code indicating the success or failure of the operation + */ + lib::error_code init(transport_con_ptr tcon) { + tcon->register_ostream(m_output_stream); + if (m_shutdown_handler) { + tcon->set_shutdown_handler(m_shutdown_handler); + } + if (m_write_handler) { + tcon->set_write_handler(m_write_handler); + } + return lib::error_code(); + } +private: + std::ostream * m_output_stream; + shutdown_handler m_shutdown_handler; + write_handler m_write_handler; + + elog_type * m_elog; + alog_type * m_alog; + bool m_is_secure; +}; + + +} // namespace iostream +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP diff --git a/websocketpp/transport/stub/base.hpp b/websocketpp/transport/stub/base.hpp new file mode 100644 index 00000000..754981e2 --- /dev/null +++ b/websocketpp/transport/stub/base.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP +#define WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP + +#include +#include + +#include + +namespace websocketpp { +namespace transport { +/// Stub transport policy that has no input or output. +namespace stub { + +/// stub transport errors +namespace error { +enum value { + /// Catch-all error for transport policy errors that don't fit in other + /// categories + general = 1, + + /// not implemented + not_implemented +}; + +/// stub transport error category +class category : public lib::error_category { + public: + category() {} + + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.transport.stub"; + } + + std::string message(int value) const { + switch(value) { + case general: + return "Generic stub transport policy error"; + case not_implemented: + return "feature not implemented"; + default: + return "Unknown"; + } + } +}; + +/// Get a reference to a static copy of the stub transport error category +inline lib::error_category const & get_category() { + static category instance; + return instance; +} + +/// Get an error code with the given value and the stub transport category +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); +} + +} // namespace error +} // namespace stub +} // namespace transport +} // namespace websocketpp +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + +#endif // WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP diff --git a/websocketpp/transport/stub/connection.hpp b/websocketpp/transport/stub/connection.hpp new file mode 100644 index 00000000..59bd4a0a --- /dev/null +++ b/websocketpp/transport/stub/connection.hpp @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_STUB_CON_HPP +#define WEBSOCKETPP_TRANSPORT_STUB_CON_HPP + +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace stub { + +/// Empty timer class to stub out for timer functionality that stub +/// transport doesn't support +struct timer { + void cancel() {} +}; + +template +class connection : public lib::enable_shared_from_this< connection > { +public: + /// Type of this connection transport component + typedef connection type; + /// Type of a shared pointer to this connection transport component + typedef lib::shared_ptr ptr; + + /// transport concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of this transport's access logging policy + typedef typename config::alog_type alog_type; + /// Type of this transport's error logging policy + typedef typename config::elog_type elog_type; + + // Concurrency policy types + typedef typename concurrency_type::scoped_lock_type scoped_lock_type; + typedef typename concurrency_type::mutex_type mutex_type; + + typedef lib::shared_ptr timer_ptr; + + explicit connection(bool is_server, alog_type & alog, elog_type & elog) + : m_alog(alog), m_elog(elog) + { + m_alog.write(log::alevel::devel,"stub con transport constructor"); + } + + /// Get a shared pointer to this component + ptr get_shared() { + return type::shared_from_this(); + } + + /// Set whether or not this connection is secure + /** + * Todo: docs + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not this connection is secure. + */ + void set_secure(bool value) {} + + /// Tests whether or not the underlying transport is secure + /** + * TODO: docs + * + * @return Whether or not the underlying transport is secure + */ + bool is_secure() const { + return false; + } + + /// Set uri hook + /** + * Called by the endpoint as a connection is being established to provide + * the uri being connected to to the transport layer. + * + * Implementation is optional and can be ignored if the transport has no + * need for this information. + * + * @since 0.6.0 + * + * @param u The uri to set + */ + void set_uri(uri_ptr) {} + + /// Set human readable remote endpoint address + /** + * Sets the remote endpoint address returned by `get_remote_endpoint`. This + * value should be a human readable string that describes the remote + * endpoint. Typically an IP address or hostname, perhaps with a port. But + * may be something else depending on the nature of the underlying + * transport. + * + * If none is set a default is returned. + * + * @since 0.3.0-alpha4 + * + * @param value The remote endpoint address to set. + */ + void set_remote_endpoint(std::string value) {} + + /// Get human readable remote endpoint address + /** + * TODO: docs + * + * This value is used in access and error logs and is available to the end + * application for including in user facing interfaces and messages. + * + * @return A string identifying the address of the remote endpoint + */ + std::string get_remote_endpoint() const { + return "unknown (stub transport)"; + } + + /// Get the connection handle + /** + * @return The handle for this connection. + */ + connection_hdl get_handle() const { + return connection_hdl(); + } + + /// Call back a function after a period of time. + /** + * Timers are not implemented in this transport. The timer pointer will + * always be empty. The handler will never be called. + * + * @param duration Length of time to wait in milliseconds + * @param callback The function to call back when the timer has expired + * @return A handle that can be used to cancel the timer if it is no longer + * needed. + */ + timer_ptr set_timer(long duration, timer_handler handler) { + return timer_ptr(); + } +protected: + /// Initialize the connection transport + /** + * Initialize the connection's transport component. + * + * @param handler The `init_handler` to call when initialization is done + */ + void init(init_handler handler) { + m_alog.write(log::alevel::devel,"stub connection init"); + handler(make_error_code(error::not_implemented)); + } + + /// Initiate an async_read for at least num_bytes bytes into buf + /** + * Initiates an async_read request for at least num_bytes bytes. The input + * will be read into buf. A maximum of len bytes will be input. When the + * operation is complete, handler will be called with the status and number + * of bytes read. + * + * This method may or may not call handler from within the initial call. The + * application should be prepared to accept either. + * + * The application should never call this method a second time before it has + * been called back for the first read. If this is done, the second read + * will be called back immediately with a double_read error. + * + * If num_bytes or len are zero handler will be called back immediately + * indicating success. + * + * @param num_bytes Don't call handler until at least this many bytes have + * been read. + * @param buf The buffer to read bytes into + * @param len The size of buf. At maximum, this many bytes will be read. + * @param handler The callback to invoke when the operation is complete or + * ends in an error + */ + void async_read_at_least(size_t num_bytes, char * buf, size_t len, + read_handler handler) + { + m_alog.write(log::alevel::devel, "stub_con async_read_at_least"); + handler(make_error_code(error::not_implemented), 0); + } + + /// Asyncronous Transport Write + /** + * Write len bytes in buf to the output stream. Call handler to report + * success or failure. handler may or may not be called during async_write, + * but it must be safe for this to happen. + * + * Will return 0 on success. + * + * @param buf buffer to read bytes from + * @param len number of bytes to write + * @param handler Callback to invoke with operation status. + */ + void async_write(char const * buf, size_t len, write_handler handler) { + m_alog.write(log::alevel::devel,"stub_con async_write"); + handler(make_error_code(error::not_implemented)); + } + + /// Asyncronous Transport Write (scatter-gather) + /** + * Write a sequence of buffers to the output stream. Call handler to report + * success or failure. handler may or may not be called during async_write, + * but it must be safe for this to happen. + * + * Will return 0 on success. + * + * @param bufs vector of buffers to write + * @param handler Callback to invoke with operation status. + */ + void async_write(std::vector const & bufs, write_handler handler) { + m_alog.write(log::alevel::devel,"stub_con async_write buffer list"); + handler(make_error_code(error::not_implemented)); + } + + /// Set Connection Handle + /** + * @param hdl The new handle + */ + void set_handle(connection_hdl hdl) {} + + /// Call given handler back within the transport's event system (if present) + /** + * Invoke a callback within the transport's event system if it has one. If + * it doesn't, the handler will be invoked immediately before this function + * returns. + * + * @param handler The callback to invoke + * + * @return Whether or not the transport was able to register the handler for + * callback. + */ + lib::error_code dispatch(dispatch_handler handler) { + handler(); + return lib::error_code(); + } + + /// Perform cleanup on socket shutdown_handler + /** + * @param h The `shutdown_handler` to call back when complete + */ + void async_shutdown(shutdown_handler handler) { + handler(lib::error_code()); + } +private: + // member variables! + alog_type & m_alog; + elog_type & m_elog; +}; + + +} // namespace stub +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_STUB_CON_HPP diff --git a/websocketpp/transport/stub/endpoint.hpp b/websocketpp/transport/stub/endpoint.hpp new file mode 100644 index 00000000..3bbb78f3 --- /dev/null +++ b/websocketpp/transport/stub/endpoint.hpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_TRANSPORT_STUB_HPP +#define WEBSOCKETPP_TRANSPORT_STUB_HPP + +#include +#include + +#include +#include + +namespace websocketpp { +namespace transport { +namespace stub { + +template +class endpoint { +public: + /// Type of this endpoint transport component + typedef endpoint type; + /// Type of a pointer to this endpoint transport component + typedef lib::shared_ptr ptr; + + /// Type of this endpoint's concurrency policy + typedef typename config::concurrency_type concurrency_type; + /// Type of this endpoint's error logging policy + typedef typename config::elog_type elog_type; + /// Type of this endpoint's access logging policy + typedef typename config::alog_type alog_type; + + /// Type of this endpoint transport component's associated connection + /// transport component. + typedef stub::connection transport_con_type; + /// Type of a shared pointer to this endpoint transport component's + /// associated connection transport component + typedef typename transport_con_type::ptr transport_con_ptr; + + // generate and manage our own io_service + explicit endpoint() + { + //std::cout << "transport::iostream::endpoint constructor" << std::endl; + } + + /// Set whether or not endpoint can create secure connections + /** + * TODO: docs + * + * Setting this value only indicates whether or not the endpoint is capable + * of producing and managing secure connections. Connections produced by + * this endpoint must also be individually flagged as secure if they are. + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not the endpoint can create secure connections. + */ + void set_secure(bool value) {} + + /// Tests whether or not the underlying transport is secure + /** + * TODO: docs + * + * @return Whether or not the underlying transport is secure + */ + bool is_secure() const { + return false; + } +protected: + /// Initialize logging + /** + * The loggers are located in the main endpoint class. As such, the + * transport doesn't have direct access to them. This method is called + * by the endpoint constructor to allow shared logging from the transport + * component. These are raw pointers to member variables of the endpoint. + * In particular, they cannot be used in the transport constructor as they + * haven't been constructed yet, and cannot be used in the transport + * destructor as they will have been destroyed by then. + * + * @param a A pointer to the access logger to use. + * @param e A pointer to the error logger to use. + */ + void init_logging(alog_type * a, elog_type * e) {} + + /// Initiate a new connection + /** + * @param tcon A pointer to the transport connection component of the + * connection to connect. + * @param u A URI pointer to the URI to connect to. + * @param cb The function to call back with the results when complete. + */ + void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) { + cb(make_error_code(error::not_implemented)); + } + + /// Initialize a connection + /** + * Init is called by an endpoint once for each newly created connection. + * It's purpose is to give the transport policy the chance to perform any + * transport specific initialization that couldn't be done via the default + * constructor. + * + * @param tcon A pointer to the transport portion of the connection. + * @return A status code indicating the success or failure of the operation + */ + lib::error_code init(transport_con_ptr tcon) { + return make_error_code(error::not_implemented); + } +private: + +}; + +} // namespace stub +} // namespace transport +} // namespace websocketpp + +#endif // WEBSOCKETPP_TRANSPORT_STUB_HPP diff --git a/websocketpp/uri.hpp b/websocketpp/uri.hpp new file mode 100644 index 00000000..7159234f --- /dev/null +++ b/websocketpp/uri.hpp @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_URI_HPP +#define WEBSOCKETPP_URI_HPP + +#include + +#include + +#include +#include +#include + +namespace websocketpp { + +// TODO: figure out why this fixes horrible linking errors. + +/// Default port for ws:// +static uint16_t const uri_default_port = 80; +/// Default port for wss:// +static uint16_t const uri_default_secure_port = 443; + +class uri { +public: + explicit uri(std::string const & uri_string) : m_valid(false) { + std::string::const_iterator it; + std::string::const_iterator temp; + + int state = 0; + + it = uri_string.begin(); + size_t uri_len = uri_string.length(); + + if (uri_len >= 7 && std::equal(it,it+6,"wss://")) { + m_secure = true; + m_scheme = "wss"; + it += 6; + } else if (uri_len >= 6 && std::equal(it,it+5,"ws://")) { + m_secure = false; + m_scheme = "ws"; + it += 5; + } else if (uri_len >= 8 && std::equal(it,it+7,"http://")) { + m_secure = false; + m_scheme = "http"; + it += 7; + } else if (uri_len >= 9 && std::equal(it,it+8,"https://")) { + m_secure = true; + m_scheme = "https"; + it += 8; + } else { + return; + } + + // extract host. + // either a host string + // an IPv4 address + // or an IPv6 address + if (*it == '[') { + ++it; + // IPv6 literal + // extract IPv6 digits until ] + + // TODO: this doesn't work on g++... not sure why + //temp = std::find(it,it2,']'); + + temp = it; + while (temp != uri_string.end()) { + if (*temp == ']') { + break; + } + ++temp; + } + + if (temp == uri_string.end()) { + return; + } else { + // validate IPv6 literal parts + // can contain numbers, a-f and A-F + m_host.append(it,temp); + } + it = temp+1; + if (it == uri_string.end()) { + state = 2; + } else if (*it == '/') { + state = 2; + ++it; + } else if (*it == ':') { + state = 1; + ++it; + } else { + // problem + return; + } + } else { + // IPv4 or hostname + // extract until : or / + while (state == 0) { + if (it == uri_string.end()) { + state = 2; + break; + } else if (*it == '/') { + state = 2; + } else if (*it == ':') { + // end hostname start port + state = 1; + } else { + m_host += *it; + } + ++it; + } + } + + // parse port + std::string port; + while (state == 1) { + if (it == uri_string.end()) { + // state is not used after this point presently. + // this should be re-enabled if it ever is needed in a future + // refactoring + //state = 3; + break; + } else if (*it == '/') { + state = 3; + } else { + port += *it; + } + ++it; + } + + lib::error_code ec; + m_port = get_port_from_string(port, ec); + + if (ec) { + return; + } + + m_resource = "/"; + m_resource.append(it,uri_string.end()); + + + m_valid = true; + } + + uri(bool secure, std::string const & host, uint16_t port, + std::string const & resource) + : m_scheme(secure ? "wss" : "ws") + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_port(port) + , m_secure(secure) + , m_valid(true) {} + + uri(bool secure, std::string const & host, std::string const & resource) + : m_scheme(secure ? "wss" : "ws") + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_port(secure ? uri_default_secure_port : uri_default_port) + , m_secure(secure) + , m_valid(true) {} + + uri(bool secure, std::string const & host, std::string const & port, + std::string const & resource) + : m_scheme(secure ? "wss" : "ws") + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_secure(secure) + { + lib::error_code ec; + m_port = get_port_from_string(port,ec); + m_valid = !ec; + } + + uri(std::string const & scheme, std::string const & host, uint16_t port, + std::string const & resource) + : m_scheme(scheme) + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_port(port) + , m_secure(scheme == "wss" || scheme == "https") + , m_valid(true) {} + + uri(std::string scheme, std::string const & host, std::string const & resource) + : m_scheme(scheme) + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_port((scheme == "wss" || scheme == "https") ? uri_default_secure_port : uri_default_port) + , m_secure(scheme == "wss" || scheme == "https") + , m_valid(true) {} + + uri(std::string const & scheme, std::string const & host, + std::string const & port, std::string const & resource) + : m_scheme(scheme) + , m_host(host) + , m_resource(resource.empty() ? "/" : resource) + , m_secure(scheme == "wss" || scheme == "https") + { + lib::error_code ec; + m_port = get_port_from_string(port,ec); + m_valid = !ec; + } + + bool get_valid() const { + return m_valid; + } + + bool get_secure() const { + return m_secure; + } + + std::string const & get_scheme() const { + return m_scheme; + } + + std::string const & get_host() const { + return m_host; + } + + std::string get_host_port() const { + if (m_port == (m_secure ? uri_default_secure_port : uri_default_port)) { + return m_host; + } else { + std::stringstream p; + p << m_host << ":" << m_port; + return p.str(); + } + } + + std::string get_authority() const { + std::stringstream p; + p << m_host << ":" << m_port; + return p.str(); + } + + uint16_t get_port() const { + return m_port; + } + + std::string get_port_str() const { + std::stringstream p; + p << m_port; + return p.str(); + } + + std::string const & get_resource() const { + return m_resource; + } + + std::string str() const { + std::stringstream s; + + s << m_scheme << "://" << m_host; + + if (m_port != (m_secure ? uri_default_secure_port : uri_default_port)) { + s << ":" << m_port; + } + + s << m_resource; + return s.str(); + } + + /// Return the query portion + /** + * Returns the query portion (after the ?) of the URI or an empty string if + * there is none. + * + * @return query portion of the URI. + */ + std::string get_query() const { + std::size_t found = m_resource.find('?'); + if (found != std::string::npos) { + return m_resource.substr(found + 1); + } else { + return ""; + } + } + + // get fragment + + // hi <3 + + // get the string representation of this URI + + //std::string base() const; // is this still needed? + + // setter methods set some or all (in the case of parse) based on the input. + // These functions throw a uri_exception on failure. + /*void set_uri(const std::string& uri); + + void set_secure(bool secure); + void set_host(const std::string& host); + void set_port(uint16_t port); + void set_port(const std::string& port); + void set_resource(const std::string& resource);*/ +private: + uint16_t get_port_from_string(std::string const & port, lib::error_code & + ec) const + { + ec = lib::error_code(); + + if (port.empty()) { + return (m_secure ? uri_default_secure_port : uri_default_port); + } + + unsigned int t_port = static_cast(atoi(port.c_str())); + + if (t_port > 65535) { + ec = error::make_error_code(error::invalid_port); + } + + if (t_port == 0) { + ec = error::make_error_code(error::invalid_port); + } + + return static_cast(t_port); + } + + std::string m_scheme; + std::string m_host; + std::string m_resource; + uint16_t m_port; + bool m_secure; + bool m_valid; +}; + +/// Pointer to a URI +typedef lib::shared_ptr uri_ptr; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_URI_HPP diff --git a/websocketpp/utf8_validator.hpp b/websocketpp/utf8_validator.hpp new file mode 100644 index 00000000..c057da62 --- /dev/null +++ b/websocketpp/utf8_validator.hpp @@ -0,0 +1,154 @@ +/* + * The following code is adapted from code originally written by Bjoern + * Hoehrmann . See + * http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. + * + * The original license: + * + * Copyright (c) 2008-2009 Bjoern Hoehrmann + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +#ifndef UTF8_VALIDATOR_HPP +#define UTF8_VALIDATOR_HPP + +#include + +#include + +namespace websocketpp { +namespace utf8_validator { + +/// State that represents a valid utf8 input sequence +static unsigned int const utf8_accept = 0; +/// State that represents an invalid utf8 input sequence +static unsigned int const utf8_reject = 1; + +/// Lookup table for the UTF8 decode state machine +static uint8_t const utf8d[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 +}; + +/// Decode the next byte of a UTF8 sequence +/** + * @param [out] state The decoder state to advance + * @param [out] codep The codepoint to fill in + * @param [in] byte The byte to input + * @return The ending state of the decode operation + */ +inline uint32_t decode(uint32_t * state, uint32_t * codep, uint8_t byte) { + uint32_t type = utf8d[byte]; + + *codep = (*state != utf8_accept) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> type) & (byte); + + *state = utf8d[256 + *state*16 + type]; + return *state; +} + +/// Provides streaming UTF8 validation functionality +class validator { +public: + /// Construct and initialize the validator + validator() : m_state(utf8_accept),m_codepoint(0) {} + + /// Advance the state of the validator with the next input byte + /** + * @param byte The byte to advance the validation state with + * @return Whether or not the byte resulted in a validation error. + */ + bool consume (uint8_t byte) { + if (utf8_validator::decode(&m_state,&m_codepoint,byte) == utf8_reject) { + return false; + } + return true; + } + + /// Advance validator state with input from an iterator pair + /** + * @param begin Input iterator to the start of the input range + * @param end Input iterator to the end of the input range + * @return Whether or not decoding the bytes resulted in a validation error. + */ + template + bool decode (iterator_type begin, iterator_type end) { + for (iterator_type it = begin; it != end; ++it) { + unsigned int result = utf8_validator::decode( + &m_state, + &m_codepoint, + static_cast(*it) + ); + + if (result == utf8_reject) { + return false; + } + } + return true; + } + + /// Return whether the input sequence ended on a valid utf8 codepoint + /** + * @return Whether or not the input sequence ended on a valid codepoint. + */ + bool complete() { + return m_state == utf8_accept; + } + + /// Reset the validator to decode another message + void reset() { + m_state = utf8_accept; + m_codepoint = 0; + } +private: + uint32_t m_state; + uint32_t m_codepoint; +}; + +/// Validate a UTF8 string +/** + * convenience function that creates a validator, validates a complete string + * and returns the result. + */ +inline bool validate(std::string const & s) { + validator v; + if (!v.decode(s.begin(),s.end())) { + return false; + } + return v.complete(); +} + +} // namespace utf8_validator +} // namespace websocketpp + +#endif // UTF8_VALIDATOR_HPP diff --git a/websocketpp/utilities.hpp b/websocketpp/utilities.hpp new file mode 100644 index 00000000..747f1199 --- /dev/null +++ b/websocketpp/utilities.hpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2014, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_UTILITIES_HPP +#define WEBSOCKETPP_UTILITIES_HPP + +#include + +#include +#include +#include + +namespace websocketpp { +/// Generic non-websocket specific utility functions and data structures +namespace utility { + +/// Helper functor for case insensitive find +/** + * Based on code from + * http://stackoverflow.com/questions/3152241/case-insensitive-stdstring-find + * + * templated version of my_equal so it could work with both char and wchar_t + */ +template +struct my_equal { + /// Construct the functor with the given locale + /** + * @param [in] loc The locale to use for determining the case of values + */ + my_equal(std::locale const & loc ) : m_loc(loc) {} + + /// Perform a case insensitive comparison + /** + * @param ch1 The first value to compare + * @param ch2 The second value to compare + * @return Whether or not the two values are equal when both are converted + * to uppercase using the given locale. + */ + bool operator()(charT ch1, charT ch2) { + return std::toupper(ch1, m_loc) == std::toupper(ch2, m_loc); + } +private: + std::locale const & m_loc; +}; + +/// Helper less than functor for case insensitive find +/** + * Based on code from + * http://stackoverflow.com/questions/3152241/case-insensitive-stdstring-find + */ +struct ci_less : std::binary_function { + // case-independent (ci) compare_less binary function + struct nocase_compare + : public std::binary_function + { + bool operator() (unsigned char const & c1, unsigned char const & c2) const { + return tolower (c1) < tolower (c2); + } + }; + bool operator() (std::string const & s1, std::string const & s2) const { + return std::lexicographical_compare + (s1.begin (), s1.end (), // source range + s2.begin (), s2.end (), // dest range + nocase_compare ()); // comparison + } +}; + +/// Find substring (case insensitive) +/** + * @param [in] haystack The string to search in + * @param [in] needle The string to search for + * @param [in] loc The locale to use for determining the case of values. + * Defaults to the current locale. + * @return An iterator to the first element of the first occurrance of needle in + * haystack. If the sequence is not found, the function returns + * haystack.end() + */ +template +typename T::const_iterator ci_find_substr(T const & haystack, T const & needle, + std::locale const & loc = std::locale()) +{ + return std::search( haystack.begin(), haystack.end(), + needle.begin(), needle.end(), my_equal(loc) ); +} + +/// Find substring (case insensitive) +/** + * @todo Is this still used? This method may not make sense.. should use + * iterators or be less generic. As is it is too tightly coupled to std::string + * + * @param [in] haystack The string to search in + * @param [in] needle The string to search for as a char array of values + * @param [in] size Length of needle + * @param [in] loc The locale to use for determining the case of values. + * Defaults to the current locale. + * @return An iterator to the first element of the first occurrance of needle in + * haystack. If the sequence is not found, the function returns + * haystack.end() + */ +template +typename T::const_iterator ci_find_substr(T const & haystack, + typename T::value_type const * needle, typename T::size_type size, + std::locale const & loc = std::locale()) +{ + return std::search( haystack.begin(), haystack.end(), + needle, needle+size, my_equal(loc) ); +} + +/// Convert a string to lowercase +/** + * @param [in] in The string to convert + * @return The converted string + */ +std::string to_lower(std::string const & in); + +/// Replace all occurrances of a substring with another +/** + * @param [in] subject The string to search in + * @param [in] search The string to search for + * @param [in] replace The string to replace with + * @return A copy of `subject` with all occurances of `search` replaced with + * `replace` + */ +std::string string_replace_all(std::string subject, std::string const & search, + std::string const & replace); + +/// Convert std::string to ascii printed string of hex digits +/** + * @param [in] input The string to print + * @return A copy of `input` converted to the printable representation of the + * hex values of its data. + */ +std::string to_hex(std::string const & input); + +/// Convert byte array (uint8_t) to ascii printed string of hex digits +/** + * @param [in] input The byte array to print + * @param [in] length The length of input + * @return A copy of `input` converted to the printable representation of the + * hex values of its data. + */ +std::string to_hex(uint8_t const * input, size_t length); + +/// Convert char array to ascii printed string of hex digits +/** + * @param [in] input The char array to print + * @param [in] length The length of input + * @return A copy of `input` converted to the printable representation of the + * hex values of its data. + */ +std::string to_hex(char const * input, size_t length); + +} // namespace utility +} // namespace websocketpp + +#include + +#endif // WEBSOCKETPP_UTILITIES_HPP diff --git a/websocketpp/version.hpp b/websocketpp/version.hpp new file mode 100644 index 00000000..b88cc110 --- /dev/null +++ b/websocketpp/version.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_VERSION_HPP +#define WEBSOCKETPP_VERSION_HPP + +/// Namespace for the WebSocket++ project +namespace websocketpp { + +/* + other places where version information is kept + - readme.md + - changelog.md + - Doxyfile + - CMakeLists.txt +*/ + +/// Library major version number +static int const major_version = 0; +/// Library minor version number +static int const minor_version = 7; +/// Library patch version number +static int const patch_version = 0; +/// Library pre-release flag +/** + * This is a textual flag indicating the type and number for pre-release + * versions (dev, alpha, beta, rc). This will be blank for release versions. + */ + +static char const prerelease_flag[] = ""; + +/// Default user agent string +static char const user_agent[] = "WebSocket++/0.7.0"; + +} // namespace websocketpp + +#endif // WEBSOCKETPP_VERSION_HPP -- cgit 1.2.3-korg