// ---------------------------------------------------------------------------- // - Open3D: www.open3d.org - // ---------------------------------------------------------------------------- // Copyright (c) 2018-2023 www.open3d.org // SPDX-License-Identifier: MIT // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // Contains source code from // https://github.com/mpromonet/webrtc-streamer // // This software is in the public domain, furnished "as is", without technical // support, and with no warranty, express or implied, as to its usefulness for // any purpose. // ---------------------------------------------------------------------------- // // This is a private header. It shall be hidden from Open3D's public API. Do not // put this in Open3D.h.in. #pragma once #include #include #include "open3d/visualization/webrtc_server/BitmapTrackSource.h" namespace open3d { namespace visualization { namespace webrtc_server { class VideoScaler : public rtc::VideoSinkInterface, public rtc::VideoSourceInterface { public: VideoScaler(rtc::scoped_refptr video_source, const std::map &opts) : video_source_(video_source), width_(0), height_(0), rotation_(webrtc::kVideoRotation_0), roi_x_(0), roi_y_(0), roi_width_(0), roi_height_(0) { if (opts.find("width") != opts.end()) { width_ = std::stoi(opts.at("width")); } if (opts.find("height") != opts.end()) { height_ = std::stoi(opts.at("height")); } if (opts.find("rotation") != opts.end()) { int rotation = std::stoi(opts.at("rotation")); switch (rotation) { case 90: rotation_ = webrtc::kVideoRotation_90; break; case 180: rotation_ = webrtc::kVideoRotation_180; break; case 270: rotation_ = webrtc::kVideoRotation_270; break; } } if (opts.find("roi_x") != opts.end()) { roi_x_ = std::stoi(opts.at("roi_x")); if (roi_x_ < 0) { utility::LogWarning("Ignore roi_x={}, it must be >=0", roi_x_); roi_x_ = 0; } } if (opts.find("roi_y") != opts.end()) { roi_y_ = std::stoi(opts.at("roi_y")); if (roi_y_ < 0) { utility::LogWarning("Ignore roi_y_={}, it must be >=0", roi_y_); roi_y_ = 0; } } if (opts.find("roi_width") != opts.end()) { roi_width_ = std::stoi(opts.at("roi_width")); if (roi_width_ <= 0) { utility::LogWarning("Ignore roi_width={}, it must be >0", roi_width_); roi_width_ = 0; } } if (opts.find("roi_height") != opts.end()) { roi_height_ = std::stoi(opts.at("roi_height")); if (roi_height_ <= 0) { utility::LogWarning("Ignore roi_height={}, it must be >0", roi_height_); roi_height_ = 0; } } } virtual ~VideoScaler() {} void OnFrame(const webrtc::VideoFrame &frame) override { if (roi_x_ >= frame.width()) { utility::LogWarning( "The ROI position protrudes beyond the right edge of the " "image. Ignore roi_x."); roi_x_ = 0; } if (roi_y_ >= frame.height()) { utility::LogWarning( "The ROI position protrudes beyond the right edge of the " "image. Ignore roi_y_."); roi_y_ = 0; } if (roi_width_ != 0 && (roi_width_ + roi_x_) > frame.width()) { utility::LogWarning( "The ROI position protrudes beyond the right edge of the " "image. Ignore roi_width_."); roi_width_ = 0; } if (roi_height_ != 0 && (roi_height_ + roi_y_) > frame.height()) { utility::LogWarning( "The ROI position protrudes beyond the right edge of the " "image. Ignore roi_height_."); roi_height_ = 0; } if (roi_width_ == 0) { roi_width_ = frame.width() - roi_x_; } if (roi_height_ == 0) { roi_height_ = frame.height() - roi_y_; } // source image is croped but destination image size is not set if ((roi_width_ != frame.width() || roi_height_ != frame.height()) && (height_ == 0 && width_ == 0)) { height_ = roi_height_; width_ = roi_width_; } if ((height_ == 0) && (width_ == 0) && (rotation_ == webrtc::kVideoRotation_0)) { broadcaster_.OnFrame(frame); } else { int height = height_; int width = width_; if ((height == 0) && (width == 0)) { height = frame.height(); width = frame.width(); } else if (height == 0) { height = (roi_height_ * width) / roi_width_; } else if (width == 0) { width = (roi_width_ * height) / roi_height_; } rtc::scoped_refptr scaled_buffer = webrtc::I420Buffer::Create(width, height); if (roi_width_ != frame.width() || roi_height_ != frame.height()) { scaled_buffer->CropAndScaleFrom( *frame.video_frame_buffer()->ToI420(), roi_x_, roi_y_, roi_width_, roi_height_); } else { scaled_buffer->ScaleFrom(*frame.video_frame_buffer()->ToI420()); } webrtc::VideoFrame scaledFrame = webrtc::VideoFrame(scaled_buffer, frame.timestamp(), frame.render_time_ms(), rotation_); broadcaster_.OnFrame(scaledFrame); } } void AddOrUpdateSink(rtc::VideoSinkInterface *sink, const rtc::VideoSinkWants &wants) override { video_source_->AddOrUpdateSink(this, wants); broadcaster_.AddOrUpdateSink(sink, wants); } void RemoveSink( rtc::VideoSinkInterface *sink) override { video_source_->RemoveSink(this); broadcaster_.RemoveSink(sink); } int width() { return roi_width_; } int height() { return roi_height_; } private: rtc::scoped_refptr video_source_; rtc::VideoBroadcaster broadcaster_; int width_; int height_; webrtc::VideoRotation rotation_; int roi_x_; int roi_y_; int roi_width_; int roi_height_; }; } // namespace webrtc_server } // namespace visualization } // namespace open3d