Merge pull request #371 from Palakis/pause-recording

Recording Pause support
This commit is contained in:
Stéphane Lepin
2019-09-03 21:28:55 +02:00
committed by GitHub
8 changed files with 215 additions and 64 deletions

View File

@ -22,6 +22,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include <obs.hpp> #include <obs.hpp>
#include <util/platform.h>
#include "obs-websocket.h" #include "obs-websocket.h"
#include "Utils.h" #include "Utils.h"
@ -764,3 +766,51 @@ obs_data_array_t* Utils::GetSourceFiltersList(obs_source_t* source, bool include
return enumParams.filters; return enumParams.filters;
} }
void getPauseRecordingFunctions(RecordingPausedFunction* recPausedFuncPtr, PauseRecordingFunction* pauseRecFuncPtr)
{
void* frontendApi = os_dlopen("obs-frontend-api");
if (recPausedFuncPtr) {
*recPausedFuncPtr = (RecordingPausedFunction)os_dlsym(frontendApi, "obs_frontend_recording_paused");
}
if (pauseRecFuncPtr) {
*pauseRecFuncPtr = (PauseRecordingFunction)os_dlsym(frontendApi, "obs_frontend_recording_pause");
}
os_dlclose(frontendApi);
}
bool Utils::RecordingPauseSupported()
{
RecordingPausedFunction recordingPaused = nullptr;
PauseRecordingFunction pauseRecording = nullptr;
getPauseRecordingFunctions(&recordingPaused, &pauseRecording);
return (recordingPaused && pauseRecording);
}
bool Utils::RecordingPaused()
{
RecordingPausedFunction recordingPaused = nullptr;
getPauseRecordingFunctions(&recordingPaused, nullptr);
if (recordingPaused == nullptr) {
return false;
}
return recordingPaused();
}
void Utils::PauseRecording(bool pause)
{
PauseRecordingFunction pauseRecording = nullptr;
getPauseRecordingFunctions(nullptr, &pauseRecording);
if (pauseRecording == nullptr) {
return;
}
pauseRecording(pause);
}

View File

@ -31,6 +31,9 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <obs-module.h> #include <obs-module.h>
#include <util/config-file.h> #include <util/config-file.h>
typedef void(*PauseRecordingFunction)(bool);
typedef bool(*RecordingPausedFunction)();
class Utils { class Utils {
public: public:
static obs_data_array_t* StringListToArray(char** strings, const char* key); static obs_data_array_t* StringListToArray(char** strings, const char* key);
@ -78,4 +81,8 @@ class Utils {
static const char* GetFilenameFormatting(); static const char* GetFilenameFormatting();
static bool SetFilenameFormatting(const char* filenameFormatting); static bool SetFilenameFormatting(const char* filenameFormatting);
static bool RecordingPauseSupported();
static bool RecordingPaused();
static void PauseRecording(bool pause);
}; };

View File

@ -17,7 +17,9 @@
* with this program. If not, see <https://www.gnu.org/licenses/> * with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include <inttypes.h>
#include <util/platform.h> #include <util/platform.h>
#include <media-io/video-io.h>
#include <QtWidgets/QPushButton> #include <QtWidgets/QPushButton>
@ -29,7 +31,7 @@
#define STATUS_INTERVAL 2000 #define STATUS_INTERVAL 2000
const char* nsToTimestamp(uint64_t ns) { QString nsToTimestamp(uint64_t ns) {
uint64_t ms = ns / 1000000ULL; uint64_t ms = ns / 1000000ULL;
uint64_t secs = ms / 1000ULL; uint64_t secs = ms / 1000ULL;
uint64_t minutes = secs / 60ULL; uint64_t minutes = secs / 60ULL;
@ -39,10 +41,7 @@ const char* nsToTimestamp(uint64_t ns) {
uint64_t secsPart = secs % 60ULL; uint64_t secsPart = secs % 60ULL;
uint64_t msPart = ms % 1000ULL; uint64_t msPart = ms % 1000ULL;
char* ts = (char*)bmalloc(64); return QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
sprintf(ts, "%02llu:%02llu:%02llu.%03llu", hoursPart, minutesPart, secsPart, msPart);
return ts;
} }
const char* sourceTypeToString(obs_source_type type) { const char* sourceTypeToString(obs_source_type type) {
@ -75,7 +74,8 @@ const char* calldata_get_string(const calldata_t* data, const char* name) {
WSEvents::WSEvents(WSServerPtr srv) : WSEvents::WSEvents(WSServerPtr srv) :
_srv(srv), _srv(srv),
_streamStarttime(0), _streamStarttime(0),
_recStarttime(0), _lastBytesSent(0),
_lastBytesSentTime(0),
HeartbeatIsActive(false), HeartbeatIsActive(false),
pulse(false) pulse(false)
{ {
@ -206,6 +206,14 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private
owner->OnRecordingStopped(); owner->OnRecordingStopped();
break; break;
case OBS_FRONTEND_EVENT_RECORDING_PAUSED:
owner->OnRecordingPaused();
break;
case OBS_FRONTEND_EVENT_RECORDING_UNPAUSED:
owner->OnRecordingResumed();
break;
case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING: case OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING:
owner->OnReplayStarting(); owner->OnReplayStarting();
break; break;
@ -247,17 +255,14 @@ void WSEvents::broadcastUpdate(const char* updateType,
OBSDataAutoRelease update = obs_data_create(); OBSDataAutoRelease update = obs_data_create();
obs_data_set_string(update, "update-type", updateType); obs_data_set_string(update, "update-type", updateType);
const char* ts = nullptr;
if (obs_frontend_streaming_active()) { if (obs_frontend_streaming_active()) {
ts = nsToTimestamp(os_gettime_ns() - _streamStarttime); QString streamingTimecode = getStreamingTimecode();
obs_data_set_string(update, "stream-timecode", ts); obs_data_set_string(update, "stream-timecode", streamingTimecode.toUtf8().constData());
bfree((void*)ts);
} }
if (obs_frontend_recording_active()) { if (obs_frontend_recording_active()) {
ts = nsToTimestamp(os_gettime_ns() - _recStarttime); QString recordingTimecode = getRecordingTimecode();
obs_data_set_string(update, "rec-timecode", ts); obs_data_set_string(update, "rec-timecode", recordingTimecode.toUtf8().constData());
bfree((void*)ts);
} }
if (additionalFields) if (additionalFields)
@ -362,26 +367,34 @@ void WSEvents::unhookTransitionBeginEvent() {
obs_frontend_source_list_free(&transitions); obs_frontend_source_list_free(&transitions);
} }
uint64_t WSEvents::GetStreamingTime() { uint64_t getOutputRunningTime(obs_output_t* output) {
if (obs_frontend_streaming_active()) if (!output || !obs_output_active(output)) {
return (os_gettime_ns() - _streamStarttime);
else
return 0; return 0;
} }
const char* WSEvents::GetStreamingTimecode() { video_t* video = obs_output_video(output);
return nsToTimestamp(GetStreamingTime()); uint64_t frameTimeNs = video_output_get_frame_time(video);
int totalFrames = obs_output_get_total_frames(output);
return (((uint64_t)totalFrames) * frameTimeNs);
} }
uint64_t WSEvents::GetRecordingTime() { uint64_t WSEvents::getStreamingTime() {
if (obs_frontend_recording_active()) OBSOutputAutoRelease streamingOutput = obs_frontend_get_streaming_output();
return (os_gettime_ns() - _recStarttime); return getOutputRunningTime(streamingOutput);
else
return 0;
} }
const char* WSEvents::GetRecordingTimecode() { uint64_t WSEvents::getRecordingTime() {
return nsToTimestamp(GetRecordingTime()); OBSOutputAutoRelease recordingOutput = obs_frontend_get_recording_output();
return getOutputRunningTime(recordingOutput);
}
QString WSEvents::getStreamingTimecode() {
return nsToTimestamp(getStreamingTime());
}
QString WSEvents::getRecordingTimecode() {
return nsToTimestamp(getRecordingTime());
} }
/** /**
@ -534,6 +547,7 @@ void WSEvents::OnStreamStarting() {
void WSEvents::OnStreamStarted() { void WSEvents::OnStreamStarted() {
_streamStarttime = os_gettime_ns(); _streamStarttime = os_gettime_ns();
_lastBytesSent = 0; _lastBytesSent = 0;
broadcastUpdate("StreamStarted"); broadcastUpdate("StreamStarted");
} }
@ -550,7 +564,6 @@ void WSEvents::OnStreamStarted() {
void WSEvents::OnStreamStopping() { void WSEvents::OnStreamStopping() {
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "preview-only", false); obs_data_set_bool(data, "preview-only", false);
broadcastUpdate("StreamStopping", data); broadcastUpdate("StreamStopping", data);
} }
@ -564,6 +577,7 @@ void WSEvents::OnStreamStopping() {
*/ */
void WSEvents::OnStreamStopped() { void WSEvents::OnStreamStopped() {
_streamStarttime = 0; _streamStarttime = 0;
broadcastUpdate("StreamStopped"); broadcastUpdate("StreamStopped");
} }
@ -588,7 +602,6 @@ void WSEvents::OnRecordingStarting() {
* @since 0.3 * @since 0.3
*/ */
void WSEvents::OnRecordingStarted() { void WSEvents::OnRecordingStarted() {
_recStarttime = os_gettime_ns();
broadcastUpdate("RecordingStarted"); broadcastUpdate("RecordingStarted");
} }
@ -613,10 +626,33 @@ void WSEvents::OnRecordingStopping() {
* @since 0.3 * @since 0.3
*/ */
void WSEvents::OnRecordingStopped() { void WSEvents::OnRecordingStopped() {
_recStarttime = 0;
broadcastUpdate("RecordingStopped"); broadcastUpdate("RecordingStopped");
} }
/**
* Current recording paused
*
* @api events
* @name RecordingPaused
* @category recording
* @since 4.7.0
*/
void WSEvents::OnRecordingPaused() {
broadcastUpdate("RecordingPaused");
}
/**
* Current recording resumed
*
* @api events
* @name RecordingResumed
* @category recording
* @since 4.7.0
*/
void WSEvents::OnRecordingResumed() {
broadcastUpdate("RecordingResumed");
}
/** /**
* A request to start the replay buffer has been issued. * A request to start the replay buffer has been issued.
* *
@ -708,6 +744,7 @@ void WSEvents::OnExit() {
void WSEvents::StreamStatus() { void WSEvents::StreamStatus() {
bool streamingActive = obs_frontend_streaming_active(); bool streamingActive = obs_frontend_streaming_active();
bool recordingActive = obs_frontend_recording_active(); bool recordingActive = obs_frontend_recording_active();
bool recordingPaused = Utils::RecordingPaused();
bool replayBufferActive = obs_frontend_replay_buffer_active(); bool replayBufferActive = obs_frontend_replay_buffer_active();
OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output(); OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output();
@ -734,9 +771,7 @@ void WSEvents::StreamStatus() {
_lastBytesSent = bytesSent; _lastBytesSent = bytesSent;
_lastBytesSentTime = bytesSentTime; _lastBytesSentTime = bytesSentTime;
uint64_t totalStreamTime = uint64_t totalStreamTime = (getStreamingTime() / 1000000000ULL);
(os_gettime_ns() - _streamStarttime) / 1000000000;
int totalFrames = obs_output_get_total_frames(streamOutput); int totalFrames = obs_output_get_total_frames(streamOutput);
int droppedFrames = obs_output_get_frames_dropped(streamOutput); int droppedFrames = obs_output_get_frames_dropped(streamOutput);
@ -746,6 +781,7 @@ void WSEvents::StreamStatus() {
obs_data_set_bool(data, "streaming", streamingActive); obs_data_set_bool(data, "streaming", streamingActive);
obs_data_set_bool(data, "recording", recordingActive); obs_data_set_bool(data, "recording", recordingActive);
obs_data_set_bool(data, "recording-paused", recordingPaused);
obs_data_set_bool(data, "replay-buffer-active", replayBufferActive); obs_data_set_bool(data, "replay-buffer-active", replayBufferActive);
obs_data_set_int(data, "bytes-per-sec", bytesPerSec); obs_data_set_int(data, "bytes-per-sec", bytesPerSec);
@ -792,6 +828,7 @@ void WSEvents::Heartbeat() {
bool streamingActive = obs_frontend_streaming_active(); bool streamingActive = obs_frontend_streaming_active();
bool recordingActive = obs_frontend_recording_active(); bool recordingActive = obs_frontend_recording_active();
bool recordingPaused = Utils::RecordingPaused();
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output(); OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output();
@ -807,16 +844,15 @@ void WSEvents::Heartbeat() {
obs_data_set_bool(data, "streaming", streamingActive); obs_data_set_bool(data, "streaming", streamingActive);
if (streamingActive) { if (streamingActive) {
uint64_t totalStreamTime = (os_gettime_ns() - _streamStarttime) / 1000000000; obs_data_set_int(data, "total-stream-time", (getStreamingTime() / 1000000000ULL));
obs_data_set_int(data, "total-stream-time", totalStreamTime);
obs_data_set_int(data, "total-stream-bytes", (uint64_t)obs_output_get_total_bytes(streamOutput)); obs_data_set_int(data, "total-stream-bytes", (uint64_t)obs_output_get_total_bytes(streamOutput));
obs_data_set_int(data, "total-stream-frames", obs_output_get_total_frames(streamOutput)); obs_data_set_int(data, "total-stream-frames", obs_output_get_total_frames(streamOutput));
} }
obs_data_set_bool(data, "recording", recordingActive); obs_data_set_bool(data, "recording", recordingActive);
obs_data_set_bool(data, "recording-paused", recordingPaused);
if (recordingActive) { if (recordingActive) {
uint64_t totalRecordTime = (os_gettime_ns() - _recStarttime) / 1000000000; obs_data_set_int(data, "total-record-time", (getRecordingTime() / 1000000000ULL));
obs_data_set_int(data, "total-record-time", totalRecordTime);
obs_data_set_int(data, "total-record-bytes", (uint64_t)obs_output_get_total_bytes(recordOutput)); obs_data_set_int(data, "total-record-bytes", (uint64_t)obs_output_get_total_bytes(recordOutput));
obs_data_set_int(data, "total-record-frames", obs_output_get_total_frames(recordOutput)); obs_data_set_int(data, "total-record-frames", obs_output_get_total_frames(recordOutput));
} }

View File

@ -43,10 +43,12 @@ public:
void hookTransitionBeginEvent(); void hookTransitionBeginEvent();
void unhookTransitionBeginEvent(); void unhookTransitionBeginEvent();
uint64_t GetStreamingTime(); uint64_t getStreamingTime();
const char* GetStreamingTimecode(); uint64_t getRecordingTime();
uint64_t GetRecordingTime();
const char* GetRecordingTimecode(); QString getStreamingTimecode();
QString getRecordingTimecode();
obs_data_t* GetStats(); obs_data_t* GetStats();
void OnBroadcastCustomMessage(QString realm, obs_data_t* data); void OnBroadcastCustomMessage(QString realm, obs_data_t* data);
@ -67,7 +69,6 @@ private:
bool pulse; bool pulse;
uint64_t _streamStarttime; uint64_t _streamStarttime;
uint64_t _recStarttime;
uint64_t _lastBytesSent; uint64_t _lastBytesSent;
uint64_t _lastBytesSentTime; uint64_t _lastBytesSentTime;
@ -95,6 +96,8 @@ private:
void OnRecordingStarted(); void OnRecordingStarted();
void OnRecordingStopping(); void OnRecordingStopping();
void OnRecordingStopped(); void OnRecordingStopped();
void OnRecordingPaused();
void OnRecordingResumed();
void OnReplayStarting(); void OnReplayStarting();
void OnReplayStarted(); void OnReplayStarted();

View File

@ -57,10 +57,14 @@ QHash<QString, HandlerResponse(*)(WSRequestHandler*)> WSRequestHandler::messageM
{ "GetStreamingStatus", WSRequestHandler::HandleGetStreamingStatus }, { "GetStreamingStatus", WSRequestHandler::HandleGetStreamingStatus },
{ "StartStopStreaming", WSRequestHandler::HandleStartStopStreaming }, { "StartStopStreaming", WSRequestHandler::HandleStartStopStreaming },
{ "StartStopRecording", WSRequestHandler::HandleStartStopRecording }, { "StartStopRecording", WSRequestHandler::HandleStartStopRecording },
{ "StartStreaming", WSRequestHandler::HandleStartStreaming }, { "StartStreaming", WSRequestHandler::HandleStartStreaming },
{ "StopStreaming", WSRequestHandler::HandleStopStreaming }, { "StopStreaming", WSRequestHandler::HandleStopStreaming },
{ "StartRecording", WSRequestHandler::HandleStartRecording }, { "StartRecording", WSRequestHandler::HandleStartRecording },
{ "StopRecording", WSRequestHandler::HandleStopRecording }, { "StopRecording", WSRequestHandler::HandleStopRecording },
{ "PauseRecording", WSRequestHandler::HandlePauseRecording },
{ "ResumeRecording", WSRequestHandler::HandleResumeRecording },
{ "StartStopReplayBuffer", WSRequestHandler::HandleStartStopReplayBuffer }, { "StartStopReplayBuffer", WSRequestHandler::HandleStartStopReplayBuffer },
{ "StartReplayBuffer", WSRequestHandler::HandleStartReplayBuffer }, { "StartReplayBuffer", WSRequestHandler::HandleStartReplayBuffer },

View File

@ -103,10 +103,14 @@ class WSRequestHandler : public QObject {
static HandlerResponse HandleGetStreamingStatus(WSRequestHandler* req); static HandlerResponse HandleGetStreamingStatus(WSRequestHandler* req);
static HandlerResponse HandleStartStopStreaming(WSRequestHandler* req); static HandlerResponse HandleStartStopStreaming(WSRequestHandler* req);
static HandlerResponse HandleStartStopRecording(WSRequestHandler* req); static HandlerResponse HandleStartStopRecording(WSRequestHandler* req);
static HandlerResponse HandleStartStreaming(WSRequestHandler* req); static HandlerResponse HandleStartStreaming(WSRequestHandler* req);
static HandlerResponse HandleStopStreaming(WSRequestHandler* req); static HandlerResponse HandleStopStreaming(WSRequestHandler* req);
static HandlerResponse HandleStartRecording(WSRequestHandler* req); static HandlerResponse HandleStartRecording(WSRequestHandler* req);
static HandlerResponse HandleStopRecording(WSRequestHandler* req); static HandlerResponse HandleStopRecording(WSRequestHandler* req);
static HandlerResponse HandlePauseRecording(WSRequestHandler* req);
static HandlerResponse HandleResumeRecording(WSRequestHandler* req);
static HandlerResponse HandleStartStopReplayBuffer(WSRequestHandler* req); static HandlerResponse HandleStartStopReplayBuffer(WSRequestHandler* req);
static HandlerResponse HandleStartReplayBuffer(WSRequestHandler* req); static HandlerResponse HandleStartReplayBuffer(WSRequestHandler* req);

View File

@ -1,6 +1,20 @@
#include "WSRequestHandler.h"
#include <util/platform.h>
#include "Utils.h" #include "Utils.h"
#include "WSRequestHandler.h" HandlerResponse ifCanPause(WSRequestHandler* req, std::function<HandlerResponse()> callback)
{
if (!obs_frontend_recording_active()) {
return req->SendErrorResponse("recording is not active");
}
if (!Utils::RecordingPauseSupported()) {
return req->SendErrorResponse("recording pauses are not available in this version of OBS Studio");
}
return callback();
}
/** /**
* Toggle recording on or off. * Toggle recording on or off.
@ -11,11 +25,7 @@
* @since 0.3 * @since 0.3
*/ */
HandlerResponse WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req) { HandlerResponse WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req) {
if (obs_frontend_recording_active()) (obs_frontend_recording_active() ? obs_frontend_recording_stop() : obs_frontend_recording_start());
obs_frontend_recording_stop();
else
obs_frontend_recording_start();
return req->SendOKResponse(); return req->SendOKResponse();
} }
@ -29,12 +39,12 @@ HandlerResponse WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) { HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
if (obs_frontend_recording_active() == false) { if (obs_frontend_recording_active()) {
obs_frontend_recording_start();
return req->SendOKResponse();
} else {
return req->SendErrorResponse("recording already active"); return req->SendErrorResponse("recording already active");
} }
obs_frontend_recording_start();
return req->SendOKResponse();
} }
/** /**
@ -47,12 +57,52 @@ HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
* @since 4.1.0 * @since 4.1.0
*/ */
HandlerResponse WSRequestHandler::HandleStopRecording(WSRequestHandler* req) { HandlerResponse WSRequestHandler::HandleStopRecording(WSRequestHandler* req) {
if (obs_frontend_recording_active() == true) { if (!obs_frontend_recording_active()) {
obs_frontend_recording_stop();
return req->SendOKResponse();
} else {
return req->SendErrorResponse("recording not active"); return req->SendErrorResponse("recording not active");
} }
obs_frontend_recording_stop();
return req->SendOKResponse();
}
/**
* Pause the current recording.
* Returns an error if recording is not active or already paused.
*
* @api requests
* @name PauseRecording
* @category recording
* @since 4.7.0
*/
HandlerResponse WSRequestHandler::HandlePauseRecording(WSRequestHandler* req) {
return ifCanPause(req, [req]() {
if (Utils::RecordingPaused()) {
return req->SendErrorResponse("recording already paused");
}
Utils::PauseRecording(true);
return req->SendOKResponse();
});
}
/**
* Resume/unpause the current recording (if paused).
* Returns an error if recording is not active or not paused.
*
* @api requests
* @name ResumeRecording
* @category recording
* @since 4.7.0
*/
HandlerResponse WSRequestHandler::HandleResumeRecording(WSRequestHandler* req) {
return ifCanPause(req, [req]() {
if (!Utils::RecordingPaused()) {
return req->SendErrorResponse("recording is not paused");
}
Utils::PauseRecording(false);
return req->SendOKResponse();
});
} }
/** /**
@ -63,7 +113,6 @@ HandlerResponse WSRequestHandler::HandleStartRecording(WSRequestHandler* req) {
* in progress, the change won't be applied immediately and will be * in progress, the change won't be applied immediately and will be
* effective on the next recording. * effective on the next recording.
* *
*
* @param {String} `rec-folder` Path of the recording folder. * @param {String} `rec-folder` Path of the recording folder.
* *
* @api requests * @api requests

View File

@ -26,19 +26,17 @@ HandlerResponse WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "streaming", obs_frontend_streaming_active()); obs_data_set_bool(data, "streaming", obs_frontend_streaming_active());
obs_data_set_bool(data, "recording", obs_frontend_recording_active()); obs_data_set_bool(data, "recording", obs_frontend_recording_active());
obs_data_set_bool(data, "recording-paused", Utils::RecordingPaused());
obs_data_set_bool(data, "preview-only", false); obs_data_set_bool(data, "preview-only", false);
const char* tc = nullptr;
if (obs_frontend_streaming_active()) { if (obs_frontend_streaming_active()) {
tc = events->GetStreamingTimecode(); QString streamingTimecode = events->getStreamingTimecode();
obs_data_set_string(data, "stream-timecode", tc); obs_data_set_string(data, "stream-timecode", streamingTimecode.toUtf8().constData());
bfree((void*)tc);
} }
if (obs_frontend_recording_active()) { if (obs_frontend_recording_active()) {
tc = events->GetRecordingTimecode(); QString recordingTimecode = events->getRecordingTimecode();
obs_data_set_string(data, "rec-timecode", tc); obs_data_set_string(data, "rec-timecode", recordingTimecode.toUtf8().constData());
bfree((void*)tc);
} }
return req->SendOKResponse(data); return req->SendOKResponse(data);