├── .gitignore ├── src ├── producer │ ├── Camera.h │ ├── ProducerStatusData.h │ ├── ConsumerCounter.h │ ├── DebugCamera.h │ ├── CameraMetadata.h │ ├── Signature.h │ ├── VideoTile.h │ ├── Constants.h │ ├── ImageTile.h │ ├── MediaTile.h │ ├── ConsumerCounter.cpp │ ├── MediaTile.cpp │ ├── DebugCamera.cpp │ ├── Producer.h │ ├── CvCamera.h │ ├── CapturedFrame.h │ ├── mainProducer.cpp │ ├── ImageTile.cpp │ ├── VideoTile.cpp │ ├── Signature.cpp │ ├── CapturedFrame.cpp │ ├── ParametersProducer.h │ ├── CvCamera.cpp │ ├── ParametersProducer.cpp │ └── Producer.cpp └── consumer-js │ ├── README.md │ ├── js │ ├── parameters.js │ ├── rtt-statistics.js │ ├── fieldofview.js │ ├── fixedratedownloader.js │ ├── panorama.js │ ├── framedownloader.js │ └── consumer.js │ ├── libs │ ├── README.md │ └── install-libs.sh │ └── index.html ├── LICENSE.txt ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | .DS_Store 3 | *~ 4 | /bin 5 | /obj 6 | /deps 7 | /test 8 | /local 9 | /.vscode 10 | *.log 11 | /src/consumer-js/libs/*.js 12 | /src/consumer-js/libs/*.css 13 | /src/consumer-js/libs/*.map 14 | /src/consumer-js/libs/*.eot 15 | /src/consumer-js/libs/*.ttf 16 | /src/consumer-js/libs/*.woff2 17 | /src/consumer-js/libs/*.woff 18 | /src/consumer-js/libs/*.json 19 | /src/consumer-js/libs/*.zip 20 | /src/consumer-js/libs/LICENSE 21 | 22 | -------------------------------------------------------------------------------- /src/producer/Camera.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * @license MIT License 4 | * @copyright 2019 KDDI Research, Inc. 5 | */ 6 | #pragma once 7 | #include 8 | 9 | 10 | class CapturedFrame; 11 | struct CameraMetadata; 12 | 13 | class Camera 14 | { 15 | public: 16 | Camera(){}; 17 | virtual ~Camera(){}; 18 | 19 | virtual std::shared_ptr getCapturedFrame(uint32_t sequenceNo) = 0; 20 | virtual void getCameraMetadata(CameraMetadata& metadata) = 0; 21 | }; 22 | -------------------------------------------------------------------------------- /src/producer/ProducerStatusData.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | #include 11 | 12 | struct ProducerStatusData 13 | { 14 | int consumers; 15 | 16 | template 17 | void serialize(Archive & archive) 18 | { 19 | archive(CEREAL_NVP(consumers)); 20 | } 21 | }; -------------------------------------------------------------------------------- /src/producer/ConsumerCounter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | #include 11 | #include 12 | 13 | class ConsumerCounter 14 | { 15 | private: 16 | std::atomic counter_; 17 | int consumer_num_; 18 | std::chrono::system_clock::time_point start_time_; 19 | 20 | public: 21 | ConsumerCounter(); 22 | 23 | void increase(); 24 | int get(); 25 | }; 26 | -------------------------------------------------------------------------------- /src/consumer-js/README.md: -------------------------------------------------------------------------------- 1 | consumer js 2 | === 3 | 360 degree video streaming consumer on NDN.js. Currently only Equirectangular is supported. 4 | 5 | ### Required Library 6 | 7 | You should downalod required libraries and put in `libs` folder 8 | 9 | - ndn.js : NDN Liburary 10 | - THREE.js : 3D Liburary based on WebGL 11 | - Material Dedign Lite : GUI Tools 12 | - dialog-polyfill : UI 13 | - Material Design Icons : Icons 14 | 15 | please read [`libs/README.md`](/src/consumer-js/libs/README.md) 16 | 17 | ### How to use 18 | #### consumer 19 | 20 | Please put `consumer-js` folder in your web site where nfd with WebSocket runs, and access it by a web browser. 21 | One simple way is use http server module of Python. 22 | 23 | ``` 24 | > cd src/consumer-js 25 | > python3 -m http.server 8000 26 | ``` 27 | -------------------------------------------------------------------------------- /src/producer/DebugCamera.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | #include 11 | #include 12 | #include "Camera.h" 13 | 14 | 15 | class DebugCamera : public Camera 16 | { 17 | private: 18 | cv::Mat image_; 19 | std::shared_ptr frame_; 20 | std::chrono::system_clock::time_point base_time_; 21 | int fiv_; 22 | 23 | public: 24 | DebugCamera(); 25 | virtual ~DebugCamera(); 26 | 27 | std::shared_ptr getCapturedFrame(uint32_t sequenceNo); 28 | void getCameraMetadata(CameraMetadata& metadata); 29 | }; 30 | -------------------------------------------------------------------------------- /src/producer/CameraMetadata.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | #include 11 | 12 | struct CameraMetadata 13 | { 14 | int width; 15 | int height; 16 | int fiv; 17 | int fps; 18 | int tile[2]; 19 | int seq; 20 | int mui; // Metadata Update Interval [ms] 21 | std::string mime; 22 | 23 | template 24 | void serialize(Archive & archive) 25 | { 26 | archive(CEREAL_NVP(width), CEREAL_NVP(height), 27 | CEREAL_NVP(fiv), CEREAL_NVP(fps), 28 | CEREAL_NVP(tile), CEREAL_NVP(seq), CEREAL_NVP(mui), 29 | CEREAL_NVP(mime)); 30 | } 31 | }; -------------------------------------------------------------------------------- /src/producer/Signature.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "ParametersProducer.h" 16 | 17 | class Signature 18 | { 19 | private: 20 | ndn::security::v2::KeyChain key_chain_; 21 | ndn::security::Identity identity_; 22 | ndn::security::Key key_; 23 | SignatureType sign_type_; 24 | ndn::security::SigningInfo signature_info_; 25 | 26 | bool has_key_; 27 | 28 | 29 | public: 30 | Signature(const char* prefix); 31 | ~Signature(); 32 | 33 | void sign(ndn::Data& data); 34 | 35 | private: 36 | void signWithHmac(ndn::Data& data); 37 | }; -------------------------------------------------------------------------------- /src/producer/VideoTile.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "MediaTile.h" 17 | 18 | class VideoTile : public MediaTile 19 | { 20 | private: 21 | int file_size_; 22 | boost::filesystem::path filepath_; 23 | 24 | bool encode_; 25 | std::mutex mtx_; 26 | 27 | public: 28 | VideoTile(const std::string& tile_id, std::vector&& frames, int mw, int mh); 29 | ~VideoTile(); 30 | 31 | virtual void encode(); 32 | virtual int fileSize(); 33 | virtual std::shared_ptr> getSegment(uint32_t segment_no); 34 | }; -------------------------------------------------------------------------------- /src/consumer-js/js/parameters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | 10 | Parameters = { 11 | NAME_PREFIX: "/icn2020.org/theta", 12 | MATA_DATA_PREFIX: "/icn2020.org/theta/metadata", 13 | IMG_DATA_PREFIX: "/icn2020.org/theta/frames", 14 | METADATA_UPDATE_INTERVAL: 5000, // 5sec 15 | DOWNLOAD_WAIT_INTERVAL: 250, 16 | DOWNLOAD_PIPELINES: 10, 17 | FIXED_WINDOW_SIZE: 1, 18 | INTEREST_TIMEOUT: 500, 19 | HTTP_MODE: false, 20 | SHOW_GRID: false, 21 | EDGE_MARK: true, 22 | VR_MODE: false, 23 | DEVICE_ORIENTATION: true, 24 | HMD_FOV: 120, 25 | WINDOW_FOV: 75, 26 | 27 | }; -------------------------------------------------------------------------------- /src/producer/Constants.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | 11 | // Naming 12 | constexpr char APP_PREFIX[] = "/icn2020.org/theta"; 13 | constexpr char META_DATA_PATH[] = "/icn2020.org/theta/metadata"; 14 | constexpr char IMG_DATA_PATH[] = "/icn2020.org/theta/frames"; 15 | constexpr char META_DATA_SUFFIX[] = "metadata"; 16 | constexpr char IMG_DATA_SUFFIX[] = "frames"; 17 | constexpr char STATUS_DATA_SUFFIX[] = "status"; 18 | constexpr char LOW_DATA_PREFIX[] = "f:transcoding"; 19 | 20 | 21 | constexpr int NAME_SEGMENT_LOCATION = -1; 22 | constexpr int NAME_COORDINATE_Y_LOCATION = -2; 23 | constexpr int NAME_COORDINATE_X_LOCATION = -3; 24 | constexpr int NAME_SEQUENCE_LOCATION = -4; 25 | constexpr int NAME_QUALITY_LOCATION = -5; -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2019 KDDI Research, Inc. 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. -------------------------------------------------------------------------------- /src/producer/ImageTile.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "MediaTile.h" 17 | 18 | class ImageTile : public MediaTile 19 | { 20 | private: 21 | int file_size_; 22 | // boost::filesystem::path filepath_; 23 | 24 | bool encode_; 25 | std::mutex mtx_; 26 | std::mutex it_mtx_; 27 | 28 | std::unique_ptr> image_; 29 | 30 | public: 31 | ImageTile(const std::string& tile_id, std::vector&& frames, int mw, int mh); 32 | ~ImageTile(); 33 | 34 | virtual void encode(); 35 | virtual int fileSize(); 36 | virtual std::shared_ptr> getSegment(uint32_t segment_no); 37 | }; -------------------------------------------------------------------------------- /src/producer/MediaTile.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "MediaTile.h" 17 | 18 | class MediaTile 19 | { 20 | public: 21 | static std::string tileId(uint32_t seq, int x, int y, bool low_quarity); 22 | 23 | protected: 24 | std::vector frames_; 25 | int width_; 26 | int height_; 27 | 28 | public: 29 | MediaTile(const std::string& tile_id, std::vector&& frames, int mw, int mh); 30 | virtual ~MediaTile(); 31 | 32 | virtual void encode() = 0; 33 | virtual int fileSize() = 0; 34 | virtual std::shared_ptr> getSegment(uint32_t segment_no) = 0; 35 | 36 | uint32_t finalSegmentNo(); 37 | }; -------------------------------------------------------------------------------- /src/producer/ConsumerCounter.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #include "ParametersProducer.h" 10 | #include "ConsumerCounter.h" 11 | 12 | 13 | ConsumerCounter::ConsumerCounter() 14 | : counter_(0), 15 | consumer_num_(0) 16 | { 17 | start_time_ = std::chrono::system_clock::now(); 18 | } 19 | 20 | 21 | void 22 | ConsumerCounter::increase() 23 | { 24 | auto now = std::chrono::system_clock::now(); 25 | auto elapsed = std::chrono::duration_cast(now - start_time_).count(); 26 | if(elapsed > METADATA_UPDATE_INTERVAL){ 27 | consumer_num_ = counter_.load(); 28 | counter_.exchange(0); 29 | start_time_ = std::chrono::system_clock::now(); 30 | } 31 | int expected = counter_.load(); 32 | int desired; 33 | do { 34 | desired = expected + 1; 35 | } while (!counter_.compare_exchange_weak(expected, desired)); 36 | } 37 | 38 | 39 | int 40 | ConsumerCounter::get() 41 | { 42 | return consumer_num_; 43 | } 44 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | UNAME := $(shell uname -s) 2 | 3 | SRC_DIR = ./src 4 | TARGET_DIR = ./bin 5 | OBJ_DIR = ./obj 6 | 7 | 8 | CXXFLAGS = -g -Wall -std=c++14 -MMD -MP 9 | LDLIBS = -lpthread -lm -lboost_program_options -lboost_system 10 | OPENCV_LIBS = `pkg-config --libs opencv` 11 | NDNCXX_LIBS = `pkg-config --libs libndn-cxx` 12 | 13 | PRODUCER = $(TARGET_DIR)/producer 14 | PR_SRC_DIR = $(SRC_DIR)/producer 15 | PR_OBJ_DIR = $(OBJ_DIR)/producer 16 | PR_CXXFLAGS = `pkg-config --cflags opencv libndn-cxx` 17 | PR_SRCS = $(wildcard $(PR_SRC_DIR)/*.cpp) 18 | PR_OBJS = $(addprefix $(PR_OBJ_DIR)/, $(notdir $(PR_SRCS:.cpp=.o))) 19 | PR_DEPS = $(PR_OBJS:.o=.d) 20 | PR_LIBS = $(LOADLIBES) $(NDNCXX_LIBS) $(OPENCV_LIBS) $(LDLIBS) 21 | 22 | SRCS = $(PR_SRCS) 23 | 24 | .PHONY: all clean depend 25 | 26 | all: producer 27 | 28 | producer: $(PRODUCER) 29 | 30 | 31 | $(PRODUCER): $(PR_OBJS) 32 | @-mkdir -p $(TARGET_DIR) 33 | $(LINK.cc) $^ $(PR_LIBS) -o $@ 34 | 35 | $(PR_OBJ_DIR)/%.o: $(PR_SRC_DIR)/%.cpp 36 | @-mkdir -p $(OBJ_DIR) 37 | @-mkdir -p $(PR_OBJ_DIR) 38 | $(COMPILE.cc) $< $(PR_CXXFLAGS) -o $@ 39 | 40 | 41 | clean: 42 | -$(RM) $(CONSUMER) $(PRODUCER) \ 43 | $(PR_OBJS) $(PR_DEPS) \ 44 | *~ .*~ core 45 | 46 | -include $(PR_DEPS) 47 | -------------------------------------------------------------------------------- /src/consumer-js/libs/README.md: -------------------------------------------------------------------------------- 1 | ### Required Library 2 | 3 | - ndn.js : [GitHub:named-data/ndn-js](https://github.com/named-data/ndn-js) 4 | - ndn.min.js : /build/ 5 | - THREE.js : [GitHub:mrdoob/three.js](https://github.com/mrdoob/three.js) 6 | - three.min.js : /build/ 7 | - OrbitControls.js : /examples/js/controls/ 8 | - DeviceOrientationControls.js : /examples/js/controls/ 9 | - StereoEffect.js : /examples/js/effects/ 10 | - WebVR.js : /examples/js/vr/ 11 | - Material Dedign Lite : [https://getmdl.io/started/index.html](https://getmdl.io/started/index.html) 12 | - material.min.js 13 | - material.min.js.map 14 | - material.min.css 15 | - material.min.css.map 16 | - dialog-polyfill : [GitHub:GoogleChrome/dialog-polyfill](https://github.com/GoogleChrome/dialog-polyfill) 17 | - dialog-polyfill.js 18 | - dialog-polyfill.css 19 | - Material Design Icons : [GitHub:google/material-design-icons](https://github.com/google/material-design-icons) 20 | - MaterialIcons-Regular.eot : /iconfont/ 21 | - MaterialIcons-Regular.woff2 : /iconfont/ 22 | - MaterialIcons-Regular.woff : /iconfont/ 23 | - MaterialIcons-Regular.ttf : /iconfont/ 24 | 25 | ### License 26 | 27 | This software is released under the MIT License, see LICENSE.txt -------------------------------------------------------------------------------- /src/producer/MediaTile.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "ParametersProducer.h" 16 | #include "MediaTile.h" 17 | 18 | const size_t MAX_SEGMENT_SIZE = ndn::MAX_NDN_PACKET_SIZE >> 1; 19 | 20 | 21 | std::string 22 | MediaTile::tileId(uint32_t seq, int x, int y, bool low_quality) 23 | { 24 | int block_id = y * ParametersProducer::matrixNumX() + x; 25 | return low_quality ? std::to_string(seq) + "_" + std::to_string(block_id) + "L" 26 | : std::to_string(seq) + "_" + std::to_string(block_id); 27 | } 28 | 29 | 30 | MediaTile::MediaTile(const std::string& tile_id, std::vector&& frames, int mw, int mh) 31 | : frames_(std::move(frames)), 32 | width_(mw), 33 | height_(mh) 34 | { 35 | } 36 | 37 | 38 | MediaTile::~MediaTile() 39 | { 40 | } 41 | 42 | 43 | uint32_t 44 | MediaTile::finalSegmentNo() { 45 | return (fileSize() / MAX_SEGMENT_SIZE); 46 | }; 47 | -------------------------------------------------------------------------------- /src/producer/DebugCamera.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #include 10 | #include "ParametersProducer.h" 11 | #include "CameraMetadata.h" 12 | #include "CapturedFrame.h" 13 | #include "DebugCamera.h" 14 | 15 | DebugCamera::DebugCamera() 16 | : image_(), 17 | frame_(), 18 | base_time_() 19 | { 20 | image_ = cv::imread(ParametersProducer::debugFilename(), cv::IMREAD_COLOR); 21 | frame_ = std::make_shared(image_.rows, image_.cols); 22 | base_time_ = std::chrono::system_clock::now(); 23 | fiv_ = ParametersProducer::videoSegmentSize(); 24 | fiv_ = fiv_ > 0 ? fiv_ : DEFAULT_FRAME_INTERVAL_TIME; 25 | 26 | } 27 | 28 | 29 | DebugCamera::~DebugCamera() 30 | { 31 | 32 | } 33 | 34 | 35 | void 36 | DebugCamera::getCameraMetadata(CameraMetadata& metadata) 37 | { 38 | metadata.width = image_.cols; 39 | metadata.height = image_.rows; 40 | metadata.fiv = fiv_; 41 | metadata.tile[0] = ParametersProducer::matrixNumX(); 42 | metadata.tile[1] = ParametersProducer::matrixNumY(); 43 | auto diff = std::chrono::duration_cast(std::chrono::system_clock::now() - base_time_); 44 | metadata.seq = diff.count() / fiv_; 45 | } 46 | 47 | 48 | std::shared_ptr 49 | DebugCamera::getCapturedFrame(uint32_t sequenceNo){ 50 | return frame_; 51 | } 52 | -------------------------------------------------------------------------------- /src/producer/Producer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "Camera.h" 16 | #include "Signature.h" 17 | #include "ConsumerCounter.h" 18 | 19 | class Producer : ndn::noncopyable 20 | { 21 | private: 22 | ndn::Face face_; 23 | Camera& camera_; 24 | Signature signature_; 25 | std::vector threads_; 26 | boost::asio::io_service interest_queue_; 27 | std::shared_ptr worker_; 28 | bool running_; 29 | std::string app_prefix_; 30 | ConsumerCounter consumer_counter_; 31 | 32 | ndn::Name meta_data_name_; 33 | ndn::Name img_data_name_; 34 | ndn::Name status_data_name_; 35 | ndn::Name::Component low_quality_component_; 36 | 37 | public: 38 | Producer(Camera& camera, std::string prefix); 39 | ~Producer(); 40 | 41 | void start(); 42 | bool isRunning(){ return running_; }; 43 | 44 | private: 45 | void onInterest(const ndn::InterestFilter& filter, const ndn::Interest& interest); 46 | 47 | void onRegisterFailed(const ndn::Name& prefix, const std::string& reason); 48 | void sendMetaData(const ndn::Name& name); 49 | void sendImageData(const ndn::Interest& name); 50 | void sendStatusData(const ndn::Name& name); 51 | 52 | void processInterest(const ndn::Interest& name); 53 | }; 54 | -------------------------------------------------------------------------------- /src/consumer-js/libs/install-libs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Download required libraries for ICN-360-JS-SERVER." 3 | # ndn-js 4 | curl -O https://raw.githubusercontent.com/named-data/ndn-js/master/build/ndn.min.js 5 | # three-js 6 | curl -O https://raw.githubusercontent.com/mrdoob/three.js/dev/build/three.min.js 7 | curl -O https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js 8 | curl -O https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/js/controls/DeviceOrientationControls.js 9 | curl -O https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/js/effects/StereoEffect.js 10 | curl -O https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/js/vr/WebVR.js 11 | # mdl 12 | curl -O https://code.getmdl.io/1.3.0/mdl.zip 13 | unzip mdl.zip 14 | # dialog 15 | curl -O https://raw.githubusercontent.com/GoogleChrome/dialog-polyfill/master/dist/dialog-polyfill.js 16 | curl -O https://raw.githubusercontent.com/GoogleChrome/dialog-polyfill/master/dialog-polyfill.css 17 | 18 | #meterial-design-icons 19 | curl -O https://raw.githubusercontent.com/google/material-design-icons/master/iconfont/material-icons.css 20 | curl -O https://raw.githubusercontent.com/google/material-design-icons/master/iconfont/MaterialIcons-Regular.eot 21 | curl -O https://raw.githubusercontent.com/google/material-design-icons/master/iconfont/MaterialIcons-Regular.woff2 22 | curl -O https://raw.githubusercontent.com/google/material-design-icons/master/iconfont/MaterialIcons-Regular.woff 23 | curl -O https://raw.githubusercontent.com/google/material-design-icons/master/iconfont/MaterialIcons-Regular.ttf 24 | -------------------------------------------------------------------------------- /src/producer/CvCamera.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "CameraMetadata.h" 19 | #include "CapturedFrame.h" 20 | #include "Camera.h" 21 | 22 | 23 | class CvCamera : public Camera 24 | { 25 | private: 26 | cv::VideoCapture camera_; 27 | cv::Mat frame_; 28 | uint32_t width_, height_; 29 | std::thread thr_; 30 | uint32_t frame_interval_; 31 | bool run_; 32 | bool is_file_; 33 | 34 | std::deque> frame_queue_; 35 | std::mutex mtx_; 36 | 37 | public: 38 | CvCamera(); 39 | virtual ~CvCamera(); 40 | 41 | void start(); 42 | void stop(); 43 | 44 | bool open(int device, uint32_t width = 0, uint32_t height = 0, uint32_t fps = 0); 45 | bool open(const std::string &file); 46 | 47 | inline uint32_t width() const { return width_; } 48 | inline uint32_t height() const { return height_; } 49 | 50 | std::shared_ptr getCapturedFrame(uint32_t sequenceNo); 51 | void getCameraMetadata(CameraMetadata& metadata); 52 | 53 | private: 54 | virtual bool initCamera(uint32_t init_width, uint32_t init_height, uint32_t init_fps); 55 | virtual void capture(); 56 | 57 | CvCamera(const CvCamera &c); 58 | CvCamera &operator=(const CvCamera &w); 59 | }; 60 | -------------------------------------------------------------------------------- /src/producer/CapturedFrame.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "MediaTile.h" 19 | 20 | class CapturedFrame { 21 | private: 22 | static uint32_t next_sequence_; 23 | 24 | std::mutex mtx_; 25 | std::chrono::system_clock::time_point startTime_; 26 | std::vector frame_vector_; 27 | 28 | uint32_t sequence_; 29 | size_t width_; 30 | size_t height_; 31 | 32 | std::unordered_map> tiles_; 33 | 34 | public: 35 | CapturedFrame(size_t width, size_t height); 36 | virtual ~CapturedFrame(); 37 | 38 | void append(const cv::Mat mat); 39 | 40 | int elapsedTime(){ return std::chrono::duration_cast(std::chrono::system_clock::now() - startTime_).count(); }; 41 | size_t tileNum(){ return frame_vector_.size(); }; 42 | 43 | uint32_t sequenceNo() const { return sequence_; }; 44 | static uint32_t currentSequenceNo() { return (next_sequence_ - 1); }; 45 | 46 | void writeTiles(); 47 | std::shared_ptr getMediaTile(int x, int y, bool low_quality); 48 | 49 | protected: 50 | std::shared_ptr createNewVideoTile(int x, int y, bool low_quality); 51 | std::shared_ptr createNewImageTile(int x, int y, bool low_quality); 52 | }; 53 | -------------------------------------------------------------------------------- /src/producer/mainProducer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include "CvCamera.h" 13 | #include "Producer.h" 14 | #include "ParametersProducer.h" 15 | #include "Constants.h" 16 | 17 | 18 | int 19 | main(int argc, char** argv) 20 | { 21 | 22 | if(ParametersProducer::getInstance().parseProgramOptions(argc, argv) == false){ 23 | return EXIT_FAILURE; 24 | } 25 | const std::string& input_device = ParametersProducer::inputDevice(); 26 | std::unique_ptr camera = nullptr; 27 | 28 | auto cv_camera = std::unique_ptr(new CvCamera()); 29 | try { 30 | int device_id = boost::lexical_cast(input_device); 31 | if (!cv_camera->open(device_id, ParametersProducer::cameraWidth(), ParametersProducer::cameraHeight(), 0)){ 32 | std::cerr << "Can't open capture device:" << device_id << std::endl; 33 | return EXIT_FAILURE; 34 | } 35 | }catch(const boost::bad_lexical_cast& e){ 36 | if (!cv_camera->open(input_device)){ 37 | std::cerr << "Can't open capture device:.\n"; 38 | return EXIT_FAILURE; 39 | } 40 | } 41 | cv_camera->start(); 42 | camera = std::move(cv_camera); 43 | 44 | 45 | std::string app_prefix = std::string(APP_PREFIX); 46 | if(ParametersProducer::appPrefix() != "") 47 | app_prefix = ParametersProducer::appPrefix(); 48 | std::cout << app_prefix << std::endl; 49 | Producer producer(*camera, app_prefix); 50 | try { 51 | sleep(3); 52 | producer.start(); 53 | /*while(producer.isRunning()){ 54 | sleep(10); 55 | } 56 | producer.stop();*/ 57 | }catch (const std::exception& e) { 58 | std::cerr << "ERROR: " << e.what() << std::endl; 59 | } 60 | return EXIT_SUCCESS; 61 | } -------------------------------------------------------------------------------- /src/producer/ImageTile.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "ParametersProducer.h" 16 | #include "ImageTile.h" 17 | 18 | namespace fs = boost::filesystem; 19 | namespace ipc = boost::interprocess; 20 | 21 | const size_t MAX_SEGMENT_SIZE = ndn::MAX_NDN_PACKET_SIZE >> 1; 22 | 23 | 24 | ImageTile::ImageTile(const std::string& tile_id, std::vector&& frames, int mw, int mh) 25 | : MediaTile(tile_id, std::move(frames), mw, mh), 26 | file_size_(0), 27 | encode_(false) 28 | { 29 | // const auto filename = tile_id + ParametersProducer::mediaExtension(); 30 | // filepath_ = fs::path(ParametersProducer::workingDir()/filename); 31 | } 32 | 33 | ImageTile::~ImageTile() 34 | { 35 | /* boost::system::error_code error; 36 | if(fs::exists(filepath_, error)){ 37 | fs::remove(filepath_); 38 | } 39 | */ 40 | } 41 | 42 | void 43 | ImageTile::encode() 44 | { 45 | std::lock_guard lock(mtx_); 46 | if (encode_) return; 47 | 48 | auto frame = frames_.back(); 49 | std::vector param = { CV_IMWRITE_JPEG_QUALITY, ParametersProducer::quality() }; 50 | image_ = std::make_unique>(); 51 | cv::imencode(".jpg", frame, *image_, param); 52 | encode_ = true; 53 | } 54 | 55 | int 56 | ImageTile::fileSize() 57 | { 58 | encode(); 59 | return (file_size_ > 0) ? file_size_ : (file_size_ = image_->size()); 60 | } 61 | 62 | std::shared_ptr> 63 | ImageTile::getSegment(uint32_t segment_no) 64 | { 65 | auto filesize = fileSize(); 66 | size_t offset = MAX_SEGMENT_SIZE * segment_no; 67 | if (offset > filesize) return nullptr; 68 | 69 | auto buffer = std::make_shared>(0); 70 | buffer->reserve(MAX_SEGMENT_SIZE); 71 | 72 | auto it = image_->begin() + offset; 73 | auto end_it = it + MAX_SEGMENT_SIZE; 74 | if (end_it > image_->end()) end_it = image_->end(); 75 | std::copy(it, end_it, std::back_inserter(*buffer)); 76 | 77 | return buffer; 78 | } 79 | 80 | -------------------------------------------------------------------------------- /src/consumer-js/js/rtt-statistics.js: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | 10 | RttStatistics = function(){ 11 | this.nSent = 0; 12 | this.nReceived = 0; 13 | this.nTimeout = 0; 14 | this.nNacked = 0; 15 | this.nMarked = 0; 16 | this.sumRtt = 0; 17 | this.minRtt = Number.MAX_SAFE_INTEGER; 18 | this.maxRtt = 0; 19 | // This works when the name of interest and data are exactly the same 20 | this.timeContainer = {}; 21 | this.canSchedule = true; 22 | } 23 | 24 | RttStatistics.prototype.printStatus = function(){ 25 | 26 | let strout = this.nSent + " pkts transmitted, " + this.nReceived + " received
" + this.nTimeout + " timeout (" + this.nNacked + "/" + this.nMarked + " Nack/Mark, " + (100*(this.nSent-this.nReceived)/this.nSent).toFixed(3) + "% lost)
RTT avg/min/max = " + (this.sumRtt/this.nReceived).toFixed(3) + "/" + this.minRtt + "/" + this.maxRtt + "(ms)"; 27 | document.getElementById('i360-status').innerHTML = strout; 28 | this.canSchedule = true; 29 | } 30 | 31 | RttStatistics.prototype.afterSendInterest = function(interest){ 32 | this.nSent += 1; 33 | now = + new Date(); 34 | this.timeContainer[interest.getName().toUri()] = now; 35 | } 36 | RttStatistics.prototype.afterReceiveData = function(interest, data){ 37 | this.nReceived += 1; 38 | now = + new Date(); 39 | name = interest.getName().toUri(); 40 | let diff = now - this.timeContainer[name]; 41 | delete this.timeContainer[name]; 42 | this.appendRtt(diff); 43 | if(data.getCongestionMark() != 0){ 44 | this.nMarked += 1; 45 | } 46 | } 47 | RttStatistics.prototype.afterTimeout = function(interest){ 48 | this.nTimeout += 1; 49 | delete this.timeContainer[interest.getName().toUri()]; 50 | } 51 | RttStatistics.prototype.afterReceiveNack = function(interest, nack){ 52 | this.nNacked += 1; 53 | delete this.timeContainer[interest.getName().toUri()]; 54 | } 55 | 56 | RttStatistics.prototype.appendRtt = function(rtt){ 57 | this.sumRtt += rtt; 58 | if (this.minRtt > rtt){ 59 | this.minRtt = rtt; 60 | } 61 | if (this.maxRtt < rtt){ 62 | this.maxRtt = rtt; 63 | } 64 | if (this.canSchedule){ 65 | this.canSchedule = false; 66 | setTimeout(this.printStatus.bind(this), 6000); 67 | } 68 | } -------------------------------------------------------------------------------- /src/producer/VideoTile.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "ParametersProducer.h" 16 | #include "VideoTile.h" 17 | 18 | namespace fs = boost::filesystem; 19 | namespace ipc = boost::interprocess; 20 | 21 | const size_t MAX_SEGMENT_SIZE = ndn::MAX_NDN_PACKET_SIZE >> 1; 22 | 23 | 24 | VideoTile::VideoTile(const std::string& tile_id, std::vector&& frames, int mw, int mh) 25 | : MediaTile(tile_id, std::move(frames), mw, mh), 26 | file_size_(0), 27 | encode_(false) 28 | { 29 | const auto filename = tile_id + ParametersProducer::mediaExtension(); 30 | filepath_ = fs::path(ParametersProducer::workingDir()/filename); 31 | } 32 | 33 | VideoTile::~VideoTile() 34 | { 35 | boost::system::error_code error; 36 | if(fs::exists(filepath_, error)){ 37 | fs::remove(filepath_); 38 | } 39 | } 40 | 41 | void 42 | VideoTile::encode() 43 | { 44 | std::lock_guard lock(mtx_); 45 | if (encode_) return; 46 | 47 | double fps = static_cast(frames_.size()) / (static_cast(ParametersProducer::videoSegmentSize()) / 1000.0f); 48 | auto writer = cv::VideoWriter(filepath_.string(), ParametersProducer::videoCodec(), fps, cv::Size(width_, height_)); 49 | for(auto f : frames_){ 50 | writer << f; 51 | } 52 | writer.release(); 53 | encode_ = true; 54 | } 55 | 56 | int 57 | VideoTile::fileSize() 58 | { 59 | encode(); 60 | return (file_size_ > 0) ? file_size_ : (file_size_ = fs::file_size(filepath_)); 61 | } 62 | 63 | std::shared_ptr> 64 | VideoTile::getSegment(uint32_t segment_no) 65 | { 66 | auto filesize = fileSize(); 67 | size_t offset = MAX_SEGMENT_SIZE * segment_no; 68 | if (offset > filesize) return nullptr; 69 | size_t size = (offset + MAX_SEGMENT_SIZE) > filesize ? (filesize - offset) : MAX_SEGMENT_SIZE; 70 | 71 | ipc::file_mapping map(filepath_.c_str(), ipc::read_only); 72 | ipc::mapped_region view(map,ipc::read_only, offset, size); 73 | 74 | auto buffer = std::make_shared>(view.get_size()); 75 | memcpy(buffer->data(), view.get_address(), view.get_size()); 76 | 77 | return buffer; 78 | } 79 | 80 | -------------------------------------------------------------------------------- /src/producer/Signature.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include "Signature.h" 26 | 27 | static const uint8_t HMAC_KEY[] = { 28 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 29 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f 30 | }; 31 | 32 | Signature::Signature(const char* prefix) 33 | : sign_type_(ParametersProducer::signatureType()) 34 | { 35 | has_key_ = sign_type_.hasType(SignatureType::RSA) || sign_type_.hasType(SignatureType::ECDSA); 36 | if(has_key_){ 37 | identity_ = key_chain_.createIdentity(prefix); 38 | if(sign_type_.hasType(SignatureType::RSA)){ 39 | key_ = key_chain_.createKey(identity_, ndn::RsaKeyParams(sign_type_.keySize())); 40 | }else if(sign_type_.hasType(SignatureType::ECDSA)){ 41 | key_ = key_chain_.createKey(identity_, ndn::EcKeyParams(sign_type_.keySize())); 42 | } 43 | signature_info_ = ndn::security::signingByKey(key_); 44 | }else if(sign_type_ == SignatureType::SHA_256){ 45 | signature_info_ = ndn::security::signingWithSha256(); 46 | } 47 | } 48 | 49 | 50 | Signature::~Signature() 51 | { 52 | if(has_key_){ 53 | key_chain_.deleteKey(identity_, key_); 54 | key_chain_.deleteIdentity(identity_); 55 | } 56 | } 57 | 58 | 59 | 60 | void 61 | Signature::sign(ndn::Data& data) 62 | { 63 | // Digest 64 | if(sign_type_ == SignatureType::NO_SIGN){ 65 | return; 66 | }else{ 67 | key_chain_.sign(data, signature_info_); 68 | } 69 | } 70 | 71 | void 72 | Signature::signWithHmac(ndn::Data& data) 73 | { 74 | data.setSignature(ndn::Signature(signature_info_.getSignatureInfo())); 75 | ndn::OBufferStream os; 76 | 77 | ndn::EncodingBuffer encoder; 78 | data.wireEncode(encoder, true); 79 | 80 | ndn::security::transform::bufferSource(encoder.buf(), encoder.size()) >> 81 | ndn::security::transform::hmacFilter(ndn::DigestAlgorithm::SHA256, HMAC_KEY, sizeof(HMAC_KEY)) >> 82 | ndn::security::transform::streamSink(os); 83 | 84 | data.wireEncode(encoder, ndn::Block(ndn::tlv::SignatureValue, os.buf())); 85 | } -------------------------------------------------------------------------------- /src/consumer-js/js/fieldofview.js: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | 10 | FieldOfView = function(){ 11 | if(!(this instanceof FieldOfView)){ return new FieldOfView(); } 12 | 13 | this.mx = undefined; 14 | this.my = undefined; 15 | 16 | this.animationInterval = 0; 17 | this.fov = []; 18 | this.geometry = []; 19 | } 20 | 21 | FieldOfView.prototype.setTileSize = function(mx, my){ 22 | this.mx = mx; 23 | this.my = my; 24 | } 25 | 26 | FieldOfView.prototype.setAnimationInterval = function(p){ 27 | this.animationInterval = p; 28 | } 29 | 30 | FieldOfView.prototype.animationInterval = function(p){ 31 | return(this.animationInterval); 32 | } 33 | 34 | 35 | FieldOfView.prototype.createGeometry = function(radius){ 36 | if (typeof this.mx == 'undefined' || typeof this.my == 'undefined') { 37 | return false; 38 | } 39 | 40 | let pi_2 = Math.PI * 2; 41 | 42 | for(let iy = 0; iy <= this.my; iy++){ 43 | let v = iy / this.my; 44 | for(let ix = 0; ix <= this.mx; ix++){ 45 | let u = ix / this.mx; 46 | 47 | let x = radius * Math.cos(u * pi_2) * Math.sin(v * Math.PI); 48 | let y = radius * Math.cos(v * Math.PI); 49 | let z = radius * Math.sin(u * pi_2) * Math.sin(v * Math.PI); 50 | this.geometry.push(new THREE.Vector3(x, y, z)); 51 | } 52 | } 53 | } 54 | 55 | FieldOfView.prototype.calcFieldOfView = function(radius, camera){ 56 | function gridToPos(x, y, mx){ 57 | return x + (y * (mx + 1)); 58 | } 59 | 60 | if (this.geometry.length == 0){ 61 | if(!this.createGeometry(radius)){ 62 | return false; 63 | } 64 | } 65 | 66 | camera.updateMatrix(); 67 | camera.updateMatrixWorld(); 68 | 69 | let frustum = new THREE.Frustum(); 70 | frustum.setFromMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)); 71 | let flag = []; 72 | for(let i = 0; i < this.geometry.length; i ++){ 73 | flag.push(frustum.containsPoint(this.geometry[i]) ? 1 : 0); 74 | } 75 | let fov = []; 76 | for(let iy = 0; iy < this.my; iy++){ 77 | for(let ix = 0; ix < this.mx; ix++){ 78 | let weight = flag[gridToPos(ix, iy, this.mx)] + flag[gridToPos(ix+1, iy, this.mx)] 79 | + flag[gridToPos(ix, iy+1, this.mx)] + flag[gridToPos(ix+1, iy+1, this.mx)]; 80 | if(weight > 2){ 81 | fov.push({x: ix, y: iy}); 82 | }else if(weight > 0){ 83 | fov.push({x: ix, y: iy, edge: true}); 84 | } 85 | } 86 | } 87 | this.fov = fov; 88 | } 89 | 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | icn-360 2 | === 3 | 360 degree video streaming producer/consumer on NDN. 4 | 5 | ### Required Library 6 | 7 | Required libraries can be installed from default repository of each distribution 8 | 9 | - [libopencv-dev](https://github.com/opencv/opencv) : OpenCV3 10 | - [libcereal-dev](https://github.com/USCiLab/cereal) : Serialization (serialize METAFILE to JSON) 11 | - [ndn-cxx](https://github.com/named-data) : NDN C++ library 12 | 13 | Homebrew installs OpenCV4 as default. Please use `opencv@3` instead of `opencv` 14 | 15 | 16 | ### How to use 17 | #### producer 18 | 19 | ``` 20 | producer [-i device_id] [-m matrix_size] [-n app_prefix] [-r resolution] [-s signature_mode] [-t thread_num] [-f frame_interval] [-l] [-w working_dir] [-c codec] 21 | ``` 22 | 23 | |Option|Description|Default| 24 | |:--:|:--|--:| 25 | |i|Device ID of video device. /dev/video or video filename/URL | 0| 26 | |m|The number of divisions of a video frame. ( * )|10| 27 | |n|The name prefix of produced video. | /icn2020.org/theta| 28 | |r|Camera Resolution (4k: 3840x1920, THETA-S-L: 1920x1080) See ParametersProducer.cpp for more option |-| 29 | |s|Signature type. (SHA_256, RSA_2048, ECDSA_224) See ParametersProducer.cpp for more option |SHA_256| 30 | |t|The number of threads. The default number is the same as the number of CPU cores|-| 31 | |f| Time length of on content object [ms]. |1000| 32 | |l|Playback loop option for video file |false| 33 | |w|Working directory for video encoding |/tmp/i360| 34 | |c|Video codec (JPEG, MPEG, H264) |JPEG| 35 | 36 | #### consumer-js 37 | 38 | Please read [`src/consumer-js/README.md`](/src/consumer-js/README.md) 39 | 40 | ### License 41 | 42 | This software is released under the MIT License, see LICENSE.txt. 43 | 44 | ### Contributors 45 | 46 | - Mayutan Arumaithurai 47 | - Jacopo De Benedetto 48 | - Andrea Detti 49 | - Giulio Rossi 50 | 51 | ### References 52 | 1. A. Tagami, K. Ueda, R. Lukita, J. D. Benedetto, M. Arumaithurai, G. Rossi, A. Detti and T. Hasegawa, "[Tile-based Panoramic Live Video Streaming on ICN](https://ieeexplore.ieee.org/document/8756899)," In Proceedings of IEEE International Conference on Communications Workshops, ICC Workshops RAFNET, pp. 1 - 6, Shanghai, China, May 2019. 53 | 1. A. Tagami, "[Demo: Panoramic Video Streaming via Edge-Computing and ICN](https://datatracker.ietf.org/meeting/interim-2018-icnrg-02/session/icnrg)," ICNRG Interim Meeting , Montreal, Canada, Jul. 2018. 54 | 1. "[e360: Panoramic Video Streaming via Edge-Computing and Future Internet](http://www.icn2020.org/e360/)," CEBIT 2018, Hannover, Germany, Jun. 2018. 55 | 1. A. Tagami, K. Ueda, R. Lukita, J. D. Benedetto, M. Arumaithurai, G. Rossi and A. Detti, "[Demo: Edge Transcoding with Name-based Routing](https://dl.acm.org/citation.cfm?doid=3267955.3269008)," In Proceedings of ACM Conference on Information-Centric Networking, ACM ICN, pp. 218 - 219, Boston, MA, Sep. 2018. 56 | 1. K. Ueda, Y. Ishugaki, A. Tagami and T. Hasegawa, "[Demo: Panoramic Streaming using Named Tiles](https://dl.acm.org/citation.cfm?doid=3125719.3132093)," In Proceedings of ACM Conference on Information-Centric Networking, ACM ICN, pp. 204 - 205, Berlin, Germany, Sep. 2017. 57 | 58 | -------------------------------------------------------------------------------- /src/consumer-js/js/fixedratedownloader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | 10 | FixedRateDownloader = function(face, name, rttStatistics, mime, callback){ 11 | this.face = face; 12 | this.name = name; 13 | this.callback_ = callback; 14 | this.finalSegmentNumber = undefined; 15 | this.segmentStore_ = []; 16 | this.rttStatistics = rttStatistics; 17 | this.mime = mime; 18 | this.segmentNumber = 0; 19 | } 20 | 21 | FixedRateDownloader.prototype.startDownload = function(){ 22 | for(let i=0; i 0){ 34 | this.finalSegmentNumber = data.getMetaInfo().getFinalBlockId().toSegment(); 35 | } 36 | this.rttStatistics.afterReceiveData(interest, data); 37 | 38 | let segment_no = data.getName().get(-1).toSegment(); 39 | this.segmentStore_[segment_no] = data.getContent().buf(); 40 | 41 | if(this.finalSegmentNumber < Object.keys(this.segmentStore_).length){ 42 | let sumLength = 0; 43 | for(let i=0; i 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "Constants.h" 16 | #include "ParametersProducer.h" 17 | #include "VideoTile.h" 18 | #include "ImageTile.h" 19 | #include "CapturedFrame.h" 20 | 21 | 22 | namespace fs = boost::filesystem; 23 | 24 | uint32_t CapturedFrame::next_sequence_ = 0; 25 | 26 | CapturedFrame::CapturedFrame(size_t width, size_t height) 27 | : width_(width), height_(height) 28 | { 29 | sequence_ = next_sequence_++; 30 | startTime_ = std::chrono::system_clock::now(); 31 | } 32 | 33 | CapturedFrame::~CapturedFrame(){ 34 | 35 | } 36 | 37 | void 38 | CapturedFrame::append(const cv::Mat mat){ 39 | frame_vector_.push_back(mat); 40 | } 41 | 42 | void 43 | CapturedFrame::writeTiles(){ 44 | } 45 | 46 | 47 | std::shared_ptr 48 | CapturedFrame::getMediaTile(int x, int y, bool low_quality){ 49 | int matrix_x = ParametersProducer::matrixNumX(); 50 | int matrix_y = ParametersProducer::matrixNumY(); 51 | bool is_image = ParametersProducer::mediaCodec().isImage(); 52 | 53 | if (x < 0 || y < 0 || x >= matrix_x || y >= matrix_y) return nullptr; 54 | 55 | std::lock_guard locker(mtx_); 56 | 57 | std::shared_ptr tile; 58 | if (low_quality){ 59 | if(is_image){ 60 | tile = createNewImageTile(x, y, low_quality); 61 | }else{ 62 | tile = createNewVideoTile(x, y, low_quality); 63 | } 64 | }else{ 65 | auto tile_id = MediaTile::tileId(sequence_, x, y, low_quality); 66 | auto it = tiles_.find(tile_id); 67 | if (it != tiles_.end()){ 68 | tile = it->second; 69 | }else{ 70 | if(is_image){ 71 | tile = createNewImageTile(x, y, low_quality); 72 | }else{ 73 | tile = createNewVideoTile(x, y, low_quality); 74 | } 75 | tiles_[tile_id] = tile; 76 | } 77 | } 78 | return tile; 79 | } 80 | 81 | 82 | std::shared_ptr 83 | CapturedFrame::createNewVideoTile(int x, int y, bool low_quality){ 84 | int matrix_x = ParametersProducer::matrixNumX(); 85 | int matrix_y = ParametersProducer::matrixNumY(); 86 | 87 | int mw = width_ / matrix_x; 88 | int mh = height_ / matrix_y; 89 | int lx = x * mw; 90 | int ly = y * mh; 91 | 92 | std::vector frames; 93 | int width = mw; int height = mh; 94 | for(auto it : frame_vector_){ 95 | cv::Mat roi_img(it, cv::Rect(lx, ly, mw, mh)); 96 | if(low_quality){ 97 | cv::resize(roi_img, roi_img, cv::Size(), 0.707, 0.707); 98 | width = roi_img.cols; height = roi_img.rows; 99 | } 100 | frames.push_back(roi_img); 101 | } 102 | 103 | auto tile = std::make_shared(MediaTile::tileId(sequence_, x, y, low_quality), std::move(frames), width, height); 104 | return tile; 105 | } 106 | 107 | 108 | 109 | std::shared_ptr 110 | CapturedFrame::createNewImageTile(int x, int y, bool low_quality){ 111 | int matrix_x = ParametersProducer::matrixNumX(); 112 | int matrix_y = ParametersProducer::matrixNumY(); 113 | 114 | int mw = width_ / matrix_x; 115 | int mh = height_ / matrix_y; 116 | int lx = x * mw; 117 | int ly = y * mh; 118 | 119 | std::vector frames; 120 | int width = mw; int height = mh; 121 | auto it = frame_vector_.back(); 122 | cv::Mat roi_img(it, cv::Rect(lx, ly, mw, mh)); 123 | if(low_quality){ 124 | cv::resize(roi_img, roi_img, cv::Size(), 0.707, 0.707); 125 | width = roi_img.cols; height = roi_img.rows; 126 | } 127 | frames.push_back(roi_img); 128 | 129 | auto tile = std::make_shared(MediaTile::tileId(sequence_, x, y, low_quality), std::move(frames), width, height); 130 | return tile; 131 | } 132 | -------------------------------------------------------------------------------- /src/consumer-js/js/panorama.js: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | 10 | PanoramaView = function(container_id, field_of_view) { 11 | if(!(this instanceof PanoramaView)){ return new PanoramaView(container_id, canvas_id, field_of_view); } 12 | 13 | this.enableVR = ('xr' in navigator && 'supportsSession' in navigator.xr) || ('getVRDisplays' in navigator); 14 | 15 | this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1100); 16 | this.camera.target = new THREE.Vector3(0,0,0); 17 | this.camera.position.set(0,0,0.1); 18 | 19 | this.scene = new THREE.Scene(); 20 | this.render = new THREE.WebGLRenderer(); 21 | this.render.setPixelRatio(window.devicePixelRatio); 22 | this.render.setSize(window.innerWidth, window.innerHeight); 23 | 24 | this.effect = new THREE.StereoEffect(this.render); 25 | let container = document.getElementById(container_id); 26 | container.appendChild(this.render.domElement); 27 | if(this.enableVR){ 28 | this.render.vr.enabled = true; 29 | container.appendChild(THREE.WEBVR.createButton(this.render, {referenceSpaceType:'local'})); 30 | } 31 | this.surface = null; 32 | this.initScene(); 33 | if(!this.enableVR){ 34 | this.initControl(); 35 | } 36 | this.start_time = new Date(); 37 | 38 | this.field_of_view = field_of_view; 39 | this.consumer = undefined; 40 | } 41 | 42 | PanoramaView.prototype.initScene = function(){ 43 | let geometry = new THREE.SphereBufferGeometry( 500, 60, 40 ); 44 | geometry.scale( - 1, 1, 1 ); 45 | 46 | let canvas = document.createElement('canvas'); 47 | canvas.width = '640'; 48 | canvas.height = '480'; 49 | this.surface = canvas; 50 | this.texture = new THREE.Texture(canvas); 51 | this.texture.needsUpdate = true; 52 | this.texture.minFilter = THREE.LinearFilter; 53 | let material = new THREE.MeshBasicMaterial( { 54 | map: this.texture 55 | } ); 56 | this.mesh = new THREE.Mesh( geometry, material ); 57 | this.scene.add(this.mesh); 58 | window.addEventListener('resize', this.onWindowResize.bind(this), false); 59 | } 60 | 61 | PanoramaView.prototype.initControl = function(){ 62 | 63 | let ua = navigator.userAgent; 64 | 65 | let prevCamera = this.camera; 66 | 67 | this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1100); 68 | this.camera.target = new THREE.Vector3(0,0,0); 69 | this.camera.position.copy( prevCamera.position ); 70 | this.camera.rotation.copy( prevCamera.rotation ); 71 | 72 | if(Parameters.DEVICE_ORIENTATION && ((ua.indexOf('iPhone') > 0 || ua.indexOf('iPod') > 0 || ua.indexOf('iPad') > 0 || ua.indexOf('Android') > 0)) ) { 73 | this.controls = new THREE.DeviceOrientationControls(this.camera, this.render.domElement); 74 | } else { 75 | this.controls = new THREE.OrbitControls(this.camera, this.render.domElement); 76 | } 77 | 78 | } 79 | 80 | 81 | PanoramaView.prototype.setConsumer = function(consumer){ 82 | this.consumer = consumer; 83 | } 84 | 85 | 86 | PanoramaView.prototype.animate = function(){ 87 | if(this.enableVR){ 88 | this.render.setAnimationLoop(this.animate.bind(this)); 89 | }else{ 90 | window.requestAnimationFrame(this.animate.bind(this)); 91 | } 92 | this.update(); 93 | this.field_of_view.setAnimationInterval((new Date()) - this.start_time); 94 | this.start_time = new Date(); 95 | } 96 | 97 | 98 | PanoramaView.prototype.update = function(){ 99 | if(this.consumer !== undefined && this.consumer.isUpdate()){ 100 | this.mesh.material.map.needsUpdate = true; 101 | } 102 | if(Parameters.VR_MODE){ 103 | this.effect.render(this.scene, this.camera); 104 | }else{ 105 | this.render.render(this.scene, this.camera); 106 | } 107 | if(!this.enableVR){ 108 | this.controls.update(); 109 | } 110 | this.field_of_view.calcFieldOfView(500, this.camera); 111 | } 112 | 113 | 114 | PanoramaView.prototype.onWindowResize = function(){ 115 | this.camera.aspect = window.innerWidth / window.innerHeight; 116 | this.camera.updateProjectionMatrix(); 117 | 118 | this.render.setSize( window.innerWidth, window.innerHeight ); 119 | this.effect.setSize( window.innerWidth, window.innerHeight ); 120 | } 121 | 122 | 123 | PanoramaView.prototype.getSurface = function(){ 124 | console.log(this.surface); 125 | return this.surface; 126 | } -------------------------------------------------------------------------------- /src/producer/ParametersProducer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #pragma once 10 | #include 11 | #include 12 | 13 | 14 | // For CvCamera/DebugCamera 15 | constexpr size_t FRAME_BUFFER_LENGTH = 10; // 5 * frameInterval 16 | constexpr uint32_t DEFAULT_FRAME_INTERVAL_TIME = 70; 17 | constexpr uint32_t FRAME_INTERVAL_REFRESH = 10000; // 10000 ms (10s) 18 | constexpr uint32_t METADATA_UPDATE_INTERVAL = 1000; // 1000 ms (1s) 19 | 20 | constexpr int RSA_SIGNATURE_SIZE = 1024; 21 | 22 | struct SignatureType { 23 | enum Type : unsigned int { 24 | NO_SIGN = 0, 25 | SHA = 1 << 0, 26 | RSA = 1 << 1, 27 | ECDSA = 1 << 2, 28 | HMAC = 1 << 3, 29 | KEY_1 = 0x80 << 0, 30 | KEY_2 = 0x80 << 1, 31 | KEY_3 = 0x80 << 2, 32 | SHA_256 = SHA | KEY_1, 33 | RSA_2048 = RSA | KEY_2, 34 | ECDSA_224 = ECDSA | KEY_1, 35 | ECDSA_256 = ECDSA | KEY_2, 36 | ECDSA_384 = ECDSA | KEY_3, 37 | HMAC_SHA_256 = HMAC | KEY_1 38 | } type_; 39 | SignatureType(unsigned int v) : type_(static_cast(v)) {}; 40 | operator Type() {return type_;}; 41 | bool hasType(SignatureType type) {return ((type_ & type.type_) == type.type_); }; 42 | static SignatureType signatureFromKey(std::string key); 43 | int keySize(); 44 | }; 45 | 46 | struct MediaCodicType { 47 | enum MediaType { 48 | JPEG, MP4, H264 49 | } type_; 50 | int fourcc_; 51 | std::string extention_; 52 | 53 | MediaCodicType(MediaType v); 54 | static MediaCodicType mediaCodicFromName(std::string name); 55 | operator MediaType() {return type_; }; 56 | bool isImage() {return type_ == JPEG; }; 57 | const int fourcc() { return fourcc_; }; 58 | const std::string extension() { return extention_; }; 59 | const std::string mime(){ 60 | switch(type_){ 61 | case JPEG: 62 | return "image/jpeg"; 63 | break; 64 | case MP4: 65 | case H264: 66 | return "video/mp4"; 67 | break; 68 | } 69 | } 70 | }; 71 | 72 | // Singleton Design Pattern 73 | class ParametersProducer 74 | { 75 | private: 76 | std::string input_device_; 77 | int camera_width_; 78 | int camera_height_; 79 | int matrix_num_x_; 80 | int matrix_num_y_; 81 | int quality_; 82 | int thread_count_; 83 | std::string debug_filename_; 84 | SignatureType signature_type_; 85 | std::string app_prefix_; 86 | int video_segment_size_; 87 | boost::filesystem::path working_dir_; 88 | bool is_loop_; 89 | MediaCodicType media_codec_; 90 | 91 | public: 92 | static ParametersProducer& getInstance(){ 93 | static ParametersProducer instance; 94 | return instance; 95 | } 96 | 97 | bool parseProgramOptions(int argc, char** argv); 98 | 99 | static const std::string& inputDevice() { return ParametersProducer::getInstance().input_device_; }; 100 | static int cameraWidth() { return ParametersProducer::getInstance().camera_width_; }; 101 | static int cameraHeight() { return ParametersProducer::getInstance().camera_height_; }; 102 | static int matrixNumX() { return ParametersProducer::getInstance().matrix_num_x_; }; 103 | static int matrixNumY() { return ParametersProducer::getInstance().matrix_num_y_; }; 104 | static int quality() { return ParametersProducer::getInstance().quality_; }; 105 | static int threadCount() { return ParametersProducer::getInstance().thread_count_; }; 106 | static std::string& debugFilename() { return ParametersProducer::getInstance().debug_filename_; }; 107 | static SignatureType signatureType() { return ParametersProducer::getInstance().signature_type_; }; 108 | static std::string& appPrefix() { return ParametersProducer::getInstance().app_prefix_; }; 109 | static int videoSegmentSize() { return ParametersProducer::getInstance().video_segment_size_; }; 110 | static const boost::filesystem::path& workingDir() { return ParametersProducer::getInstance().working_dir_; }; 111 | static bool isLoop() { return ParametersProducer::getInstance().is_loop_; }; 112 | 113 | static MediaCodicType mediaCodec() { return ParametersProducer::getInstance().media_codec_; }; 114 | static int videoCodec() { return ParametersProducer::getInstance().media_codec_.fourcc(); }; 115 | static const std::string mediaExtension() { return ParametersProducer::getInstance().media_codec_.extension(); }; 116 | 117 | private: 118 | ParametersProducer(); 119 | ~ParametersProducer() = default; 120 | 121 | bool makeWorkingDir(std::string dirname); 122 | void setCameraResolutionFromString(std::string s); 123 | void setVideoCodecFromString(std::string s); 124 | 125 | }; 126 | -------------------------------------------------------------------------------- /src/consumer-js/js/framedownloader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | 10 | FrameDownloader = function(face, metadata, rttStatistics){ 11 | if(!(this instanceof FrameDownloader)){ return new FrameDownloader(hostname, port, field_of_view); } 12 | 13 | this.face = face; 14 | this.metadata = metadata; 15 | this.download_start_time = null; 16 | this.download_queue = []; 17 | this.remaining_tiles_num = 0; 18 | this.media_array = new Array(); 19 | this.rttStatistics = rttStatistics; 20 | this.cb = undefined; 21 | } 22 | 23 | 24 | FrameDownloader.prototype.downloadFrame = function(sequence_number, fov, callback){ 25 | this.download_start_time = new Date(); 26 | this.sequence_number = sequence_number; 27 | this.cb = callback; 28 | 29 | let tile_num = this.metadata.width * this.metadata.height + 1; 30 | this.download_queue = [].concat(fov.sort(function(a,b){ 31 | return (((a.edge === undefined) ? tile_num : 0) + a.x + a.y) < (((b.edge === undefined) ? tile_num : 0) + b.x + b.y)})); 32 | this.remaining_tiles_num = this.download_queue.length; 33 | 34 | for(let i=0; i 0){ 41 | let it = this.download_queue.shift(); 42 | if(Parameters.HTTP_MODE == false){ 43 | this.downloadByNDN(it); 44 | }else{ 45 | // this.downloadByHTTP(it); // 46 | } 47 | } 48 | } 49 | 50 | FrameDownloader.prototype.downloadByNDN = function(tile){ 51 | let name = new Name(Parameters.IMG_DATA_PREFIX); 52 | if(tile.edge !== undefined && tile.edge == true && Parameters.EDGE_MARK == true){ 53 | name.append("f:transcoding"); 54 | } 55 | name.append(Name.Component.fromNumber(this.sequence_number)); 56 | name.append(Name.Component.fromNumber(tile.x)); 57 | name.append(Name.Component.fromNumber(tile.y)); 58 | 59 | let downloader = new FixedRateDownloader(this.face, name, this.rttStatistics, this.metadata.mime, this.onGetTiledata.bind(this, tile.x, tile.y)); 60 | downloader.startDownload(); 61 | } 62 | 63 | FrameDownloader.prototype.onGetTiledata = function(x, y, imageData){ 64 | let mx = (this.metadata.width * x) / this.metadata.tile.value0; 65 | let my = (this.metadata.height * y) / this.metadata.tile.value1; 66 | let wx = this.metadata.width / this.metadata.tile.value0; 67 | let wy = this.metadata.height / this.metadata.tile.value1; 68 | 69 | this.remaining_tiles_num--; 70 | if(imageData !== undefined){ 71 | let loadMediaData; 72 | if(this.metadata.mime.startsWith('video/')){ 73 | loadMediaData = (url) => { 74 | return new Promise((resolve, reject) => { 75 | let video = document.createElement("video"); 76 | video.preload = "none"; 77 | video.muted = true; 78 | video.loop = false; 79 | video.oncanplaythrough = () => { 80 | video.play(); 81 | video.pause(); 82 | video.onerror = null; 83 | resolve(video); 84 | } 85 | video.onerror = (e) => reject(e); 86 | video.src = url; 87 | }) 88 | } 89 | }else{ 90 | loadMediaData = (url) => { 91 | return new Promise((resolve, reject) => { 92 | let image = new Image; 93 | image.onload = () => resolve(image); 94 | image.onerror = (e) => reject(e); 95 | image.src = url; 96 | }) 97 | } 98 | } 99 | 100 | loadMediaData(imageData).then(res => { 101 | this.appendMedia(this.sequence_number, x, y, res); 102 | 103 | if(this.remaining_tiles_num <= 0){ 104 | this.cb(this); 105 | } 106 | }).catch( e => { 107 | console.log(e); 108 | URL.revokeObjectURL(imageData); 109 | if(this.remaining_tiles_num <= 0){ 110 | this.cb(this); 111 | } 112 | }) 113 | } 114 | 115 | if(this.remaining_tiles_num > 0){ 116 | setTimeout(this.downloadNextTiledata(), 0); 117 | } 118 | } 119 | 120 | FrameDownloader.prototype.getMediaArray = function() { 121 | return this.media_array; 122 | } 123 | 124 | FrameDownloader.prototype.appendMedia = function(sequence_number, x, y, data) { 125 | let key_name = String(x) + "," + String(y); 126 | this.media_array[key_name] = data; 127 | } 128 | 129 | FrameDownloader.prototype.revoke = function(sequence_number, x, y, data) { 130 | for(var key_name in this.media_array){ 131 | let src = this.media_array[key_name].src; 132 | if(src.startsWith("blob:")){ 133 | URL.revokeObjectURL(src); 134 | } 135 | }; 136 | } 137 | -------------------------------------------------------------------------------- /src/producer/CvCamera.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "ParametersProducer.h" 16 | #include "CvCamera.h" 17 | 18 | 19 | CvCamera::CvCamera() 20 | : camera_(), 21 | frame_(), 22 | frame_interval_(), 23 | run_(false), 24 | is_file_(false) 25 | { 26 | } 27 | 28 | CvCamera::~CvCamera() 29 | { 30 | stop(); 31 | } 32 | 33 | bool 34 | CvCamera::initCamera(uint32_t init_width, uint32_t init_height, uint32_t init_fps) 35 | { 36 | if (init_width > 0) camera_.set(CV_CAP_PROP_FRAME_WIDTH, init_width); 37 | if (init_height > 0) camera_.set(CV_CAP_PROP_FRAME_HEIGHT, init_height); 38 | if (init_fps > 0) camera_.set(CV_CAP_PROP_FPS, init_fps); 39 | 40 | if (camera_.grab()) 41 | { 42 | width_ = static_cast(camera_.get(CV_CAP_PROP_FRAME_WIDTH)); 43 | height_ = static_cast(camera_.get(CV_CAP_PROP_FRAME_HEIGHT)); 44 | if (width_ == 0) width_ = init_width; 45 | if (height_ == 0) height_ = init_height; 46 | 47 | frame_.release(); 48 | camera_.retrieve(frame_, 3); 49 | 50 | uint32_t fps = static_cast(camera_.get(CV_CAP_PROP_FPS)); 51 | frame_interval_ = (fps > 0 && fps < 1000) ? static_cast(1000 / fps) : DEFAULT_FRAME_INTERVAL_TIME; 52 | 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | 59 | void 60 | CvCamera::capture() 61 | { 62 | auto frame_count = static_cast(camera_.get(CV_CAP_PROP_FRAME_COUNT)); 63 | bool is_loop = (frame_count > 0) && ParametersProducer::isLoop(); 64 | 65 | std::shared_ptr captured_frame; 66 | 67 | auto frame_time = std::chrono::system_clock::now() + std::chrono::milliseconds(ParametersProducer::videoSegmentSize()); 68 | while (run_) { 69 | auto start = std::chrono::system_clock::now(); 70 | if (camera_.grab()) 71 | { 72 | frame_.release(); 73 | camera_.retrieve(frame_); 74 | 75 | if(captured_frame.get() == nullptr || 76 | std::chrono::system_clock::now() >= frame_time){ 77 | 78 | if(captured_frame.get() != nullptr){ 79 | std::lock_guard locker(mtx_); 80 | while(frame_queue_.size() >= FRAME_BUFFER_LENGTH){ 81 | frame_queue_.pop_back(); 82 | } 83 | frame_queue_.push_front(captured_frame); 84 | } 85 | frame_time = frame_time + std::chrono::milliseconds(ParametersProducer::videoSegmentSize()); 86 | captured_frame = std::make_shared(width_, height_); 87 | } 88 | captured_frame->append(frame_); 89 | 90 | if(is_file_){ 91 | auto end = std::chrono::system_clock::now(); 92 | uint32_t elapsed = std::chrono::duration_cast(end - start).count(); 93 | if(frame_interval_ > elapsed){ 94 | std::this_thread::sleep_for(std::chrono::milliseconds(frame_interval_ - elapsed)); 95 | } 96 | } 97 | 98 | } else { 99 | if(is_loop){ 100 | //if(static_cast(camera_.get(CV_CAP_PROP_POS_FRAMES)) == frame_count){ 101 | camera_.set(CV_CAP_PROP_POS_FRAMES, 0); 102 | //} 103 | } 104 | std::this_thread::sleep_for(std::chrono::milliseconds(1L)); 105 | } 106 | } 107 | } 108 | 109 | 110 | void 111 | CvCamera::start() 112 | { 113 | run_ = true; 114 | thr_ = std::thread([this](){ this->capture(); }); 115 | } 116 | 117 | void 118 | CvCamera::stop() 119 | { 120 | if (run_) 121 | { 122 | run_ = false; 123 | thr_.join(); 124 | } 125 | } 126 | 127 | bool 128 | CvCamera::open(int device, uint32_t width, uint32_t height, uint32_t fps) 129 | { 130 | camera_.open(device); 131 | is_file_ = false; 132 | if (camera_.isOpened() && initCamera(width, height, fps)) 133 | return true; 134 | 135 | return false; 136 | } 137 | 138 | 139 | bool 140 | CvCamera::open(const std::string &file) 141 | { 142 | camera_.open(file); 143 | is_file_ = true; 144 | if (camera_.isOpened() && initCamera(0, 0, 0)) return true; 145 | 146 | return false; 147 | } 148 | 149 | void 150 | CvCamera::getCameraMetadata(CameraMetadata& metadata) 151 | { 152 | metadata.width = width_; 153 | metadata.height = height_; 154 | metadata.fiv = ParametersProducer::videoSegmentSize(); 155 | metadata.tile[0] = ParametersProducer::matrixNumX(); 156 | metadata.tile[1] = ParametersProducer::matrixNumY(); 157 | metadata.seq = CapturedFrame::currentSequenceNo(); 158 | metadata.mime = ParametersProducer::mediaCodec().mime(); 159 | metadata.fps = ParametersProducer::mediaCodec().isImage() ? metadata.fiv : frame_interval_; 160 | } 161 | 162 | 163 | std::shared_ptr 164 | CvCamera::getCapturedFrame(uint32_t sequenceNo){ 165 | std::lock_guard locker(mtx_); 166 | auto frame = std::find_if(frame_queue_.begin(), frame_queue_.end(), [&sequenceNo](const std::shared_ptr i){ 167 | return i->sequenceNo() == sequenceNo; 168 | }); 169 | return (frame == frame_queue_.end()) ? nullptr : *frame; 170 | } 171 | -------------------------------------------------------------------------------- /src/consumer-js/js/consumer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | Consumer = function(hostname, port, field_of_view, surface){ 10 | if(!(this instanceof Consumer)){ return new Consumer(hostname, port, field_of_view); } 11 | 12 | this.face = new Face({host:hostname, port:port}); 13 | this.metadata = undefined; 14 | 15 | this.base_time = undefined; 16 | this.field_of_view = field_of_view; 17 | this.sequence_number = 0; 18 | 19 | this.surface = surface; 20 | this.clientID = Math.floor((Math.random() * 1000000) + 1); 21 | this.rttStatistics = new RttStatistics(); 22 | 23 | this.frame_downloader = []; 24 | this.current_frame = undefined; 25 | 26 | this.renew_surface = false; 27 | this.frame_time = 0; 28 | 29 | this.priv_interval_time = undefined; 30 | 31 | this.surface_timer = undefined; 32 | 33 | this.animation_rate = 0; 34 | this.is_video_tile = false; 35 | 36 | Interest.setDefaultCanBePrefix(true); 37 | } 38 | 39 | Consumer.prototype.updateMetaData = function(){ 40 | let name = new Name(Parameters.MATA_DATA_PREFIX); 41 | name.append(Math.random().toString(36).slice(-8)); 42 | let request = new Interest(name); 43 | request.setMustBeFresh(true); 44 | new_base_time = new Date(); 45 | 46 | this.face.expressInterest(request, 47 | (interest, data) => { 48 | this.rttStatistics.afterReceiveData(interest, data); 49 | let string = new TextDecoder('utf-8').decode(data.content); 50 | let first = (typeof this.metadata === 'undefined'); 51 | this.metadata = JSON.parse(string).root; 52 | if(first){ 53 | this.surface.setAttribute('width', this.metadata.width); 54 | this.surface.setAttribute('height', this.metadata.height); 55 | }; 56 | this.field_of_view.setTileSize(this.metadata.tile.value0, this.metadata.tile.value1); 57 | this.base_time = new_base_time; 58 | 59 | this.is_video_tile = this.metadata.mime.startsWith('video/'); 60 | setTimeout(this.updateMetaData.bind(this), Parameters.METADATA_UPDATE_INTERVAL); 61 | }, 62 | (interest, data) => { 63 | this.rttStatistics.afterTimeout(interest); 64 | setTimeout(this.updateMetaData.bind(this), Parameters.METADATA_UPDATE_INTERVAL); 65 | } 66 | ); 67 | 68 | this.rttStatistics.afterSendInterest(request); 69 | } 70 | 71 | Consumer.prototype.downloadImagedata = function(){ 72 | if(typeof this.metadata == 'undefined' || this.field_of_view.fov.length == 0){ 73 | setTimeout(this.downloadImagedata.bind(this), Parameters.DOWNLOAD_WAIT_INTERVAL); 74 | } else { 75 | let start_time = new Date(); 76 | let diff = Math.max(start_time - this.base_time, 0); 77 | let next_sequence_number = Math.max(Math.floor(diff / this.metadata.fiv) + this.metadata.seq - 1, 0); 78 | if(this.sequence_number == next_sequence_number){ 79 | console.log("skip"); 80 | setTimeout(this.downloadImagedata.bind(this), Parameters.DOWNLOAD_WAIT_INTERVAL); 81 | return; 82 | } 83 | var downloader = new FrameDownloader(this.face, this.metadata, this.rttStatistics); 84 | downloader.downloadFrame(next_sequence_number, this.field_of_view.fov, this.onGetFramedata.bind(this, next_sequence_number)); 85 | this.sequence_number = next_sequence_number; 86 | this.frame_downloader.push(downloader); 87 | setTimeout(this.downloadImagedata.bind(this), this.metadata.fiv); 88 | } 89 | } 90 | 91 | 92 | 93 | Consumer.prototype.onGetFramedata = function(sequence_number, downloader){ 94 | if(this.surface_timer === undefined){ 95 | this.updateSurface(); 96 | 97 | } 98 | } 99 | 100 | 101 | Consumer.prototype.updateSurface = function(){ 102 | if(this.frame_downloader.length > 0){ 103 | let downloader = this.current_frame; 104 | this.current_frame = this.frame_downloader.shift(); 105 | if(downloader !== undefined){ 106 | downloader.revoke(); 107 | } 108 | this.priv_interval_time = undefined; 109 | if(!this.is_video_tile){ 110 | this.update(); 111 | } 112 | } 113 | this.surface_timer = setTimeout(this.updateSurface.bind(this), this.metadata.fiv); 114 | } 115 | 116 | Consumer.prototype.isUpdate = function(){ 117 | if(this.is_video_tile){ 118 | this.update(); 119 | } 120 | if(this.renew_surface){ 121 | this.renew_surface = false; 122 | return true; 123 | }else{ 124 | return false; 125 | } 126 | } 127 | 128 | Consumer.prototype.update = function(){ 129 | if(this.current_frame !== undefined){ 130 | let tiles = this.current_frame.getMediaArray(); 131 | //this.current_frame = undefined; 132 | if(this.priv_interval_time === undefined){ 133 | this.priv_interval_time = new Date(); 134 | } 135 | this.frame_time = ((new Date()).getTime() - this.priv_interval_time.getTime()); 136 | if(this.frame_time > this.metadata.fiv){ 137 | this.frame_time = this.metadata.fiv - 1; 138 | } 139 | this.frame_time = this.frame_time / 1000.0; 140 | 141 | let ctx = this.surface.getContext('2d'); 142 | for(let name in tiles){ 143 | if(this.is_video_tile){ tiles[name].currentTime = this.frame_time; }; 144 | let loc = name.split(','); 145 | let x = parseInt(loc[0]); 146 | let y = parseInt(loc[1]); 147 | let mx = (this.metadata.width * x) / this.metadata.tile.value0; 148 | let my = (this.metadata.height * y) / this.metadata.tile.value1; 149 | let wx = this.metadata.width / this.metadata.tile.value0; 150 | let wy = this.metadata.height / this.metadata.tile.value1; 151 | ctx.drawImage(tiles[name], mx, my, wx, wy); 152 | if(Parameters.SHOW_GRID == true){ 153 | ctx.strokeStyle = 'rgb(255,255,255)'; 154 | ctx.strokeRect(mx, my, wx, wy); 155 | } 156 | } 157 | 158 | this.renew_surface = true; 159 | }else{ 160 | console.log("null frame?"); 161 | } 162 | } 163 | 164 | 165 | Consumer.prototype.setSequenceNumber = function(seq){ 166 | this.current_sequence = seq; 167 | } 168 | -------------------------------------------------------------------------------- /src/producer/ParametersProducer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "ParametersProducer.h" 16 | 17 | namespace fs = boost::filesystem; 18 | 19 | ParametersProducer::ParametersProducer() 20 | : camera_width_(0), 21 | camera_height_(0), 22 | matrix_num_x_(10), 23 | matrix_num_y_(10), 24 | quality_(70), 25 | thread_count_(std::thread::hardware_concurrency()), 26 | debug_filename_(""), 27 | signature_type_(SignatureType::SHA_256), 28 | app_prefix_(""), 29 | video_segment_size_(1000), 30 | working_dir_(""), 31 | is_loop_(false), 32 | media_codec_(MediaCodicType::JPEG) 33 | { 34 | 35 | } 36 | 37 | bool 38 | ParametersProducer::parseProgramOptions(int argc, char** argv) 39 | { 40 | namespace po = boost::program_options; 41 | po::options_description desc("360 degree video streaming producer on NDN"); 42 | desc.add_options() 43 | ("help,h", "produce help message") 44 | ("input,i", po::value()->default_value("0"), "camera's device id or video filename") 45 | ("matrix,m", po::value()->default_value(10), "matrix size") 46 | ("prefix-name,n", po::value()->default_value("/icn2020.org/theta"), "name prefix of produced video") 47 | ("resolution,r", po::value()->default_value("NONE"), "camera resolution") 48 | ("signature-type,s", po::value()->default_value("SHA_256"), "signature type") 49 | ("threads,t", po::value()->default_value(std::thread::hardware_concurrency() - 1), "the number of threads") 50 | ("video-segment-size,f", po::value()->default_value(500), "time length of one content object [ms]") 51 | ("playback-loop,l", "playback loop option for video file") 52 | ("work-dir,w", po::value()->default_value("/tmp/i360"), "working directory") 53 | ("codec,c", po::value()->default_value("jpeg"), "video codec"); 54 | 55 | po::variables_map vm; 56 | try { 57 | po::store(po::parse_command_line(argc, argv, desc), vm); 58 | } catch (const po::error_with_option_name& e) { 59 | std::cout << e.what() << std::endl; 60 | return false; 61 | } 62 | po::notify(vm); 63 | 64 | if (vm.count("help")) { 65 | std::cout << desc <(); 70 | matrix_num_x_ = matrix_num_y_ = vm["matrix"].as(); 71 | app_prefix_ = vm["prefix-name"].as(); 72 | setCameraResolutionFromString(vm["resolution"].as()); 73 | signature_type_ = SignatureType::signatureFromKey(vm["signature-type"].as()); 74 | thread_count_ = vm["threads"].as(); 75 | video_segment_size_ = vm["video-segment-size"].as(); 76 | is_loop_ = vm.count("playback-loop") ? true : false; 77 | setVideoCodecFromString(vm["codec"].as()); 78 | if(!makeWorkingDir(vm["work-dir"].as())){ 79 | return false; 80 | } 81 | } catch(const boost::bad_any_cast& e) { 82 | std::cout << e.what() << std::endl; 83 | return false; 84 | } 85 | return true; 86 | } 87 | 88 | 89 | bool 90 | ParametersProducer::makeWorkingDir(std::string dirname) 91 | { 92 | boost::system::error_code error; 93 | 94 | working_dir_ = boost::filesystem::path(dirname); 95 | if(! fs::exists(working_dir_)) { 96 | if(!fs::create_directories(working_dir_, error)){ 97 | std::cerr << "Cannot create directory: " << working_dir_ << std::endl; 98 | return false; 99 | } 100 | } 101 | if(!fs::is_directory(working_dir_)){ 102 | std::cerr << working_dir_ << "is not a directory" << std::endl; 103 | return false; 104 | } 105 | return true; 106 | } 107 | 108 | void 109 | ParametersProducer::setCameraResolutionFromString(std::string s){ 110 | typedef std::pair Resolution; 111 | const std::map resolutionName = { 112 | {"NONE", Resolution(0, 0)}, 113 | {"4k", Resolution(3840, 1920)}, 114 | {"4K", Resolution(3840, 1920)}, 115 | {"RICOH-R", Resolution(1920, 960)}, 116 | {"THETA-V", Resolution(1920, 960)}, 117 | {"THETA-S-L", Resolution(1920, 960)}, 118 | {"THETA-S-M", Resolution(1280, 720)} 119 | }; 120 | auto it = resolutionName.find(s); 121 | if(it != resolutionName.end()){ 122 | camera_width_ = it->second.first; 123 | camera_height_ = it->second.second; 124 | }else{ 125 | std::cerr << "Unknown resolution : " << s << std::endl; 126 | } 127 | } 128 | 129 | SignatureType 130 | SignatureType::signatureFromKey(std::string key) 131 | { 132 | const std::map signatureName = { 133 | {"0", SHA_256}, 134 | // {"1", RSA_1024}, 135 | {"2", RSA_2048}, 136 | {"SHA_256", SHA_256}, 137 | // {"RSA_1024", RSA_1024}, 138 | {"RSA_2048", RSA_2048}, 139 | {"ECDSA_224", ECDSA_224}, 140 | {"ECDSA_256", ECDSA_256}, 141 | {"ECDSA_384", ECDSA_384} 142 | }; 143 | auto it = signatureName.find(key); 144 | if(it != signatureName.end()){ 145 | return it->second; 146 | }else{ 147 | std::cerr << "Cannot find signature : " << key << std::endl; 148 | return NO_SIGN; 149 | } 150 | } 151 | 152 | int 153 | SignatureType::keySize() 154 | { 155 | int ks; 156 | switch(type_){ 157 | case RSA_2048: 158 | ks = 2048; 159 | break; 160 | case ECDSA_224: 161 | ks = 224; 162 | break; 163 | case ECDSA_256: 164 | ks = 256; 165 | break; 166 | case ECDSA_384: 167 | ks = 384; 168 | break; 169 | default: 170 | ks = 0; 171 | break; 172 | } 173 | return ks; 174 | } 175 | 176 | 177 | void 178 | ParametersProducer::setVideoCodecFromString(std::string s){ 179 | media_codec_ = MediaCodicType::mediaCodicFromName(s); 180 | } 181 | 182 | 183 | MediaCodicType::MediaCodicType(MediaType t) 184 | : type_(t) 185 | { 186 | switch(type_){ 187 | case JPEG: 188 | extention_ = ".jpg"; 189 | fourcc_ = 0; 190 | break; 191 | case MP4: 192 | extention_ = ".mp4"; 193 | fourcc_ = cv::VideoWriter::fourcc('m', 'p', '4', 'v'); 194 | break; 195 | case H264: 196 | extention_ = ".mp4"; 197 | fourcc_ = cv::VideoWriter::fourcc('a', 'v', 'c', '1'); 198 | break; 199 | default: 200 | extention_ = ".bim"; 201 | fourcc_ = 0; 202 | break; 203 | } 204 | } 205 | 206 | 207 | MediaCodicType 208 | MediaCodicType::mediaCodicFromName(std::string name) 209 | { 210 | const std::map mediaCodic = { 211 | {"JPEG", JPEG}, 212 | {"jpeg", JPEG}, 213 | {"JPG", JPEG}, 214 | {"jpg", JPEG}, 215 | {"MP4", MP4}, 216 | {"mp4", MP4}, 217 | {"h264", H264}, 218 | {"H264", H264}, 219 | }; 220 | auto it = mediaCodic.find(name); 221 | if(it != mediaCodic.end()){ 222 | return it->second; 223 | }else{ 224 | std::cerr << "Cannot find codic : " << name << std::endl; 225 | return JPEG; 226 | } 227 | } -------------------------------------------------------------------------------- /src/producer/Producer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * icn-360 - 360 degree video streaming producer/consumer on NDN 3 | * 4 | * Copyright (c) 2019 KDDI Research, Inc. 5 | * 6 | * This software is released under the MIT License. 7 | * http://opensource.org/licenses/mit-license.php 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "ParametersProducer.h" 25 | #include "CapturedFrame.h" 26 | #include "Constants.h" 27 | #include "CameraMetadata.h" 28 | #include "ProducerStatusData.h" 29 | #include "MediaTile.h" 30 | #include "Producer.h" 31 | 32 | /* 33 | const auto APP_PREFIX_NAME = ndn::Name(APP_PREFIX); 34 | const auto META_DATA_NAME = ndn::Name(META_DATA_PATH); 35 | const auto IMG_DATA_NAME = ndn::Name(IMG_DATA_PATH); 36 | */ 37 | 38 | Producer::Producer(Camera& camera, std::string prefix) 39 | : camera_(camera), 40 | signature_(prefix.c_str()), 41 | threads_(ParametersProducer::threadCount()), 42 | interest_queue_(), 43 | worker_(nullptr), 44 | running_(true), 45 | app_prefix_(prefix), 46 | consumer_counter_() 47 | { 48 | meta_data_name_ = ndn::Name(app_prefix_).append(META_DATA_SUFFIX); 49 | img_data_name_ = ndn::Name(app_prefix_).append(IMG_DATA_SUFFIX); 50 | status_data_name_ = ndn::Name(app_prefix_).append(STATUS_DATA_SUFFIX); 51 | low_quality_component_ = ndn::Name::Component(LOW_DATA_PREFIX); 52 | 53 | //std::cout << "META_NAME: " << meta_data_name_.toUri() << ", IMG_NAME: " << img_data_name_.toUri() << std::endl; 54 | } 55 | 56 | 57 | 58 | Producer::~Producer() 59 | { 60 | 61 | } 62 | 63 | void 64 | Producer::start() 65 | { 66 | running_ = true; 67 | face_.setInterestFilter(app_prefix_, 68 | std::bind(&Producer::onInterest, this, _1, _2), 69 | ndn::RegisterPrefixSuccessCallback(), 70 | std::bind(&Producer::onRegisterFailed, this, _1, _2)); 71 | 72 | if(worker_ != nullptr){ 73 | worker_.reset(); 74 | } 75 | worker_ = std::make_shared(interest_queue_); 76 | 77 | for (int i = 0; i < ParametersProducer::threadCount(); i++) { 78 | threads_[i] = std::thread( [this]{ this->interest_queue_.run(); }); 79 | } 80 | face_.processEvents(); 81 | } 82 | 83 | 84 | void 85 | Producer::onInterest(const ndn::InterestFilter& filter, const ndn::Interest& interest) 86 | { 87 | interest_queue_.post([this, interest]{ this->processInterest(interest); }); 88 | } 89 | 90 | 91 | void 92 | Producer::processInterest(const ndn::Interest& interest) 93 | { 94 | const ndn::Name& name = interest.getName(); 95 | 96 | if(meta_data_name_.isPrefixOf(name) == true){ 97 | consumer_counter_.increase(); 98 | sendMetaData(name); 99 | }else if(img_data_name_.isPrefixOf(name) == true){ 100 | sendImageData(interest); 101 | }else if(status_data_name_.isPrefixOf(name) == true){ 102 | sendStatusData(name); 103 | }else{ 104 | return; 105 | } 106 | } 107 | 108 | void 109 | Producer::sendMetaData(const ndn::Name& name) 110 | { 111 | if (name[NAME_SEGMENT_LOCATION].isSegment() && name[NAME_SEGMENT_LOCATION].toSegment() > 0) return; 112 | CameraMetadata metadata; 113 | camera_.getCameraMetadata(metadata); 114 | metadata.mui = METADATA_UPDATE_INTERVAL; 115 | 116 | std::stringstream ss; 117 | { 118 | cereal::JSONOutputArchive o_archive(ss); 119 | o_archive(cereal::make_nvp("root", metadata)); 120 | } 121 | 122 | std::shared_ptr data = std::make_shared(); 123 | data->setName(name); 124 | data->setFreshnessPeriod(ndn::time::milliseconds(0)); 125 | #if NDN_CXX_VERSION > 6002 126 | data->setFinalBlock(ndn::name::Component::fromSegment(0)); 127 | #else 128 | data->setFinalBlockId(ndn::name::Component::fromSegment(0)); 129 | #endif 130 | const std::string content = ss.str(); 131 | data->setContent(reinterpret_cast(content.c_str()), content.size()); 132 | 133 | signature_.sign(*data); 134 | face_.put(*data); 135 | } 136 | 137 | 138 | void 139 | Producer::sendStatusData(const ndn::Name& name) 140 | { 141 | if (name[NAME_SEGMENT_LOCATION].isSegment() && name[NAME_SEGMENT_LOCATION].toSegment() > 0) return; 142 | 143 | ProducerStatusData status; 144 | status.consumers = consumer_counter_.get(); 145 | 146 | std::stringstream ss; 147 | { 148 | cereal::JSONOutputArchive o_archive(ss); 149 | o_archive(cereal::make_nvp("root", status)); 150 | } 151 | 152 | std::shared_ptr data = std::make_shared(); 153 | data->setName(name); 154 | data->setFreshnessPeriod(ndn::time::milliseconds(0)); 155 | #if NDN_CXX_VERSION > 6002 156 | data->setFinalBlock(ndn::name::Component::fromSegment(0)); 157 | #else 158 | data->setFinalBlockId(ndn::name::Component::fromSegment(0)); 159 | #endif 160 | const std::string content = ss.str(); 161 | data->setContent(reinterpret_cast(content.c_str()), content.size()); 162 | 163 | signature_.sign(*data); 164 | face_.put(*data); 165 | } 166 | 167 | 168 | void 169 | Producer::sendImageData(const ndn::Interest& interest) 170 | { 171 | const ndn::Name& name = interest.getName(); 172 | 173 | if (!name[NAME_COORDINATE_Y_LOCATION].isNumber()) return; 174 | uint32_t y = name[NAME_COORDINATE_Y_LOCATION].toNumber(); 175 | if (!name[NAME_COORDINATE_X_LOCATION].isNumber()) return; 176 | uint32_t x = name[NAME_COORDINATE_X_LOCATION].toNumber(); 177 | if (!name[NAME_SEQUENCE_LOCATION].isNumber()) return; 178 | uint32_t sequence_no = name[NAME_SEQUENCE_LOCATION].toNumber(); 179 | if (!name[NAME_SEGMENT_LOCATION].isSegment()) return; 180 | auto segment_no = name[NAME_SEGMENT_LOCATION].toSegment(); 181 | bool low_quality = name[NAME_QUALITY_LOCATION].equals(low_quality_component_); 182 | //std::cout << name.to << std::endl; 183 | 184 | CameraMetadata metadata; 185 | camera_.getCameraMetadata(metadata); 186 | 187 | auto frame = camera_.getCapturedFrame(sequence_no); 188 | if(frame == nullptr){ 189 | ndn::Interest interest(name); 190 | auto nack = ndn::lp::Nack(interest); 191 | nack.setReason(ndn::lp::NackReason::CONGESTION); 192 | face_.put(nack); 193 | return; 194 | } 195 | 196 | auto image = frame->getMediaTile(x, y, low_quality); 197 | if(frame == nullptr){ 198 | auto nack = ndn::lp::Nack(interest); 199 | nack.setReason(ndn::lp::NackReason::DUPLICATE); 200 | face_.put(nack); 201 | return; 202 | } 203 | 204 | auto segment = image->getSegment(segment_no); 205 | if(segment == nullptr){ 206 | auto nack = ndn::lp::Nack(interest); 207 | nack.setReason(ndn::lp::NackReason::NO_ROUTE); 208 | face_.put(nack); 209 | return; 210 | } 211 | 212 | auto data = std::make_shared(); 213 | data->setName(name); 214 | data->setFreshnessPeriod(ndn::time::milliseconds(metadata.fiv * 10)); 215 | #if NDN_CXX_VERSION > 6002 216 | data->setFinalBlock(ndn::name::Component::fromSegment(image->finalSegmentNo())); 217 | #else 218 | data->setFinalBlockId(ndn::name::Component::fromSegment(image->finalSegmentNo())); 219 | #endif 220 | data->setContent(segment->data(), segment->size()); 221 | 222 | signature_.sign(*data); 223 | face_.put(*data); 224 | } 225 | 226 | 227 | 228 | void 229 | Producer::onRegisterFailed(const ndn::Name& prefix, const std::string& reason) 230 | { 231 | std::cerr << "ERROR: Failed to register prefix \"" 232 | << prefix << "\" in local hub's daemon (" << reason << ")" 233 | << std::endl; 234 | face_.shutdown(); 235 | } 236 | -------------------------------------------------------------------------------- /src/consumer-js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | icn-360 Javascript Consumer 5 | 6 | 7 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 163 | 164 | 165 | 166 |
167 | 168 | 169 | 170 | 171 | 172 | 173 |
174 |
175 |
176 | 180 |
181 |
182 | 186 |
187 |
188 | 192 |
193 |
194 | 198 |
199 |
200 |
201 | 202 |
203 | 204 |
205 |
206 |
207 | ---- icn-360 statistics ---- 208 |
209 |
210 | 213 | 214 | 215 | --------------------------------------------------------------------------------