From 2f4224d7c48b3b22bcb24eb622fe6705fa16375f Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Thu, 15 Feb 2018 19:23:46 +0100 Subject: [PATCH] Add an HTTP endpoint for enumerating channels and one for getting only their colors. Intended for remote tally applications. --- Makefile | 5 +++-- httpd.cpp | 9 +++++++++ httpd.h | 12 ++++++++++++ json.proto | 12 ++++++++++++ mixer.cpp | 29 +++++++++++++++++++++++++++++ mixer.h | 2 ++ 6 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 json.proto diff --git a/Makefile b/Makefile index ff9b4b5..7a97329 100644 --- a/Makefile +++ b/Makefile @@ -25,12 +25,12 @@ AUDIO_MIXER_OBJS = audio_mixer.o alsa_input.o alsa_pool.o ebu_r128_proc.o stereo OBJS += chroma_subsampler.o v210_converter.o mixer.o basic_stats.o metrics.o pbo_frame_allocator.o context.o ref_counted_frame.o theme.o httpd.o flags.o image_input.o alsa_output.o disk_space_estimator.o print_latency.o timecode_renderer.o tweaked_inputs.o $(AUDIO_MIXER_OBJS) # Streaming and encoding objects -OBJS += quicksync_encoder.o x264_encoder.o x264_dynamic.o x264_speed_control.o video_encoder.o metacube2.o mux.o audio_encoder.o ffmpeg_raii.o ffmpeg_util.o +OBJS += quicksync_encoder.o x264_encoder.o x264_dynamic.o x264_speed_control.o video_encoder.o metacube2.o mux.o audio_encoder.o ffmpeg_raii.o ffmpeg_util.o json.pb.o # DeckLink OBJS += decklink_capture.o decklink_util.o decklink_output.o decklink/DeckLinkAPIDispatch.o -KAERU_OBJS = kaeru.o x264_encoder.o mux.o basic_stats.o metrics.o flags.o audio_encoder.o x264_speed_control.o print_latency.o x264_dynamic.o ffmpeg_raii.o ref_counted_frame.o ffmpeg_capture.o ffmpeg_util.o httpd.o metacube2.o +KAERU_OBJS = kaeru.o x264_encoder.o mux.o basic_stats.o metrics.o flags.o audio_encoder.o x264_speed_control.o print_latency.o x264_dynamic.o ffmpeg_raii.o ref_counted_frame.o ffmpeg_capture.o ffmpeg_util.o httpd.o json.pb.o metacube2.o # bmusb ifeq ($(EMBEDDED_BMUSB),yes) @@ -77,6 +77,7 @@ mainwindow.o: ui_mainwindow.h ui_display.h ui_audio_miniview.h ui_audio_expanded mainwindow.o: midi_mapping.pb.h midi_mapper.o: midi_mapping.pb.h midi_mapping_dialog.o: ui_midi_mapping.h midi_mapping.pb.h +httpd.o: json.pb.h DEPS=$(OBJS:.o=.d) $(BM_OBJS:.o=.d) $(KAERU_OBJS:.o=.d) -include $(DEPS) diff --git a/httpd.cpp b/httpd.cpp index 13d2a71..811e095 100644 --- a/httpd.cpp +++ b/httpd.cpp @@ -88,6 +88,15 @@ int HTTPD::answer_to_connection(MHD_Connection *connection, MHD_destroy_response(response); // Only decreases the refcount; actual free is after the request is done. return ret; } + if (endpoints.count(url)) { + pair contents_and_type = endpoints[url](); + MHD_Response *response = MHD_create_response_from_buffer( + contents_and_type.first.size(), &contents_and_type.first[0], MHD_RESPMEM_MUST_COPY); + MHD_add_response_header(response, "Content-type", contents_and_type.second.c_str()); + int ret = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); // Only decreases the refcount; actual free is after the request is done. + return ret; + } HTTPD::Stream *stream = new HTTPD::Stream(this, framing); stream->add_data(header.data(), header.size(), Stream::DATA_TYPE_HEADER); diff --git a/httpd.h b/httpd.h index 630b0c3..878a184 100644 --- a/httpd.h +++ b/httpd.h @@ -9,15 +9,21 @@ #include #include #include +#include #include #include #include +#include +#include struct MHD_Connection; struct MHD_Daemon; class HTTPD { public: + // Returns a pair of content and content-type. + using EndpointCallback = std::function()>; + HTTPD(); ~HTTPD(); @@ -26,6 +32,11 @@ public: header = data; } + // Should be called before start() (due to threading issues). + void add_endpoint(const std::string &url, const EndpointCallback &callback) { + endpoints[url] = callback; + } + void start(int port); void add_data(const char *buf, size_t size, bool keyframe); int64_t get_num_connected_clients() const { @@ -81,6 +92,7 @@ private: MHD_Daemon *mhd = nullptr; std::mutex streams_mutex; std::set streams; // Not owned. + std::unordered_map endpoints; std::string header; // Metrics. diff --git a/json.proto b/json.proto new file mode 100644 index 0000000..ae10a7f --- /dev/null +++ b/json.proto @@ -0,0 +1,12 @@ +// Messages used to produce JSON (it's the simplest way we can create valid +// JSON without pulling in an external JSON library). + +message Channels { + repeated Channel channel = 1; +} + +message Channel { + required int32 index = 1; + required string name = 2; + required string color = 3; +} diff --git a/mixer.cpp b/mixer.cpp index 4deba02..70175a1 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -51,6 +51,10 @@ #include "v210_converter.h" #include "video_encoder.h" +#undef Status +#include +#include "json.pb.h" + class IDeckLink; class QOpenGLContext; @@ -352,6 +356,13 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) // Must be instantiated after VideoEncoder has initialized global_flags.use_zerocopy. theme.reset(new Theme(global_flags.theme_filename, global_flags.theme_dirs, resource_pool.get(), num_cards)); + httpd.add_endpoint("/channels", bind(&Mixer::get_channels_json, this)); + for (int channel_idx = 2; channel_idx < theme->get_num_channels(); ++channel_idx) { + char url[256]; + snprintf(url, sizeof(url), "/channels/%d/color", channel_idx); + httpd.add_endpoint(url, bind(&Mixer::get_channel_color_http, this, unsigned(channel_idx))); + } + // Start listening for clients only once VideoEncoder has written its header, if any. httpd.start(global_flags.http_port); @@ -1096,6 +1107,24 @@ void Mixer::trim_queue(CaptureCard *card, size_t safe_queue_length) #endif } +pair Mixer::get_channels_json() +{ + Channels ret; + for (int channel_idx = 2; channel_idx < theme->get_num_channels(); ++channel_idx) { + Channel *channel = ret.add_channel(); + channel->set_index(channel_idx); + channel->set_name(theme->get_channel_name(channel_idx)); + channel->set_color(theme->get_channel_color(channel_idx)); + } + string contents; + google::protobuf::util::MessageToJsonString(ret, &contents); // Ignore any errors. + return make_pair(contents, "text/json"); +} + +pair Mixer::get_channel_color_http(unsigned channel_idx) +{ + return make_pair(theme->get_channel_color(channel_idx), "text/plain"); +} Mixer::OutputFrameInfo Mixer::get_one_frame_from_each_card(unsigned master_card_index, bool master_card_is_output, CaptureCard::NewFrame new_frames[MAX_VIDEO_CARDS], bool has_new_frame[MAX_VIDEO_CARDS]) { diff --git a/mixer.h b/mixer.h index 1d2e200..aa3baac 100644 --- a/mixer.h +++ b/mixer.h @@ -428,6 +428,8 @@ private: void release_display_frame(DisplayFrame *frame); double pts() { return double(pts_int) / TIMEBASE; } void trim_queue(CaptureCard *card, size_t safe_queue_length); + std::pair get_channels_json(); + std::pair get_channel_color_http(unsigned channel_idx); HTTPD httpd; unsigned num_cards, num_video_inputs; -- 2.39.2