Skip to content

Commit

Permalink
Add send encoded video to janus support.
Browse files Browse the repository at this point in the history
  • Loading branch information
Meonardo committed Dec 15, 2022
1 parent ad117fc commit c01b1a5
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 100 deletions.
77 changes: 35 additions & 42 deletions src/janus-videoroom.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ os_cpu_usage_info_t *GetCpuUsageInfo()

static void janus_output_full_stop(void *data);
static void janus_deactivate(struct janus_output *output);
static void *write_thread(void *data);
static void *start_thread(void *data);
static bool try_connect(struct janus_output *output);

// data init & deinit
bool janus_data_init(struct janus_data *data, struct janus_cfg *config)
Expand Down Expand Up @@ -102,13 +101,11 @@ static void *janus_output_create(obs_data_t *settings, obs_output_t *output)
data->janus_conn = NULL;

// here create janus connection instance
data->janus_conn = CreateConncetion();

if (os_event_init(&data->stop_event, OS_EVENT_TYPE_AUTO) != 0) {
os_event_destroy(data->stop_event);
bfree(data);
return NULL;
}
#ifdef USE_ENCODED_DATA
data->janus_conn = CreateConncetion(true);
#else
data->janus_conn = CreateConncetion(false);
#endif // USE_ENCODED_DATA

UNUSED_PARAMETER(settings);
return data;
Expand All @@ -117,37 +114,40 @@ static void *janus_output_create(obs_data_t *settings, obs_output_t *output)
static bool janus_output_start(void *data)
{
struct janus_output *output = data;
int ret;

if (output->connecting)
return false;
output->connecting = true;

// get janus configs & connect to janus ws server
if (!try_connect(output)) {
obs_output_signal_stop(output->output,
OBS_OUTPUT_CONNECT_FAILED);
output->connecting = false;
return false;
}

os_atomic_set_bool(&output->stopping, false);
output->audio_start_ts = 0;
output->video_start_ts = 0;
output->total_bytes = 0;

ret = pthread_create(&output->start_thread, NULL, start_thread, output);
output->connecting = false;

return (output->connecting = (ret == 0));
return true;
}

static void janus_output_destroy(void *data)
{
struct janus_output *output = data;

if (output) {
if (output->connecting)
pthread_join(output->start_thread, NULL);

if (output->janus_conn != NULL) {
DestoryConnection(output->janus_conn);
output->janus_conn = NULL;
}

janus_output_full_stop(output);

os_event_destroy(output->stop_event);
bfree(data);
}
}
Expand Down Expand Up @@ -223,11 +223,17 @@ static bool try_connect(struct janus_output *output)
return false;
}

// set the output is active!
output->active = true;

if (!obs_output_can_begin_data_capture(output->output, 0))
return false;

#ifdef USE_ENCODED_DATA
if (!obs_output_initialize_encoders(output->output, OBS_OUTPUT_VIDEO)) {
return false;
}
#endif

// set the output is active!
os_atomic_set_bool(&output->active, true);
// begin capture
obs_output_begin_data_capture(output->output, 0);
// will call `obs_output_end_data_capture()` in `janus_output_full_stop()`
Expand All @@ -241,24 +247,6 @@ static bool try_connect(struct janus_output *output)
return true;
}

static void *start_thread(void *data)
{
struct janus_output *output = data;

// get janus configs & connect to janus ws server
if (!try_connect(output))
obs_output_signal_stop(output->output,
OBS_OUTPUT_CONNECT_FAILED);

// set connecting
output->connecting = false;

// set thread name
os_set_thread_name("janus-output-thread");

return NULL;
}

// audio callback from obs
static void receive_audio(void *param, struct audio_data *frame)
{
Expand All @@ -282,7 +270,12 @@ static void receive_encoded_data(void *data, struct encoder_packet *packet)
struct janus_output *output = data;

if (packet->type == OBS_ENCODER_VIDEO) {

if (output->janus_conn != NULL) {
// send encoded packet to janus connection
SendVideoPacket(output->janus_conn, packet,
output->js_data.config.width,
output->js_data.config.height);
}
}

/*if (packet->type == OBS_ENCODER_AUDIO) {
Expand All @@ -298,12 +291,12 @@ struct obs_output_info janus_output = {
.start = janus_output_start,
.stop = janus_output_stop,
#ifdef USE_ENCODED_DATA
.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED,
.flags = OBS_OUTPUT_VIDEO | OBS_OUTPUT_ENCODED,
.encoded_video_codecs = "h264",
.encoded_audio_codecs = "opus",
//.encoded_audio_codecs = "opus",
.encoded_packet = receive_encoded_data,
#else
.flags = OBS_OUTPUT_AV,
.flags = OBS_OUTPUT_VIDEO,
.raw_video = receive_video,
.raw_audio = receive_audio,
#endif // USE_ENCODED_DATA
Expand Down
6 changes: 0 additions & 6 deletions src/janus-videoroom.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,6 @@ struct janus_output {
volatile bool active;
volatile bool stopping;

// the thread of this module, created in `janus_output_start()`
// name: 'janus-output-thread'
pthread_t start_thread;
// stop event
os_event_t *stop_event;

uint64_t total_bytes;
uint64_t audio_start_ts;
uint64_t video_start_ts;
Expand Down
67 changes: 37 additions & 30 deletions src/janus_connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@

namespace janus {

VideoFrameFeederImpl::VideoFrameFeederImpl()
: frame_receiver_(nullptr)
VideoFeederImpl::VideoFeederImpl()
: frame_receiver_(nullptr), packet_receiver_(nullptr)
{
}

VideoFrameFeederImpl::~VideoFrameFeederImpl()
VideoFeederImpl::~VideoFeederImpl()
{
frame_receiver_ = nullptr;
}

void VideoFrameFeederImpl::FeedVideoFrame(OBSVideoFrame *frame, int width,
void VideoFeederImpl::FeedVideoFrame(OBSVideoFrame *frame, int width,
int height)
{
auto v_frame = libwebrtc::RTCVideoFrame::Create(
Expand All @@ -23,46 +23,40 @@ void VideoFrameFeederImpl::FeedVideoFrame(OBSVideoFrame *frame, int width,
}
}

void VideoFrameFeederImpl::SetFrameReceiver(
void VideoFeederImpl::SetFrameReceiver(
owt::base::VideoFrameReceiverInterface *receiver)
{
frame_receiver_ = receiver;
}

bool VideoFrameFeederImpl::InitEncoderContext(owt::base::Resolution &resolution, uint32_t fps,
uint32_t bitrate_kbps,
owt::base::VideoCodec video_codec)
{
return true;
}

bool VideoFrameFeederImpl::EncodeOneFrame(std::vector<uint8_t> &buffer,
bool key_frame)
{
return true;
}

bool VideoFrameFeederImpl::Release()
void VideoFeederImpl::SetBufferReceiver(
owt::base::VideoPacketReceiverInterface *receiver)
{
return true;
packet_receiver_ = receiver;
}

owt::base::VideoEncoderInterface *VideoFrameFeederImpl::Copy()
void VideoFeederImpl::FeedVideoPacket(OBSVideoPacket *pkt, int width,
int height)
{
return nullptr;
auto encoded_frame = libwebrtc::RTCVideoFrame::Create(pkt->data, pkt->size, pkt->keyframe,
width, height);
if (packet_receiver_ != nullptr) {
packet_receiver_->OnPacket(encoded_frame);
}
}

/////////////////////////////////////////////////////////////////////////////////

JanusConnection::JanusConnection()
JanusConnection::JanusConnection(bool send_encoded_data)
: ws_client_(nullptr),
rtc_client_(nullptr),
video_framer_(nullptr),
room_(0),
session_id_(0),
handle_id_(0),
id_(0),
joined_room_(false)
joined_room_(false),
use_encoded_data_(send_encoded_data)
{
}

Expand Down Expand Up @@ -233,12 +227,15 @@ rtc::RTCClient *JanusConnection::GetRTCClient() const
void JanusConnection::SendVideoFrame(OBSVideoFrame *frame, int width,
int height)
{
if (video_framer_ == nullptr) {
video_framer_ = new VideoFrameFeederImpl();
}
video_framer_->FeedVideoFrame(frame, width, height);
}

void JanusConnection::SendVideoPacket(OBSVideoPacket *pkt, int width,
int height)
{
video_framer_->FeedVideoPacket(pkt, width, height);
}

void JanusConnection::DestoryRTCClient()
{
if (rtc_client_ == nullptr)
Expand Down Expand Up @@ -269,12 +266,22 @@ void JanusConnection::CreateHandle()

void JanusConnection::CreateOffer()
{
if (rtc_client_ == nullptr || video_framer_ == nullptr)
// create video framer if necessary
if (video_framer_ == nullptr) {
video_framer_ = new VideoFeederImpl();
}

if (rtc_client_ == nullptr)
return;

// create media sender
rtc_client_->CreateMediaSender(
std::unique_ptr<VideoFrameFeederImpl>(video_framer_));
if (use_encoded_data_) {
rtc_client_->CreateMediaSender(video_framer_);
} else {
rtc_client_->CreateMediaSender(
std::unique_ptr<VideoFeederImpl>(video_framer_));
}

// create offer
rtc_client_->CreateOffer(this, [](janus::rtc::RTCSessionDescription &sdp,
std::string &error, void *params) {
Expand Down
34 changes: 19 additions & 15 deletions src/janus_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,40 @@

extern "C" {
#include "media-io/video-frame.h"
#include <obs.h>
typedef struct video_frame OBSVideoFrame;
typedef struct encoder_packet OBSVideoPacket;
}

namespace janus {
class VideoFrameFeederImpl : public owt::base::VideoFrameFeeder,
public owt::base::VideoEncoderInterface {
// this class impl both `VideoFrameFeeder` and `VideoPacketFeeder`
// it able to send raw or encoded video data to janus video-room
class VideoFeederImpl : public owt::base::VideoFrameFeeder,
public owt::base::VideoPacketFeeder {
public:
VideoFrameFeederImpl();
~VideoFrameFeederImpl();
VideoFeederImpl();
~VideoFeederImpl();

// tell the framegenerator to store the frame's receiver
virtual void SetFrameReceiver(owt::base::VideoFrameReceiverInterface *receiver);
// tell the VideoFrameFeeder to store the frame's receiver(do NOT free this receiver)
virtual void SetFrameReceiver(owt::base::VideoFrameReceiverInterface *receiver) override;
// call this function from obs
void FeedVideoFrame(OBSVideoFrame *frame, int width, int height);

// encoded packet
virtual bool InitEncoderContext(owt::base::Resolution &resolution, uint32_t fps,
uint32_t bitrate_kbps,
owt::base::VideoCodec video_codec) override;
virtual bool EncodeOneFrame(std::vector<uint8_t> &buffer,
bool key_frame) override;
virtual bool Release() override;
virtual owt::base::VideoEncoderInterface *Copy() override;
virtual void SetBufferReceiver(
owt::base::VideoPacketReceiverInterface *receiver) override;
// call this function from obs
void FeedVideoPacket(OBSVideoPacket *pkt, int width, int height);

private:
owt::base::VideoFrameReceiverInterface *frame_receiver_;
owt::base::VideoPacketReceiverInterface *packet_receiver_;
};

class JanusConnection : public signaling::WebsocketClientInterface,
public rtc::RTCClientIceCandidateObserver {
public:
JanusConnection();
JanusConnection(bool send_encoded_data);
~JanusConnection();

// websocket event callbacks
Expand All @@ -66,8 +68,10 @@ class JanusConnection : public signaling::WebsocketClientInterface,

// called from obs output
void SendVideoFrame(OBSVideoFrame *frame, int width, int height);
void SendVideoPacket(OBSVideoPacket *pkt, int width, int height);

private:
bool use_encoded_data_;
uint32_t id_;
uint64_t room_;
std::string display_;
Expand All @@ -81,7 +85,7 @@ class JanusConnection : public signaling::WebsocketClientInterface,

signaling::WebsocketClient *ws_client_;
rtc::RTCClient *rtc_client_;
VideoFrameFeederImpl *video_framer_;
VideoFeederImpl *video_framer_;

// websocket events
void Connect(const char *url);
Expand Down
11 changes: 9 additions & 2 deletions src/janus_connection_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
extern "C" {
#endif

void *CreateConncetion()
void *CreateConncetion(bool encoded)
{
return new janus::JanusConnection();
return new janus::JanusConnection(encoded);
}

void DestoryConnection(void *conn)
Expand Down Expand Up @@ -39,6 +39,13 @@ void SendVideoFrame(void *conn, void *video_frame, int width, int height)
janus_conn->SendVideoFrame(frame, width, height);
}

void SendVideoPacket(void *conn, void *packet, int width, int height)
{
auto janus_conn = reinterpret_cast<janus::JanusConnection *>(conn);
auto pkt = reinterpret_cast<OBSVideoPacket *>(packet);
janus_conn->SendVideoPacket(pkt, width, height);
}

#ifdef __cplusplus
}
#endif
Loading

0 comments on commit c01b1a5

Please sign in to comment.