Compare commits

...

22 Commits
v6.56 ... v6.59

Author SHA1 Message Date
Maxim Devaev
92878e22ab Bump version: 6.58 → 6.59 2026-05-31 07:23:12 +03:00
Maxim Devaev
4dc0f73e1b janus: ssrc per client 2026-05-31 07:11:44 +03:00
Maxim Devaev
9fe42b038a ignore python build 2026-05-31 05:33:55 +03:00
Maxim Devaev
3750a22091 janus: separate audio of mic 2026-05-31 05:33:42 +03:00
Maxim Devaev
ef59c52a83 Bump version: 6.57 → 6.58 2026-05-30 12:01:35 +03:00
Maxim Devaev
f296bb57e7 added python-installer 2026-05-30 11:58:34 +03:00
Maxim Devaev
f7166b7c41 Bump version: 6.56 → 6.57 2026-05-30 11:49:10 +03:00
Maxim Devaev
b35200345e Merge pull request #334 from RSATom/master
cpu/encoder: fixed luma plane size calculation
2026-05-30 07:53:40 +03:00
sergey radionov
5bd7c9c5ef cpu/encoder: fixed luma plane size calculation
for 4:2:0 pixel formats,
and removed logic related to not used 4:1:0 pixel formats.
2026-05-28 13:46:27 +07:00
Maxim Devaev
fc35b1cd87 Merge pull request #333 from intelfx/work/fix-makefile
python: Makefile: use `python -m installer`; do not install while building
2026-05-18 08:21:39 +03:00
Maxim Devaev
92d9828894 janus: lost static 2026-05-18 08:21:09 +03:00
Maxim Devaev
e7008c6086 janus: splitted _rtpv_process_nalu() 2026-05-18 08:15:52 +03:00
Ivan Shapovalov
d0672e15b9 python: Makefile: use python -m installer; do not install while building
When building a Python wheel using `python -m build --wheel ...`,
temporary build files (including extension .so) are placed in ./build/...
from where they can be retrieved without having to install the just-built
wheel. Do both and get rid of `pip` invocations.
2026-05-18 08:13:47 +03:00
Maxim Devaev
04aa4c166c Merge pull request #332 from intelfx/work/fix-janus-rtpv
janus: fix rtpv chunking
2026-05-18 07:47:54 +03:00
Ivan Shapovalov
0fbbc27d8c janus: rtpv: fix and rework {first,last}_of_frame determination
Fixes: 2dac4db ("janus: fixed first/last frame logic for sps/pps")
Fixes: f32e256 ("janus: refactoring")
2026-05-18 07:45:09 +03:00
Ivan Shapovalov
b02bb85d8b janus: rtpv: marked -> last_nalu 2026-05-18 07:44:49 +03:00
Ivan Shapovalov
80dce276ed janus: rtp: marked -> last_header 2026-05-18 07:44:49 +03:00
Maxim Devaev
e869536a11 janus: refactoring 2026-05-16 16:14:56 +03:00
Maxim Devaev
b0acf0fa4d janus: refactoring 2026-05-15 21:43:42 +03:00
Maxim Devaev
f32e2569d2 janus: refactoring 2026-05-15 21:15:50 +03:00
Maxim Devaev
6d63f8e5d2 janus: refactoring 2026-05-15 19:32:37 +03:00
Maxim Devaev
2dac4db9fc janus: fixed first/last frame logic for sps/pps 2026-05-15 01:07:59 +03:00
20 changed files with 187 additions and 146 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion] [bumpversion]
commit = True commit = True
tag = True tag = True
current_version = 6.56 current_version = 6.59
parse = (?P<major>\d+)\.(?P<minor>\d+) parse = (?P<major>\d+)\.(?P<minor>\d+)
serialize = serialize =
{major}.{minor} {major}.{minor}

1
.gitignore vendored
View File

@@ -6,6 +6,7 @@
/python/dist/ /python/dist/
/python/ustreamer.egg-info/ /python/ustreamer.egg-info/
/python/root/ /python/root/
/python/build/
/janus/build/ /janus/build/
/ustreamer /ustreamer
/ustreamer-* /ustreamer-*

View File

@@ -72,7 +72,7 @@ apps:
python: python:
$(MAKE) -C python $(MAKE) -C python
$(ECHO) ln -sf python/root/usr/lib/python*/site-packages/*.so . $(ECHO) ln -sf python/build/lib.linux-*/*.so -t .
janus: janus:

View File

@@ -55,6 +55,9 @@ us_janus_client_s *us_janus_client_init(janus_callbacks *gw, janus_plugin_sessio
US_CALLOC(client, 1); US_CALLOC(client, 1);
client->gw = gw; client->gw = gw;
client->session = session; client->session = session;
client->video_ssrc = us_get_ssrc();
client->audio_ssrc = us_get_ssrc();
atomic_init(&client->transmit, false); atomic_init(&client->transmit, false);
atomic_init(&client->transmit_acap, false); atomic_init(&client->transmit_acap, false);
atomic_init(&client->transmit_aplay, false); atomic_init(&client->transmit_aplay, false);
@@ -181,6 +184,8 @@ static void *_video_or_acap_thread(void *v_client, bool video) {
atomic_load(&client->transmit) atomic_load(&client->transmit)
&& (video || atomic_load(&client->transmit_acap)) && (video || atomic_load(&client->transmit_acap))
) { ) {
us_rtp_write_ssrc(&rtp, (rtp.video ? client->video_ssrc : client->audio_ssrc));
janus_plugin_rtp packet = { janus_plugin_rtp packet = {
.video = rtp.video, .video = rtp.video,
.buffer = (char*)rtp.datagram, .buffer = (char*)rtp.datagram,

View File

@@ -37,6 +37,9 @@
typedef struct { typedef struct {
janus_callbacks *gw; janus_callbacks *gw;
janus_plugin_session *session; janus_plugin_session *session;
u32 video_ssrc;
u32 audio_ssrc;
atomic_bool transmit; atomic_bool transmit;
atomic_bool transmit_acap; atomic_bool transmit_acap;
atomic_bool transmit_aplay; atomic_bool transmit_aplay;

View File

@@ -68,8 +68,8 @@ us_config_s *us_config_init(const char *config_dir_path) {
US_JLOG_ERROR("config", "Either acap.sampling_rate or acap.tc358743 required"); US_JLOG_ERROR("config", "Either acap.sampling_rate or acap.tc358743 required");
goto error; goto error;
} }
config->aplay_dev_name = _get_value(jcfg, "aplay", "device");
} }
config->aplay_dev_name = _get_value(jcfg, "aplay", "device");
goto ok; goto ok;

View File

@@ -443,9 +443,9 @@ static int _plugin_init(janus_callbacks *gw, const char *config_dir_path) {
if (_g_config->acap_dev_name != NULL) { if (_g_config->acap_dev_name != NULL) {
_g_rtpa = us_rtpa_init(_relay_rtp_clients); _g_rtpa = us_rtpa_init(_relay_rtp_clients);
US_THREAD_CREATE(_g_acap_tid, _acap_thread, NULL); US_THREAD_CREATE(_g_acap_tid, _acap_thread, NULL);
if (_g_config->aplay_dev_name != NULL) { }
US_THREAD_CREATE(_g_aplay_tid, _aplay_thread, NULL); if (_g_config->aplay_dev_name != NULL) {
} US_THREAD_CREATE(_g_aplay_tid, _aplay_thread, NULL);
} }
US_THREAD_CREATE(_g_video_rtp_tid, _video_rtp_thread, NULL); US_THREAD_CREATE(_g_video_rtp_tid, _video_rtp_thread, NULL);
US_THREAD_CREATE(_g_video_sink_tid, _video_sink_thread, NULL); US_THREAD_CREATE(_g_video_sink_tid, _video_sink_thread, NULL);
@@ -641,14 +641,22 @@ static struct janus_plugin_result *_plugin_handle_message(
} }
{ {
char *const sdp = us_sdp_create( _LOCK_ALL;
_g_rtpv, US_LIST_ITERATE(_g_clients, client, {
(with_acap ? _g_rtpa : NULL), if (client->session == session) {
(with_acap && with_aplay)); char *const sdp = us_sdp_create(
json_t *const offer_jsep = json_pack("{ssss}", "type", "offer", "sdp", sdp); client->video_ssrc,
PUSH_STATUS("started", NULL, offer_jsep); client->audio_ssrc,
json_decref(offer_jsep); with_acap,
free(sdp); with_aplay);
json_t *const offer_jsep = json_pack("{ssss}", "type", "offer", "sdp", sdp);
PUSH_STATUS("started", NULL, offer_jsep);
json_decref(offer_jsep);
free(sdp);
break;
}
});
_UNLOCK_ALL;
} }
{ {
@@ -671,11 +679,10 @@ static struct janus_plugin_result *_plugin_handle_message(
} else if (!strcmp(request_str, "features")) { } else if (!strcmp(request_str, "features")) {
const char *const ice_url = getenv("JANUS_USTREAMER_WEB_ICE_URL"); const char *const ice_url = getenv("JANUS_USTREAMER_WEB_ICE_URL");
const bool acap_avail = us_au_probe(_g_config->acap_dev_name);
json_t *const features = json_pack( json_t *const features = json_pack(
"{s:b, s:b, s:{s:s?}}", "{s:b, s:b, s:{s:s?}}",
"audio", acap_avail, "audio", us_au_probe(_g_config->acap_dev_name),
"mic", (acap_avail && us_au_probe(_g_config->aplay_dev_name)), "mic", us_au_probe(_g_config->aplay_dev_name),
"ice", "url", (ice_url != NULL ? ice_url : default_ice_url) "ice", "url", (ice_url != NULL ? ice_url : default_ice_url)
); );
PUSH_STATUS("features", features, NULL); PUSH_STATUS("features", features, NULL);

View File

@@ -44,22 +44,27 @@ void us_rtp_destroy(us_rtp_s *rtp) {
void us_rtp_assign(us_rtp_s *rtp, uint payload, bool video) { void us_rtp_assign(us_rtp_s *rtp, uint payload, bool video) {
rtp->payload = payload; rtp->payload = payload;
rtp->video = video; rtp->video = video;
rtp->ssrc = us_triple_u32(us_get_now_monotonic_u64());
} }
void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool marked) { #define WRITE_BE_U32(x_offset, x_value) \
*((u32*)(rtp->datagram + x_offset)) = __builtin_bswap32(x_value)
void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool last_header) {
u32 word0 = 0x80000000; u32 word0 = 0x80000000;
if (marked) { if (last_header) {
word0 |= 1 << 23; word0 |= 1 << 23;
} }
word0 |= (rtp->payload & 0x7F) << 16; word0 |= (rtp->payload & 0x7F) << 16;
word0 |= rtp->seq; word0 |= rtp->seq;
++rtp->seq; ++rtp->seq;
# define WRITE_BE_U32(x_offset, x_value) \
*((u32*)(rtp->datagram + x_offset)) = __builtin_bswap32(x_value)
WRITE_BE_U32(0, word0); WRITE_BE_U32(0, word0);
WRITE_BE_U32(4, pts); WRITE_BE_U32(4, pts);
WRITE_BE_U32(8, rtp->ssrc); WRITE_BE_U32(8, 0); // SSRC should be written separately for each client
# undef WRITE_BE_U32
} }
void us_rtp_write_ssrc(us_rtp_s *rtp, u32 ssrc) {
WRITE_BE_U32(8, ssrc);
}
#undef WRITE_BE_U32

View File

@@ -23,6 +23,7 @@
#pragma once #pragma once
#include "uslibs/types.h" #include "uslibs/types.h"
#include "uslibs/tools.h"
// Max RTP size for WebRTC is 1200 bytes: // Max RTP size for WebRTC is 1200 bytes:
@@ -47,7 +48,6 @@
typedef struct { typedef struct {
uint payload; uint payload;
bool video; bool video;
u32 ssrc;
u16 seq; u16 seq;
u8 datagram[US_RTP_TOTAL_SIZE]; u8 datagram[US_RTP_TOTAL_SIZE];
@@ -62,8 +62,13 @@ typedef struct {
typedef void (*us_rtp_callback_f)(const us_rtp_s *rtp); typedef void (*us_rtp_callback_f)(const us_rtp_s *rtp);
static inline u32 us_get_ssrc(void) {
return us_triple_u32(us_get_now_monotonic_u64());
}
us_rtp_s *us_rtp_init(void); us_rtp_s *us_rtp_init(void);
void us_rtp_destroy(us_rtp_s *rtp); void us_rtp_destroy(us_rtp_s *rtp);
void us_rtp_assign(us_rtp_s *rtp, uint payload, bool video); void us_rtp_assign(us_rtp_s *rtp, uint payload, bool video);
void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool marked); void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool last_header);
void us_rtp_write_ssrc(us_rtp_s *rtp, u32 ssrc);

View File

@@ -26,8 +26,6 @@
#include "rtpv.h" #include "rtpv.h"
#include <stdlib.h> #include <stdlib.h>
#include <inttypes.h>
#include <assert.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
@@ -38,9 +36,12 @@
#include "rtp.h" #include "rtp.h"
void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool marked);
static sz _find_annexb(const u8 *data, uz size); static sz _find_annexb(const u8 *data, uz size);
static bool _is_nalu_small(uz size);
static void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu);
static void _rtpv_process_nalu_small(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu);
static void _rtpv_process_nalu_big(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu);
us_rtpv_s *us_rtpv_init(us_rtp_callback_f callback) { us_rtpv_s *us_rtpv_init(us_rtp_callback_f callback) {
@@ -60,77 +61,109 @@ void us_rtpv_destroy(us_rtpv_s *rtpv) {
#define _PRE 3 // Annex B prefix length #define _PRE 3 // Annex B prefix length
void us_rtpv_wrap(us_rtpv_s *rtpv, const us_frame_s *frame, bool zero_playout_delay) { void us_rtpv_wrap(us_rtpv_s *rtpv, const us_frame_s *frame, bool zero_playout_delay) {
// There is a complicated logic here but everything works as it should: US_A(frame->format == V4L2_PIX_FMT_H264);
// - https://github.com/pikvm/ustreamer/issues/115#issuecomment-893071775
assert(frame->format == V4L2_PIX_FMT_H264); if (frame->used <= _PRE) {
return;
}
rtpv->rtp->first_of_frame = true;
rtpv->rtp->last_of_frame = false;
rtpv->rtp->zero_playout_delay = zero_playout_delay; rtpv->rtp->zero_playout_delay = zero_playout_delay;
rtpv->rtp->grab_ntp_ts = us_get_now_ntp() - us_ld_to_ntp(us_get_now_monotonic() - frame->grab_begin_ts); rtpv->rtp->grab_ntp_ts = us_get_now_ntp() - us_ld_to_ntp(us_get_now_monotonic() - frame->grab_begin_ts);
const u32 pts = us_get_now_monotonic_u64() * 9 / 100; // PTS units are in 90 kHz const u32 pts = us_get_now_monotonic_u64() * 9 / 100; // PTS units are in 90 kHz
sz last_offset = -_PRE;
while (true) { // Find and iterate by nalus // Bytestream can be like:
const uz next_start = last_offset + _PRE; // [00-]00-00-01 NALU [00-]00-00-01 NALU
sz offset = _find_annexb(frame->data + next_start, frame->used - next_start); // ... with optional 00 prefix for _PRE.
if (offset < 0) { // We need to iterate by NALUs only and throw away separators.
break;
}
offset += next_start;
if (last_offset >= 0) { const sz offset_to_first = _find_annexb(frame->data, frame->used);
const u8 *const data = frame->data + last_offset + _PRE; if (offset_to_first < 0) {
uz size = offset - last_offset - _PRE; return;
if (data[size - 1] == 0) { // Check for extra 00 }
uz begin = offset_to_first + _PRE;
bool first_nalu = true;
while (begin < frame->used) { // Find and iterate by NALUs
const u8 *const data = frame->data + begin;
const sz offset_to_next = _find_annexb(data, frame->used - begin);
if (offset_to_next >= 0) { // Process NALUs between prefixes
uz size = offset_to_next;
if (size > 1 && data[size - 1] == 0) { // Check for extra trailing zero
--size; --size;
} }
_rtpv_process_nalu(rtpv, data, size, pts, false); if (size > 1) { // Skip too short NALUs
_rtpv_process_nalu(rtpv, data, size, pts, first_nalu, false);
first_nalu = false;
}
begin += offset_to_next + _PRE;
} else { // Process the tail
const uz size = frame->used - begin;
if (size > 1) {
_rtpv_process_nalu(rtpv, data, size, pts, first_nalu, true);
first_nalu = false;
}
break;
} }
last_offset = offset;
}
if (last_offset >= 0) {
const u8 *const data = frame->data + last_offset + _PRE;
uz size = frame->used - last_offset - _PRE;
_rtpv_process_nalu(rtpv, data, size, pts, true);
} }
} }
void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool marked) { static sz _find_annexb(const u8 *data, uz size) {
// Parses buffer for 00 00 01 start codes
if (size >= _PRE) {
for (uz i = 0; i <= size - _PRE; ++i) {
if (
data[i] == 0
&& data[i + 1] == 0
&& data[i + 2] == 1
) {
return i;
}
}
}
return -1;
}
#undef _PRE
static bool _is_nalu_small(uz size) {
return (size + US_RTP_HEADER_SIZE <= US_RTP_TOTAL_SIZE);
}
static void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu) {
US_A(size > 1);
if (_is_nalu_small(size)) {
_rtpv_process_nalu_small(rtpv, data, size, pts, first_nalu, last_nalu);
} else {
_rtpv_process_nalu_big(rtpv, data, size, pts, first_nalu, last_nalu);
}
}
static void _rtpv_process_nalu_small(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu) {
US_A(size > 1);
US_A(_is_nalu_small(size));
us_rtp_s *rtp = rtpv->rtp;
u8 *dg = rtp->datagram;
us_rtp_write_header(rtp, pts, last_nalu);
memcpy(dg + US_RTP_HEADER_SIZE, data, size);
rtp->used = size + US_RTP_HEADER_SIZE;
rtp->first_of_frame = first_nalu;
rtp->last_of_frame = last_nalu;
rtpv->callback(rtp);
}
static void _rtpv_process_nalu_big(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool first_nalu, bool last_nalu) {
US_A(size > 1);
US_A(!_is_nalu_small(size));
const uint ref_idc = (data[0] >> 5) & 3; const uint ref_idc = (data[0] >> 5) & 3;
const uint type = data[0] & 0x1F; const uint type = data[0] & 0x1F;
u8 *dg = rtpv->rtp->datagram; us_rtp_s *rtp = rtpv->rtp;
u8 *dg = rtp->datagram;
// Set *_of_frame flags only for non-SPS/PPS packages
/*
# define CALL_FOR_SERVICE { \
const bool m_fof = rtpv->rtp->first_of_frame; \
const bool m_lof = rtpv->rtp->last_of_frame; \
rtpv->rtp->first_of_frame = false; \
rtpv->rtp->last_of_frame = false; \
rtpv->callback(rtpv->rtp); \
rtpv->rtp->first_of_frame = m_fof; \
rtpv->rtp->last_of_frame = m_lof; \
}
*/
if (size + US_RTP_HEADER_SIZE <= US_RTP_TOTAL_SIZE) {
us_rtp_write_header(rtpv->rtp, pts, marked);
memcpy(dg + US_RTP_HEADER_SIZE, data, size);
rtpv->rtp->used = size + US_RTP_HEADER_SIZE;
// if (type == 7 || type == 8) {
// CALL_FOR_SERVICE;
// } else {*/
rtpv->rtp->last_of_frame = true;
rtpv->callback(rtpv->rtp);
rtpv->rtp->first_of_frame = false;
// }
return;
}
const uz fu_overhead = US_RTP_HEADER_SIZE + 2; // FU-A overhead const uz fu_overhead = US_RTP_HEADER_SIZE + 2; // FU-A overhead
@@ -145,7 +178,7 @@ void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool
frag_size = remaining; frag_size = remaining;
} }
us_rtp_write_header(rtpv->rtp, pts, (marked && last)); us_rtp_write_header(rtp, pts, (last_nalu && last));
dg[US_RTP_HEADER_SIZE] = 28 | (ref_idc << 5); dg[US_RTP_HEADER_SIZE] = 28 | (ref_idc << 5);
@@ -159,33 +192,13 @@ void _rtpv_process_nalu(us_rtpv_s *rtpv, const u8 *data, uz size, u32 pts, bool
dg[US_RTP_HEADER_SIZE + 1] = fu; dg[US_RTP_HEADER_SIZE + 1] = fu;
memcpy(dg + fu_overhead, src, frag_size); memcpy(dg + fu_overhead, src, frag_size);
rtpv->rtp->used = fu_overhead + frag_size; rtp->used = fu_overhead + frag_size;
// if (type == 7 || type == 8) { rtp->first_of_frame = (first_nalu && first);
// CALL_FOR_SERVICE; rtp->last_of_frame = (last_nalu && last);
// } else { rtpv->callback(rtp);
rtpv->rtp->last_of_frame = last;
rtpv->callback(rtpv->rtp);
rtpv->rtp->first_of_frame = false;
// }
src += frag_size; src += frag_size;
remaining -= frag_size; remaining -= frag_size;
first = false; first = false;
} }
# undef CALL_FOR_SERVICE
} }
static sz _find_annexb(const u8 *data, uz size) {
// Parses buffer for 00 00 01 start codes
if (size >= _PRE) {
for (uz index = 0; index <= size - _PRE; ++index) {
if (data[index] == 0 && data[index + 1] == 0 && data[index + 2] == 1) {
return index;
}
}
}
return -1;
}
#undef _PRE

View File

@@ -34,12 +34,12 @@
#include "rtpa.h" #include "rtpa.h"
char *us_sdp_create(us_rtpv_s *rtpv, us_rtpa_s *rtpa, bool mic) { char *us_sdp_create(u32 video_ssrc, u32 audio_ssrc, bool acap, bool aplay) {
char *video_sdp; char *video_sdp;
{ {
// https://tools.ietf.org/html/rfc6184 // https://tools.ietf.org/html/rfc6184
// https://github.com/meetecho/janus-gateway/issues/2443 // https://github.com/meetecho/janus-gateway/issues/2443
const uint pl = rtpv->rtp->payload; const uint pl = US_RTP_H264_PAYLOAD;
US_ASPRINTF( US_ASPRINTF(
video_sdp, video_sdp,
"m=video 1 RTP/SAVPF %u" RN "m=video 1 RTP/SAVPF %u" RN
@@ -58,14 +58,22 @@ char *us_sdp_create(us_rtpv_s *rtpv, us_rtpa_s *rtpa, bool mic) {
"a=extmap:3/sendonly http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time" RN "a=extmap:3/sendonly http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time" RN
"a=sendonly" RN, "a=sendonly" RN,
pl, pl, pl, pl, pl, pl, pl, pl, pl, pl, pl, pl, pl, pl,
rtpv->rtp->ssrc); video_ssrc);
} }
char *audio_sdp; char *audio_sdp;
if (rtpa == NULL) { if (acap || aplay) {
audio_sdp = us_strdup(""); const uint pl = US_RTP_OPUS_PAYLOAD;
} else {
const uint pl = rtpa->rtp->payload; char *dir = "";
if (acap && aplay) {
dir = "sendrecv";
} else if (acap && !aplay) {
dir = "sendonly";
} else if (!acap && aplay) {
dir = "recvonly";
}
US_ASPRINTF( US_ASPRINTF(
audio_sdp, audio_sdp,
"m=audio 1 RTP/SAVPF %u" RN "m=audio 1 RTP/SAVPF %u" RN
@@ -79,8 +87,10 @@ char *us_sdp_create(us_rtpv_s *rtpv, us_rtpa_s *rtpa, bool mic) {
pl, pl, pl, pl,
US_RTP_OPUS_HZ, US_RTP_OPUS_CH, US_RTP_OPUS_HZ, US_RTP_OPUS_CH,
pl, pl,
rtpa->rtp->ssrc, audio_ssrc,
(mic ? "sendrecv" : "sendonly")); dir);
} else {
audio_sdp = us_strdup("");
} }
char *sdp; char *sdp;

View File

@@ -24,8 +24,5 @@
#include "uslibs/types.h" #include "uslibs/types.h"
#include "rtpv.h"
#include "rtpa.h"
char *us_sdp_create(u32 video_ssrc, u32 audio_ssrc, bool acap, bool aplay);
char *us_sdp_create(us_rtpv_s *rtpv, us_rtpa_s *rtpa, bool mic);

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer-dump. .\" Manpage for ustreamer-dump.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos .\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER-DUMP 1 "version 6.56" "January 2021" .TH USTREAMER-DUMP 1 "version 6.59" "January 2021"
.SH NAME .SH NAME
ustreamer-dump \- Dump uStreamer's memory sink to file ustreamer-dump \- Dump uStreamer's memory sink to file

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer. .\" Manpage for ustreamer.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos .\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER 1 "version 6.56" "November 2020" .TH USTREAMER 1 "version 6.59" "November 2020"
.SH NAME .SH NAME
ustreamer \- stream MJPEG video from any V4L2 device to the network ustreamer \- stream MJPEG video from any V4L2 device to the network

View File

@@ -3,7 +3,7 @@
pkgname=ustreamer pkgname=ustreamer
pkgver=6.56 pkgver=6.59
pkgrel=1 pkgrel=1
pkgdesc="Lightweight and fast MJPEG-HTTP streamer" pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
url="https://github.com/pikvm/ustreamer" url="https://github.com/pikvm/ustreamer"
@@ -19,7 +19,7 @@ _options="WITH_GPIO=1 WITH_SYSTEMD=1"
if [ -e /usr/bin/python3 ]; then if [ -e /usr/bin/python3 ]; then
_options="$_options WITH_PYTHON=1" _options="$_options WITH_PYTHON=1"
depends+=("python>=3.14" "python<3.15") depends+=("python>=3.14" "python<3.15")
makedepends+=(python-setuptools python-pip python-build python-wheel) makedepends+=(python-setuptools python-pip python-build python-wheel python-installer)
fi fi
if [ -e /usr/include/janus/plugins/plugin.h ];then if [ -e /usr/include/janus/plugins/plugin.h ];then
depends+=(janus-gateway alsa-lib opus) depends+=(janus-gateway alsa-lib opus)

View File

@@ -9,7 +9,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=ustreamer PKG_NAME:=ustreamer
PKG_VERSION:=6.56 PKG_VERSION:=6.59
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com> PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>

View File

@@ -9,13 +9,12 @@ all: root
root: $(shell find src -type f,l) setup.py root: $(shell find src -type f,l) setup.py
$(info == PY_BUILD ustreamer-*.so) $(info == PY_BUILD ustreamer-*.so)
rm -rf root rm -rf root
$(ECHO) $(PY) -m build --skip-dependency-check --no-isolation $(ECHO) $(PY) -m build --wheel --skip-dependency-check --no-isolation
$(ECHO) $(PY) -m pip install dist/*.whl --ignore-installed --root=./root
install: install:
$(PY) -m pip install dist/*.whl --ignore-installed --prefix=$(PREFIX) --root=$(if $(R_DESTDIR),$(R_DESTDIR),/) $(PY) -m installer --prefix=$(PREFIX) $(if $(R_DESTDIR),--destdir=$(R_DESTDIR),) dist/*.whl
clean: clean:
rm -rf root dist ustreamer.egg-info rm -rf root dist build ustreamer.egg-info

View File

@@ -34,7 +34,7 @@ def main() -> None:
flags = _find_flags() flags = _find_flags()
setup( setup(
name="ustreamer", name="ustreamer",
version="6.56", version="6.59",
description="uStreamer tools", description="uStreamer tools",
author="Maxim Devaev", author="Maxim Devaev",
author_email="mdevaev@gmail.com", author_email="mdevaev@gmail.com",

View File

@@ -26,7 +26,7 @@
#define US_VERSION_MAJOR 6 #define US_VERSION_MAJOR 6
#define US_VERSION_MINOR 56 #define US_VERSION_MINOR 59
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor #define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor) #define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)

View File

@@ -216,15 +216,11 @@ static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg,
US_CALLOC(line_buf, frame->width * 3); US_CALLOC(line_buf, frame->width * 3);
const uint padding = us_frame_get_padding(frame); const uint padding = us_frame_get_padding(frame);
const uint image_size = frame->width * frame->height; const uint luma_array_size = (frame->width + padding) * frame->height;
const uint chroma_array_size = (frame->used - image_size) / 2; const uint chroma_array_size = (frame->used - luma_array_size) >> 1;
const uint chroma_matrix_order = (image_size / chroma_array_size) == 16 ? 4 : 2;
const u8 *data = frame->data; const u8 *data = frame->data;
const u8 *chroma1_data = frame->data + image_size; const u8 *chroma1_data = frame->data + luma_array_size;
const u8 *chroma2_data = frame->data + image_size + chroma_array_size; const u8 *chroma2_data = frame->data + luma_array_size + chroma_array_size;
//US_LOG_DEBUG("Planar data: Image Size %u, Chroma Array Size %u, Chroma Matrix Order %u",
// image_size, chroma_array_size, chroma_matrix_order);
while (jpeg->next_scanline < frame->height) { while (jpeg->next_scanline < frame->height) {
u8 *ptr = line_buf; u8 *ptr = line_buf;
@@ -234,7 +230,7 @@ static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg,
u8 y = data[x]; u8 y = data[x];
u8 u; u8 u;
u8 v; u8 v;
uint chroma_position = x / chroma_matrix_order; uint chroma_position = x >> 1;
switch (frame->format) { switch (frame->format) {
case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUV420:
@@ -257,9 +253,9 @@ static void _jpeg_write_scanlines_yuv_planar(struct jpeg_compress_struct *jpeg,
data += frame->width + padding; data += frame->width + padding;
if (jpeg->next_scanline > 0 && jpeg->next_scanline % chroma_matrix_order == 0) { if (jpeg->next_scanline > 0 && jpeg->next_scanline % 2 == 0) {
chroma1_data += (frame->width + padding) / chroma_matrix_order; chroma1_data += (frame->width + padding) >> 1;
chroma2_data += (frame->width + padding) / chroma_matrix_order; chroma2_data += (frame->width + padding) >> 1;
} }
JSAMPROW scanlines[1] = {line_buf}; JSAMPROW scanlines[1] = {line_buf};