Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
a-rose committed Aug 20, 2024
2 parents b0039f8 + 92d2d05 commit d8428f6
Show file tree
Hide file tree
Showing 19 changed files with 425 additions and 260 deletions.
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

# Change Log
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.2.0] - 2024-08-20

### Added
- feat: a support for multiple active sources in a scene
- feat(client): add command to describe current studio state
- build: add bash functions 'rebuild' and 'start' to use from shell mode.
- feat(Docker): keep dev tools in the dev image

### Changed
- chore: update base image to Ubuntu 22.04 and update build
- chore: update cmake_minimum_required to 3.22
- chore: update obs to v30.2.2
- chore: update gRPC-generated files
- docs: update dev instructions
- refactor(client): code tidying

### Fixed
- fix(Show): fix name in UpdateProto
- fix(bashrc): fix rb alias
- fix(bashrc): fix st alias
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ obs_version:=30.2.3

image:=obs-headless
container:=obs-headless
version:=v2.1.1-obs${obs_version}
version:=v2.2.0-obs${obs_version}
latest:=latest-obs${obs_version}

# TODO
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,5 @@ Using the base image, you can also build obs-studio from sources.
- [client] show usage in cli (e.g. 's' to switch sources)
- [*] various TODOs in the code
- [*] pointers to ref
- [*] switch to Golang
- [*] switch to Golang
- [server] segfault in libsrt when stopping (see https://github.com/Haivision/srt/issues/2770)
67 changes: 67 additions & 0 deletions etc/bashrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,70 @@ obs_headless_src=/usr/local/src/obs-headless
alias cdsrc="cd ${obs_headless_src}"
alias rb="cdsrc && cd build && make -j$(nproc) && make install"
alias st="/opt/obs-headless/etc/docker-entrypoint.sh"

################################################################################

function rebuild() {
pushd .
set -e

OBS_HEADLESS_INSTALL_PATH="/opt/obs-headless"
OBS_INSTALL_PATH="/opt/obs-studio-portable"

cdsrc
echo -e "\033[32mGenerating proto files...\033[0m"
cd proto_gen/
sh proto_gen.sh
cd ..
echo -e "\033[32mPreparing build...\033[0m"
mkdir -p build
cd build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="${OBS_HEADLESS_INSTALL_PATH}" \
-DCMAKE_INSTALL_RPATH="${OBS_HEADLESS_INSTALL_PATH}/lib" \
-DOBS_INSTALL_PATH="${OBS_INSTALL_PATH}"
echo -e "\033[32mBuilding...\033[0m"
make -j$(nproc)
make install

set +e
popd
}

function start {
args=$(getopt -o "hm:" -- "$@")
eval set -- "$args"

mode=""

while [ $# -ge 1 ]; do
case "$1" in
--)
# No more options left.
shift
break
;;
-h)
echo "start [-m <mode>] [-c <config_file>]"
echo " -m <mode> : start mode (default: nothing special)"
echo " gdb: run in gdb"
echo " memcheck: run in valgrind with memory checks"
echo " callgrind: run in valgrind with profiling"
return 1
;;
-m)
mode="$2"
;;
-c)
config_file="$2"
shift
;;
esac

shift
done

clear
MODE=${mode} /opt/obs-headless/etc/docker-entrypoint.sh
}
31 changes: 30 additions & 1 deletion etc/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,39 @@
set -o errexit
set -o nounset

PREFIX=""
DEFAULT_MODE="normal"

OBS_INSTALL_PATH=${OBS_INSTALL_PATH:-"/opt/obs-studio-portable"}
OBS_HEADLESS_INSTALL_PATH=${OBS_HEADLESS_INSTALL_PATH:-"/opt/obs-headless/"}

################################################################################

if [ "$MODE" = 'gdb' ]; then
PREFIX="gdb --args"

elif [ "$MODE" = 'memcheck' ]; then
supp=""
#supp="--gen-suppressions=all --log-file=/root/etc/memcheck.log"
#supp="$supp --suppressions=/root/etc/memcheck_suppressions.txt"

mode="--tool=memcheck --trace-children=yes --leak-check=full \
--leak-resolution=high --show-reachable=yes --track-fds=yes \
--track-origins=yes --num-callers=32 --show-leak-kinds=all \
--error-limit=no $supp"

PREFIX="valgrind $mode"

elif [ "$MODE" = 'callgrind' ]; then
PREFIX="valgrind --tool=callgrind --callgrind-out-file=/tmp/obs-headless.callgrind"
fi

################################################################################

# Run from OBS's directory. Core dumps will be located there.
echo "OBS_INSTALL_PATH: $OBS_INSTALL_PATH"
cd ${OBS_INSTALL_PATH}/bin/64bit/
${OBS_HEADLESS_INSTALL_PATH}/obs_headless_server

echo "PREFIX: $PREFIX"

exec $PREFIX ${OBS_HEADLESS_INSTALL_PATH}/obs_headless_server
Binary file added etc/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added etc/rec.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 22 additions & 13 deletions etc/shows/bigshow.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,42 @@
{
"name": "DefaultShow",
"name": "Big Show",
"scenes": [
{
"name": "scene_0",
"sources": [
{
"name": "source_0",
"name": "video A",
"type": "RTMP",
"url": "rtmp://localhost:1936/source_a"
"url": "rtmp://localhost/sourceA"
},
{
"name": "rec_img",
"type": "Image",
"url": "/opt/obs-headless/etc/rec.png"
},
{
"name": "logo_img",
"type": "Image",
"url": "/opt/obs-headless/etc/logo.png",
"width": 128,
"height": 128
}
]
},
{
"name": "scene_1",
"sources": [
{
"name": "source_1",
"name": "video B",
"type": "RTMP",
"url": "rtmp://localhost:1936/source_a"
}
]
},
{
"name": "scene_2",
"sources": [
"url": "rtmp://localhost/sourceB"
},
{
"name": "source_2",
"name": "video A",
"type": "RTMP",
"url": "rtmp://localhost:1936/source_a"
"url": "rtmp://localhost/sourceA",
"width": 230,
"height": 135
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.22)
project(obs_headless)


Expand Down
19 changes: 16 additions & 3 deletions src/client.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <grpc++/grpc++.h>
#include "lib/proto/studio.grpc.pb.h"
#include "lib/Trace.hpp"
Expand Down Expand Up @@ -75,9 +77,11 @@ int main(int argc, char** argv) {
}
trace_info("Health reply", field(server_timestamp));

client.ShowLoad(OBS_HEADLESS_PATH "/etc/shows/default.json");
// TODO param
string show_path = "/etc/shows/bigshow.json";
client.ShowLoad(OBS_HEADLESS_PATH + show_path);

trace_info("Starting studio");
trace_info("Starting studio with show", field_ns("show", show_path.c_str()));
s = client.StudioStart();
if(!s.ok()) {
throw runtime_error("Failed to start studio: " + s.error_message());
Expand Down Expand Up @@ -137,8 +141,17 @@ void describe_state(StudioClient& client) {
string scene_pre = (scene.id() == show.active_scene_id()) ? "*" : "-";
trace_info(" " + scene_pre +" Scene", field_ns("id", scene.id()), field_ns("name", scene.name()));

std::vector<std::string> active_source_ids;
for(int i=0; i<scene.active_source_ids_size(); i++) {
active_source_ids.push_back(scene.active_source_ids(i));
}

for(auto source : scene.sources()) {
string source_pre = (source.id() == scene.active_source_id()) ? "*" : "-";
string source_pre = "-";
if(std::find(active_source_ids.begin(), active_source_ids.end(), source.id()) != active_source_ids.end()) {
source_pre = "*";
}

trace_info(" " + source_pre +" Source", field_ns("id", source.id()), field_ns("name", source.name()));
}
}
Expand Down
47 changes: 26 additions & 21 deletions src/lib/Scene.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <algorithm>
#include "Scene.hpp"

Scene::Scene(std::string id, std::string name, Settings* settings)
Expand All @@ -6,8 +7,7 @@ Scene::Scene(std::string id, std::string name, Settings* settings)
, started(false)
, obs_scene(nullptr)
, settings(settings)
, source_id_counter(0)
, active_source(nullptr) {
, source_id_counter(0) {
trace_debug("Create Scene", field_s(id), field_s(name));
}

Expand All @@ -27,21 +27,22 @@ Source* Scene::GetSource(std::string source_id) {
}


Source* Scene::AddSource(std::string source_name, SourceType type, std::string source_url) {
Source* Scene::AddSource(std::string source_name, SourceType type, std::string source_url, int width, int height) {
std::string source_id = "source_"+ std::to_string(source_id_counter);
source_id_counter++;

Source* source = new Source(source_id, source_name, type, source_url, settings);
Source* source = new Source(source_id, source_name, type, source_url, width, height, settings);
if(!source) {
trace_error("Failed to create a source", field_s(source_id));
return NULL;
}

trace_debug("Add source", field_s(source_id));
sources[source_id] = source;
if(!active_source) {
active_source = source;// TODO need a setActive method
}

// TODO at the moment, all sources are always active. Add a way to switch
// sources on and off.
active_sources.push_back(source); // TODO need a setActive method
return source;
}

Expand All @@ -52,7 +53,8 @@ Source* Scene::DuplicateSourceFromScene(Scene* scene, std::string source_id) {
return NULL;
}

Source* new_source = AddSource(source->Name(), source->Type(), source->Url());
// TODO width & height
Source* new_source = AddSource(source->Name(), source->Type(), source->Url(), -1, -1);
if(!new_source) {
trace_error("Failed to duplicate source");
return NULL;
Expand All @@ -71,7 +73,8 @@ grpc::Status Scene::RemoveSource(std::string source_id) {
trace_error("Source not found", field_s(source_id));
return grpc::Status(grpc::NOT_FOUND, "Source not found id="+ source_id);
}
if(it->second == active_source) {

if(std::find(active_sources.begin(), active_sources.end(), it->second) != active_sources.end()) {
trace_error("Source is active", field_s(source_id));
return grpc::Status(grpc::FAILED_PRECONDITION, "Source is active id="+ source_id);
}
Expand Down Expand Up @@ -103,10 +106,12 @@ grpc::Status Scene::Start() {
}


s = active_source->Start(&obs_scene);
if(!s.ok()) {
trace_error("source Start failed", error(s.error_message()));
return s;
for (auto & source : active_sources) {
s = source->Start(&obs_scene);
if(!s.ok()) {
trace_error("source Start failed", field_s(source->Id()), error(s.error_message()));
return s;
}
}

started = true;
Expand All @@ -123,10 +128,12 @@ grpc::Status Scene::Stop() {
return grpc::Status(grpc::FAILED_PRECONDITION, "Scene already stopped");
}

s = active_source->Stop();
if(!s.ok()) {
trace_error("Source Stop failed", field_s(id), error(s.error_message()));
return grpc::Status(grpc::NOT_FOUND, "Source Stop failed: "+ s.error_message());
for (auto & source : active_sources) {
s = source->Stop();
if(!s.ok()) {
trace_error("Source Stop failed", field_s(source->Id()), error(s.error_message()));
return s;
}
}

obs_scene_release(obs_scene);
Expand All @@ -140,10 +147,8 @@ grpc::Status Scene::UpdateProto(proto::Scene* proto_scene) {
proto_scene->set_id(id);
proto_scene->set_name(name);

if(active_source) {
proto_scene->set_active_source_id(active_source->Id());
} else {
proto_scene->set_active_source_id("");
for (auto & s : active_sources) {
proto_scene->add_active_source_ids(s->Id());
}

SourceMap::iterator it;
Expand Down
5 changes: 3 additions & 2 deletions src/lib/Scene.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <vector>
#include "Source.hpp"

class Scene {
Expand All @@ -15,7 +16,7 @@ class Scene {

// Methods
Source* GetSource(std::string source_id);
Source* AddSource(std::string source_name, SourceType type, std::string source_url);
Source* AddSource(std::string source_name, SourceType type, std::string source_url, int width, int height);
Source* DuplicateSourceFromScene(Scene* scene, std::string source_id);
Source* DuplicateSource(std::string source_id);
grpc::Status RemoveSource(std::string source_id);
Expand All @@ -29,7 +30,7 @@ class Scene {
bool started;
obs_scene_t* obs_scene;
SourceMap sources;
Source* active_source;
std::vector<Source*> active_sources;
Settings* settings;
uint64_t source_id_counter;
};
Expand Down
Loading

0 comments on commit d8428f6

Please # to comment.