201 lines
7.4 KiB
C
201 lines
7.4 KiB
C
![]() |
// ----------------------------------------------------------------------------
|
||
|
// - 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 <api/media_stream_interface.h>
|
||
|
#include <media/base/video_broadcaster.h>
|
||
|
|
||
|
#include "open3d/visualization/webrtc_server/BitmapTrackSource.h"
|
||
|
|
||
|
namespace open3d {
|
||
|
namespace visualization {
|
||
|
namespace webrtc_server {
|
||
|
|
||
|
class VideoScaler : public rtc::VideoSinkInterface<webrtc::VideoFrame>,
|
||
|
public rtc::VideoSourceInterface<webrtc::VideoFrame> {
|
||
|
public:
|
||
|
VideoScaler(rtc::scoped_refptr<BitmapTrackSourceInterface> video_source,
|
||
|
const std::map<std::string, std::string> &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<webrtc::I420Buffer> 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<webrtc::VideoFrame> *sink,
|
||
|
const rtc::VideoSinkWants &wants) override {
|
||
|
video_source_->AddOrUpdateSink(this, wants);
|
||
|
|
||
|
broadcaster_.AddOrUpdateSink(sink, wants);
|
||
|
}
|
||
|
|
||
|
void RemoveSink(
|
||
|
rtc::VideoSinkInterface<webrtc::VideoFrame> *sink) override {
|
||
|
video_source_->RemoveSink(this);
|
||
|
|
||
|
broadcaster_.RemoveSink(sink);
|
||
|
}
|
||
|
|
||
|
int width() { return roi_width_; }
|
||
|
int height() { return roi_height_; }
|
||
|
|
||
|
private:
|
||
|
rtc::scoped_refptr<BitmapTrackSourceInterface> 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
|