Compare commits

...

10 Commits
v5.27 ... v5.29

Author SHA1 Message Date
Maxim Devaev
95fcc3c58e Bump version: 5.28 → 5.29 2022-11-04 19:48:26 +03:00
Maxim Devaev
81500af1b3 renamed --key to --key-required 2022-11-04 19:47:50 +03:00
Maxim Devaev
86a2141361 janus: key_required handler 2022-11-04 13:51:49 +03:00
Maxim Devaev
22e5c8627b removed sps/pps from sdp 2022-11-03 22:54:06 +03:00
Maxim Devaev
c8600e62c2 Bump version: 5.27 → 5.28 2022-11-03 19:12:35 +03:00
Maxim Devaev
335f19f0e3 refactoring 2022-11-03 19:11:58 +03:00
Michael Lynch
a24e0eeb86 Document additional required audio packages (#184) 2022-11-03 19:11:26 +03:00
Michael Lynch
3fcb8d3ee5 Document additional required audio packages (#184) 2022-11-03 19:10:17 +03:00
Michael Lynch
ca30656bf8 Assign stream index on outgoing RTP packets (#182)
* Assign stream index on outgoing RTP packets (#5)

* Correctly assign mindex on outgoing rtp packets

Previously mindex was not set and defaulted to zero. This lead to most packets
getting dropped because of sequence number reuse when streaming both audio and
video. This reorders the SDP entries for video and audio so that video is first
in both a video-only and a audio+video configuration. This means that the mindex
for video packets should always be zero, and for audio (if present) should
always be one. This assumes there will never be an audio-only configuration.

* Adjust comments

Co-authored-by: Michael Lynch <git@mtlynch.io>

* Add preprocessor conditional to guard packet.mindex setting

The mindex field wasn't added to the janus_plugin_rtp_packet until Janus 1.0, so this change adds a precompiler check to ensure JANUS_PLUGIN_API_VERSION is >= 100 before assigning a value to the mindex field.

* Preserve audio-then-video ordering for Janus 0.x

Co-authored-by: Louis Goessling <louis@goessling.com>
2022-11-03 19:04:15 +03:00
Michael Lynch
4f0abf7eec Assign stream index on outgoing RTP packets (#182)
* Assign stream index on outgoing RTP packets (#5)

* Correctly assign mindex on outgoing rtp packets

Previously mindex was not set and defaulted to zero. This lead to most packets
getting dropped because of sequence number reuse when streaming both audio and
video. This reorders the SDP entries for video and audio so that video is first
in both a video-only and a audio+video configuration. This means that the mindex
for video packets should always be zero, and for audio (if present) should
always be one. This assumes there will never be an audio-only configuration.

* Adjust comments

Co-authored-by: Michael Lynch <git@mtlynch.io>

* Add preprocessor conditional to guard packet.mindex setting

The mindex field wasn't added to the janus_plugin_rtp_packet until Janus 1.0, so this change adds a precompiler check to ensure JANUS_PLUGIN_API_VERSION is >= 100 before assigning a value to the mindex field.

* Preserve audio-then-video ordering for Janus 0.x

Co-authored-by: Louis Goessling <louis@goessling.com>
2022-11-03 19:03:16 +03:00
15 changed files with 38 additions and 72 deletions

View File

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

View File

@@ -38,7 +38,7 @@ If you're going to live-stream from your backyard webcam and need to control it,
You'll need ```make```, ```gcc```, ```libevent``` with ```pthreads``` support, ```libjpeg9```/```libjpeg-turbo``` and ```libbsd``` (only for Linux).
* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev libbsd-dev`. Add `libgpiod-dev` for `WITH_GPIO=1` and `libsystemd-dev` for `WITH_SYSTEMD=1`.
* Raspbian: `sudo apt install libevent-dev libjpeg8-dev libbsd-dev`. Add `libgpiod-dev` for `WITH_GPIO=1` and `libsystemd-dev` for `WITH_SYSTEMD=1` and `libasound2-dev libspeex-dev libspeexdsp-dev libopus-dev` for `WITH_JANUS=1`.
* Debian/Ubuntu: `sudo apt install build-essential libevent-dev libjpeg-dev libbsd-dev`.
* Alpine: `sudo apk add libevent-dev libbsd-dev libjpeg-turbo-dev musl-dev`. Build with `WITH_PTHREAD_NP=0`.

View File

@@ -102,6 +102,11 @@ static void *_common_thread(void *v_client, bool video) {
packet.video = rtp->video;
packet.buffer = (char *)rtp->datagram;
packet.length = rtp->used;
# if JANUS_PLUGIN_API_VERSION >= 100
// The uStreamer Janus plugin places video in stream index 0 and audio
// (if available) in stream index 1.
packet.mindex = (rtp->video ? 0 : 1);
# endif
janus_plugin_rtp_extensions_reset(&packet.extensions);
// FIXME: Это очень эффективный способ уменьшить задержку, но WebRTC стек в хроме и фоксе
// слишком корявый, чтобы обработать это, из-за чего на кейфреймах начинаются заикания.

View File

@@ -154,9 +154,9 @@ static void *_video_sink_thread(UNUSED void *arg) {
if (frame == NULL) {
goto close_memsink;
}
// if (frame->key) {
// atomic_store(&_g_key_required, false);
// }
if (frame->key) {
atomic_store(&_g_key_required, false);
}
if (us_queue_put(_g_video_queue, frame, 0) != 0) {
_IF_NOT_REPORTED({ US_JLOG_PERROR("video", "Video queue is full"); });
us_frame_destroy(frame);
@@ -427,12 +427,7 @@ static struct janus_plugin_result *_plugin_handle_message(
} else if (!strcmp(request_str, "watch")) {
char *sdp;
{
// atomic_store(&_g_key_required, true);
char *const video_sdp = us_rtpv_make_sdp(_g_rtpv);
if (video_sdp == NULL) {
PUSH_ERROR(503, "Haven't received SPS/PPS from memsink yet");
goto ok_wait;
}
char *const audio_sdp = (_g_rtpa ? us_rtpa_make_sdp(_g_rtpa) : us_strdup(""));
US_ASPRINTF(sdp,
"v=0" RN
@@ -440,7 +435,16 @@ static struct janus_plugin_result *_plugin_handle_message(
"s=PiKVM uStreamer" RN
"t=0 0" RN
"%s%s",
us_get_now_id() >> 1, audio_sdp, video_sdp
us_get_now_id() >> 1,
# if JANUS_PLUGIN_API_VERSION >= 100
// Place video SDP before audio SDP so that the video and audio streams
// have predictable indices, even if audio is not available.
// See also client.c.
video_sdp, audio_sdp
# else
// For versions of Janus prior to 1.x, place the audio SDP first.
audio_sdp, video_sdp
# endif
);
free(audio_sdp);
free(video_sdp);
@@ -450,6 +454,10 @@ static struct janus_plugin_result *_plugin_handle_message(
PUSH_STATUS("started", offer_jsep);
json_decref(offer_jsep);
} else if (!strcmp(request_str, "key_required")) {
US_JLOG_INFO("main", "Got keyframe request from a client");
atomic_store(&_g_key_required, true);
} else {
PUSH_ERROR(405, "Not implemented");
}

View File

@@ -36,35 +36,15 @@ us_rtpv_s *us_rtpv_init(us_rtp_callback_f callback, bool zero_playout_delay) {
US_CALLOC(rtpv, 1);
rtpv->rtp = us_rtp_init(96, true, zero_playout_delay);
rtpv->callback = callback;
rtpv->sps = us_frame_init();
rtpv->pps = us_frame_init();
US_MUTEX_INIT(rtpv->mutex);
return rtpv;
}
void us_rtpv_destroy(us_rtpv_s *rtpv) {
US_MUTEX_DESTROY(rtpv->mutex);
us_frame_destroy(rtpv->pps);
us_frame_destroy(rtpv->sps);
us_rtp_destroy(rtpv->rtp);
free(rtpv);
}
char *us_rtpv_make_sdp(us_rtpv_s *rtpv) {
US_MUTEX_LOCK(rtpv->mutex);
if (rtpv->sps->used == 0 || rtpv->pps->used == 0) {
US_MUTEX_UNLOCK(rtpv->mutex);
return NULL;
}
char *sps = NULL;
char *pps = NULL;
us_base64_encode(rtpv->sps->data, rtpv->sps->used, &sps, NULL);
us_base64_encode(rtpv->pps->data, rtpv->pps->used, &pps, NULL);
US_MUTEX_UNLOCK(rtpv->mutex);
# define PAYLOAD rtpv->rtp->payload
// https://tools.ietf.org/html/rfc6184
// https://github.com/meetecho/janus-gateway/issues/2443
@@ -75,8 +55,6 @@ char *us_rtpv_make_sdp(us_rtpv_s *rtpv) {
"a=rtpmap:%u H264/90000" RN
"a=fmtp:%u profile-level-id=42E01F" RN
"a=fmtp:%u packetization-mode=1" RN
"a=fmtp:%u sprop-sps=%s" RN
"a=fmtp:%u sprop-pps=%s" RN
"a=rtcp-fb:%u nack" RN
"a=rtcp-fb:%u nack pli" RN
"a=rtcp-fb:%u goog-remb" RN
@@ -84,17 +62,12 @@ char *us_rtpv_make_sdp(us_rtpv_s *rtpv) {
"%s" // playout-delay
"a=sendonly" RN,
PAYLOAD, PAYLOAD, PAYLOAD, PAYLOAD,
PAYLOAD, sps,
PAYLOAD, pps,
PAYLOAD, PAYLOAD, PAYLOAD,
rtpv->rtp->ssrc,
(rtpv->rtp->zero_playout_delay ? "a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay" RN : "")
);
# undef PAYLOAD
free(sps);
free(pps);
return sdp;
# undef PAYLOAD
}
#define _PRE 3 // Annex B prefix length
@@ -136,22 +109,11 @@ void us_rtpv_wrap(us_rtpv_s *rtpv, const us_frame_s *frame) {
}
void _rtpv_process_nalu(us_rtpv_s *rtpv, const uint8_t *data, size_t size, uint32_t pts, bool marked) {
# define DG rtpv->rtp->datagram
const unsigned ref_idc = (data[0] >> 5) & 3;
const unsigned type = data[0] & 0x1F;
us_frame_s *ps = NULL;
switch (type) {
case 7: ps = rtpv->sps; break;
case 8: ps = rtpv->pps; break;
}
if (ps != NULL) {
US_MUTEX_LOCK(rtpv->mutex);
us_frame_set_data(ps, data, size);
US_MUTEX_UNLOCK(rtpv->mutex);
}
# define DG rtpv->rtp->datagram
if (size + US_RTP_HEADER_SIZE <= US_RTP_DATAGRAM_SIZE) {
us_rtp_write_header(rtpv->rtp, pts, marked);
memcpy(DG + US_RTP_HEADER_SIZE, data, size);

View File

@@ -31,12 +31,8 @@
#include <sys/types.h>
#include <linux/videodev2.h>
#include <pthread.h>
#include "uslibs/tools.h"
#include "uslibs/threading.h"
#include "uslibs/frame.h"
#include "uslibs/base64.h"
#include "rtp.h"
@@ -44,9 +40,6 @@
typedef struct {
us_rtp_s *rtp;
us_rtp_callback_f callback;
us_frame_s *sps; // Actually not a frame, just a bytes storage
us_frame_s *pps;
pthread_mutex_t mutex;
} us_rtpv_s;

View File

@@ -1 +0,0 @@
../../../src/libs/base64.c

View File

@@ -1 +0,0 @@
../../../src/libs/base64.h

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer-dump.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER-DUMP 1 "version 5.27" "January 2021"
.TH USTREAMER-DUMP 1 "version 5.29" "January 2021"
.SH NAME
ustreamer-dump \- Dump uStreamer's memory sink to file
@@ -45,7 +45,7 @@ Limit the number of frames. Default: 0 (infinite).
.BR \-i ", "\-\-interval\ \fIsec
Delay between reading frames (float). Default: 0.
.TP
.BR \-k ", " \-\-key
.BR \-k ", " \-\-key\-required
Request keyframe from the sink. Default: disabled.
.SS "Logging options"

View File

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

View File

@@ -3,7 +3,7 @@
pkgname=ustreamer
pkgver=5.27
pkgver=5.29
pkgrel=1
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
url="https://github.com/pikvm/ustreamer"

View File

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

View File

@@ -17,7 +17,7 @@ def _find_sources(suffix: str) -> list[str]:
if __name__ == "__main__":
setup(
name="ustreamer",
version="5.27",
version="5.29",
description="uStreamer tools",
author="Maxim Devaev",
author_email="mdevaev@gmail.com",

View File

@@ -48,7 +48,7 @@ enum _OPT_VALUES {
_O_OUTPUT_JSON = 'j',
_O_COUNT = 'c',
_O_INTERVAL = 'i',
_O_KEY = 'k',
_O_KEY_REQUIRED = 'k',
_O_HELP = 'h',
_O_VERSION = 'v',
@@ -68,7 +68,7 @@ static const struct option _LONG_OPTS[] = {
{"output-json", no_argument, NULL, _O_OUTPUT_JSON},
{"count", required_argument, NULL, _O_COUNT},
{"interval", required_argument, NULL, _O_INTERVAL},
{"key", no_argument, NULL, _O_KEY},
{"key-required", no_argument, NULL, _O_KEY_REQUIRED},
{"log-level", required_argument, NULL, _O_LOG_LEVEL},
{"perf", no_argument, NULL, _O_PERF},
@@ -154,7 +154,7 @@ int main(int argc, char *argv[]) {
case _O_OUTPUT_JSON: OPT_SET(output_json, true);
case _O_COUNT: OPT_NUMBER("--count", count, 0, LLONG_MAX, 0);
case _O_INTERVAL: OPT_LDOUBLE("--interval", interval, 0, 60);
case _O_KEY: OPT_SET(key_required, true);
case _O_KEY_REQUIRED: OPT_SET(key_required, true);
case _O_LOG_LEVEL: OPT_NUMBER("--log-level", us_g_log_level, US_LOG_LEVEL_INFO, US_LOG_LEVEL_DEBUG, 0);
case _O_PERF: OPT_SET(us_g_log_level, US_LOG_LEVEL_PERF);
@@ -332,7 +332,7 @@ static void _help(FILE *fp) {
SAY(" -j|--output-json ──────── Format output as JSON. Required option --output. Default: disabled.\n");
SAY(" -c|--count <N> ───────── Limit the number of frames. Default: 0 (infinite).\n");
SAY(" -i|--interval <sec> ───── Delay between reading frames (float). Default: 0.\n");
SAY(" -k|--key ──────────────── Request keyframe from the sink. Default: disabled.\n");
SAY(" -k|--key-required ─────── Request keyframe from the sink. Default: disabled.\n");
SAY("Logging options:");
SAY("════════════════");
SAY(" --log-level <N> ──── Verbosity level of messages from 0 (info) to 3 (debug).");

View File

@@ -23,7 +23,7 @@
#pragma once
#define US_VERSION_MAJOR 5
#define US_VERSION_MINOR 27
#define US_VERSION_MINOR 29
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)