fix live555 reference not found and use new project construction
This commit is contained in:
parent
85cc102545
commit
6159bbb27d
37
live555.lua
37
live555.lua
|
@ -1,11 +1,23 @@
|
|||
package("live555")
|
||||
set_kind("library")
|
||||
set_homepage("http://www.live555.com")
|
||||
|
||||
set_urls("http://www.live555.com/liveMedia/public/live.$(version).tar.gz")
|
||||
add_versions("2024.11.28", "a9af16f46d2f4c7ccdbfc4b617480503d27cccb46fa5abb7dfd8a25951b44cc3")
|
||||
|
||||
add_configs("no_openssl", {description = "Set 1 if no OpenSSL", default = "1", values = {"0", "1"}})
|
||||
add_configs("no_std_lib", {description = "Set 1 if no C++20 STD LIB", default = "1", values = {"0", "1"}})
|
||||
|
||||
add_includedirs(
|
||||
"include/BasicUsageEnvironment",
|
||||
"include/groupsock",
|
||||
"include/liveMedia",
|
||||
"include/UsageEnvironment"
|
||||
)
|
||||
|
||||
local compile_opts = "COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 "
|
||||
|
||||
local make_scipt = [[
|
||||
COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -DNO_OPENSSL=1 -DNO_STD_LIB=1
|
||||
C = c
|
||||
C_COMPILER = cc
|
||||
C_FLAGS = $(COMPILE_OPTS) $(CPPFLAGS) $(CFLAGS)
|
||||
|
@ -24,16 +36,32 @@ LIBS_FOR_GUI_APPLICATION =
|
|||
EXE =
|
||||
]]
|
||||
|
||||
on_load(function (package)
|
||||
local no_openssl = package:config("no_openssl") or "1"
|
||||
local no_std_lib = package:config("no_std_lib") or "1"
|
||||
package:add("defines", "NO_OPENSSL=" .. no_openssl)
|
||||
package:add("defines", "NO_STD_LIB=" .. no_std_lib)
|
||||
end)
|
||||
|
||||
on_install(function(package)
|
||||
-- Create plat make script
|
||||
local no_openssl = package:config("no_openssl") or "1"
|
||||
local no_std_lib = package:config("no_std_lib") or "1"
|
||||
local script = io.open("config.cross", "w")
|
||||
if script then
|
||||
script:write(compile_opts)
|
||||
script:print("-DNO_OPENSSL=%s -DNO_STD_LIB=%s", no_openssl, no_std_lib)
|
||||
script:write(make_scipt)
|
||||
script:print("PREFIX = %s", package:installdir())
|
||||
script:close()
|
||||
end
|
||||
-- Don't forget to append space at the end of line "LIBRARY_LINK"
|
||||
if no_openssl == "0" then
|
||||
os.vrun("sed -i 's/LIBS_FOR_CONSOLE_APPLICATION =/& -lssl -lcrypto/g' config.cross")
|
||||
end
|
||||
--! Don't forget to append space at the end of line "LIBRARY_LINK"
|
||||
os.vrun("sed -i 's/ar cr/ar cr /g' config.cross")
|
||||
os.vrun("echo ----- Compile Opts -----")
|
||||
os.vrun("cat config.cross")
|
||||
|
||||
-- Generate makefile
|
||||
os.vrun("chmod +rw genMakefiles")
|
||||
|
@ -44,3 +72,8 @@ on_install(function(package)
|
|||
os.vrun("make clean")
|
||||
import("package.tools.make").install(package)
|
||||
end)
|
||||
|
||||
on_test(function (package)
|
||||
assert(package:has_cxxtypes("RTSPServer", {includes = "liveMedia.hh"}))
|
||||
end)
|
||||
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
package("live555")
|
||||
-- set_homepage("http://www.live555.com")
|
||||
--
|
||||
-- set_urls("http://www.live555.com/liveMedia/public/live.$(version).tar.gz")
|
||||
-- add_versions("2024.11.28", "a9af16f46d2f4c7ccdbfc4b617480503d27cccb46fa5abb7dfd8a25951b44cc3")
|
||||
|
||||
set_sourcedir(path.join(os.projectdir(), "Rouring/thirdparty/live555"))
|
||||
|
||||
local make_scipt = [[
|
||||
COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -DNO_OPENSSL=1
|
||||
C = c
|
||||
C_COMPILER = cc
|
||||
C_FLAGS = $(COMPILE_OPTS) $(CPPFLAGS) $(CFLAGS)
|
||||
CPP = cpp
|
||||
CPLUSPLUS_COMPILER = c++
|
||||
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1 $(CPPFLAGS) $(CXXFLAGS)
|
||||
OBJ = o
|
||||
LINK = c++ -o
|
||||
LINK_OPTS = -L. $(LDFLAGS)
|
||||
CONSOLE_LINK_OPTS = $(LINK_OPTS)
|
||||
LIBRARY_LINK = ar cr
|
||||
LIBRARY_LINK_OPTS =
|
||||
LIB_SUFFIX = a
|
||||
LIBS_FOR_CONSOLE_APPLICATION =
|
||||
LIBS_FOR_GUI_APPLICATION =
|
||||
EXE =
|
||||
]]
|
||||
|
||||
on_install(function(package)
|
||||
-- Create plat make script
|
||||
local script = io.open("config.cross", "w")
|
||||
if script then
|
||||
script:write(make_scipt)
|
||||
script:print("PREFIX = %s", package:installdir())
|
||||
script:close()
|
||||
end
|
||||
-- Don't forget to append space at the end of line "LIBRARY_LINK"
|
||||
os.vrun("sed -i 's/ar cr/ar cr /g' config.cross")
|
||||
|
||||
-- Generate makefile
|
||||
os.vrun("chmod +rw genMakefiles")
|
||||
os.vrun("chmod ugo+rwx config.cross")
|
||||
os.vrun("sed -i 's/\\/bin\\/rm/rm/g' genMakefiles")
|
||||
os.vrun("./genMakefiles cross")
|
||||
|
||||
-- Install
|
||||
os.vrun("make clean")
|
||||
import("package.tools.make").install(package)
|
||||
end)
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @file BaseDeviceSource.hh
|
||||
* @author 吴晨
|
||||
* @brief 适配 live555
|
||||
*
|
||||
* @copyright Copyright (c) 2022-2024 OurEDA
|
||||
*/
|
||||
|
||||
#ifndef _BASE_DEVICE_SOURCE_HH
|
||||
#define _BASE_DEVICE_SOURCE_HH
|
||||
|
||||
#include "msgbus/ipc_msg.h"
|
||||
|
||||
using FrameData = mpp_packet_t;
|
||||
|
||||
class BaseDeviceSource {
|
||||
public:
|
||||
virtual codec_type_t getCodecType() = 0;
|
||||
virtual void signalNewFrame(const FrameData &frameData) = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* Copyright (C) Peter Gaal
|
||||
* this code is derived from work of W.L. Chuang <ponponli2000 at gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "JpegFrameParser.hh"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <stdio.h>
|
||||
#define LOGGY(format, ...) fprintf (stderr, format, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOGGY(format, ...)
|
||||
#endif /* NDEBUG */
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
START_MARKER = 0xFF,
|
||||
SOI_MARKER = 0xD8,
|
||||
JFIF_MARKER = 0xE0,
|
||||
CMT_MARKER = 0xFE,
|
||||
DQT_MARKER = 0xDB,
|
||||
SOF_MARKER = 0xC0,
|
||||
DHT_MARKER = 0xC4,
|
||||
SOS_MARKER = 0xDA,
|
||||
EOI_MARKER = 0xD9,
|
||||
DRI_MARKER = 0xDD
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char id;
|
||||
unsigned char samp;
|
||||
unsigned char qt;
|
||||
} CompInfo;
|
||||
|
||||
|
||||
JpegFrameParser::JpegFrameParser() :
|
||||
_width(0), _height(0), _type(0),
|
||||
_precision(0), _qFactor(255),
|
||||
_qTables(NULL), _qTablesLength(0),
|
||||
_restartInterval(0),
|
||||
_scandata(NULL), _scandataLength(0)
|
||||
{
|
||||
_qTables = new unsigned char[128 * 2];
|
||||
memset(_qTables, 8, 128 * 2);
|
||||
}
|
||||
|
||||
JpegFrameParser::~JpegFrameParser()
|
||||
{
|
||||
if (_qTables != NULL) delete[] _qTables;
|
||||
}
|
||||
|
||||
unsigned int JpegFrameParser::scanJpegMarker(const unsigned char* data,
|
||||
unsigned int size,
|
||||
unsigned int* offset)
|
||||
{
|
||||
while ((data[(*offset)++] != START_MARKER) && ((*offset) < size));
|
||||
|
||||
if ((*offset) >= size) {
|
||||
return EOI_MARKER;
|
||||
} else {
|
||||
unsigned int marker;
|
||||
|
||||
marker = data[*offset];
|
||||
(*offset)++;
|
||||
|
||||
return marker;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int _jpegHeaderSize(const unsigned char* data, unsigned int offset)
|
||||
{
|
||||
return data[offset] << 8 | data[offset + 1];
|
||||
}
|
||||
|
||||
int JpegFrameParser::readSOF(const unsigned char* data, unsigned int size,
|
||||
unsigned int* offset)
|
||||
{
|
||||
int i, j;
|
||||
CompInfo elem;
|
||||
CompInfo info[3] = { {0,}, };
|
||||
unsigned int sof_size, off;
|
||||
unsigned int width, height, infolen;
|
||||
|
||||
off = *offset;
|
||||
|
||||
/* we need at least 17 bytes for the SOF */
|
||||
if (off + 17 > size) goto wrong_size;
|
||||
|
||||
sof_size = _jpegHeaderSize(data, off);
|
||||
if (sof_size < 17) goto wrong_length;
|
||||
|
||||
*offset += sof_size;
|
||||
|
||||
/* skip size */
|
||||
off += 2;
|
||||
|
||||
/* precision should be 8 */
|
||||
if (data[off++] != 8) goto bad_precision;
|
||||
|
||||
/* read dimensions */
|
||||
height = data[off] << 8 | data[off + 1];
|
||||
width = data[off + 2] << 8 | data[off + 3];
|
||||
off += 4;
|
||||
|
||||
if (height == 0 || height > 2040) goto invalid_dimension;
|
||||
if (width == 0 || width > 2040) goto invalid_dimension;
|
||||
|
||||
_width = width / 8;
|
||||
_height = height / 8;
|
||||
|
||||
/* we only support 3 components */
|
||||
if (data[off++] != 3) goto bad_components;
|
||||
|
||||
infolen = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
elem.id = data[off++];
|
||||
elem.samp = data[off++];
|
||||
elem.qt = data[off++];
|
||||
|
||||
/* insertion sort from the last element to the first */
|
||||
for (j = infolen; j > 1; j--) {
|
||||
if (info[j - 1].id < elem.id) break;
|
||||
info[j] = info[j - 1];
|
||||
}
|
||||
info[j] = elem;
|
||||
infolen++;
|
||||
}
|
||||
|
||||
/* see that the components are supported */
|
||||
if (info[0].samp == 0x21) {
|
||||
_type = 0;
|
||||
} else if (info[0].samp == 0x22) {
|
||||
_type = 1;
|
||||
} else {
|
||||
goto invalid_comp;
|
||||
}
|
||||
|
||||
if (!(info[1].samp == 0x11)) goto invalid_comp;
|
||||
if (!(info[2].samp == 0x11)) goto invalid_comp;
|
||||
// if (info[1].qt != info[2].qt) goto invalid_comp;
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
wrong_size:
|
||||
LOGGY("Wrong SOF size\n");
|
||||
return -1;
|
||||
|
||||
wrong_length:
|
||||
LOGGY("Wrong SOF length\n");
|
||||
return -1;
|
||||
|
||||
bad_precision:
|
||||
LOGGY("Bad precision\n");
|
||||
return -1;
|
||||
|
||||
invalid_dimension:
|
||||
LOGGY("Invalid dimension\n");
|
||||
return -1;
|
||||
|
||||
bad_components:
|
||||
LOGGY("Bad component\n");
|
||||
return -1;
|
||||
|
||||
invalid_comp:
|
||||
LOGGY("Invalid component\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int JpegFrameParser::readDQT(const unsigned char* data,
|
||||
unsigned int size,
|
||||
unsigned int offset)
|
||||
{
|
||||
unsigned int quant_size, tab_size;
|
||||
unsigned char prec;
|
||||
unsigned char id;
|
||||
|
||||
if (offset + 2 > size) goto too_small;
|
||||
|
||||
quant_size = _jpegHeaderSize(data, offset);
|
||||
if (quant_size < 2) goto small_quant_size;
|
||||
|
||||
/* clamp to available data */
|
||||
if (offset + quant_size > size) {
|
||||
quant_size = size - offset;
|
||||
}
|
||||
|
||||
offset += 2;
|
||||
quant_size -= 2;
|
||||
|
||||
while (quant_size > 0) {
|
||||
/* not enough to read the id */
|
||||
if (offset + 1 > size) break;
|
||||
|
||||
id = data[offset] & 0x0f;
|
||||
if (id == 15) goto invalid_id;
|
||||
|
||||
prec = (data[offset] & 0xf0) >> 4;
|
||||
if (prec) {
|
||||
tab_size = 128;
|
||||
_qTablesLength = 128 * 2;
|
||||
} else {
|
||||
tab_size = 64;
|
||||
_qTablesLength = 64 * 2;
|
||||
}
|
||||
|
||||
/* there is not enough for the table */
|
||||
if (quant_size < tab_size + 1) goto no_table;
|
||||
|
||||
//LOGGY("Copy quantization table: %u\n", id);
|
||||
memcpy(&_qTables[id * tab_size], &data[offset + 1], tab_size);
|
||||
|
||||
tab_size += 1;
|
||||
quant_size -= tab_size;
|
||||
offset += tab_size;
|
||||
}
|
||||
|
||||
done:
|
||||
return offset + quant_size;
|
||||
|
||||
/* ERRORS */
|
||||
too_small:
|
||||
LOGGY("DQT is too small\n");
|
||||
return size;
|
||||
|
||||
small_quant_size:
|
||||
LOGGY("Quantization table is too small\n");
|
||||
return size;
|
||||
|
||||
invalid_id:
|
||||
LOGGY("Invalid table ID\n");
|
||||
goto done;
|
||||
|
||||
no_table:
|
||||
LOGGY("table doesn't exist\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
int JpegFrameParser::readDRI(const unsigned char* data,
|
||||
unsigned int size, unsigned int* offset)
|
||||
{
|
||||
unsigned int dri_size, off;
|
||||
|
||||
off = *offset;
|
||||
|
||||
/* we need at least 4 bytes for the DRI */
|
||||
if (off + 4 > size) goto wrong_size;
|
||||
|
||||
dri_size = _jpegHeaderSize(data, off);
|
||||
if (dri_size < 4) goto wrong_length;
|
||||
|
||||
*offset += dri_size;
|
||||
off += 2;
|
||||
|
||||
_restartInterval = (data[off] << 8) | data[off + 1];
|
||||
|
||||
return 0;
|
||||
|
||||
wrong_size:
|
||||
return -1;
|
||||
|
||||
wrong_length:
|
||||
*offset += dri_size;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int JpegFrameParser::parse(unsigned char* data, unsigned int size)
|
||||
{
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
_type = 0;
|
||||
_precision = 0;
|
||||
//_qFactor = 0;
|
||||
_restartInterval = 0,
|
||||
|
||||
_scandata = NULL;
|
||||
_scandataLength = 0;
|
||||
|
||||
unsigned int offset = 0;
|
||||
unsigned int dqtFound = 0;
|
||||
unsigned int sosFound = 0;
|
||||
unsigned int sofFound = 0;
|
||||
unsigned int driFound = 0;
|
||||
unsigned int jpeg_header_size = 0;
|
||||
|
||||
while ((sosFound == 0) && (offset < size)) {
|
||||
switch (scanJpegMarker(data, size, &offset)) {
|
||||
case JFIF_MARKER:
|
||||
case CMT_MARKER:
|
||||
case DHT_MARKER:
|
||||
offset += _jpegHeaderSize(data, offset);
|
||||
break;
|
||||
case SOF_MARKER:
|
||||
if (readSOF(data, size, &offset) != 0) {
|
||||
goto invalid_format;
|
||||
}
|
||||
sofFound = 1;
|
||||
break;
|
||||
case DQT_MARKER:
|
||||
offset = readDQT(data, size, offset);
|
||||
dqtFound = 1;
|
||||
break;
|
||||
case SOS_MARKER:
|
||||
sosFound = 1;
|
||||
jpeg_header_size = offset + _jpegHeaderSize(data, offset);
|
||||
break;
|
||||
case EOI_MARKER:
|
||||
/* EOI reached before SOS!? */
|
||||
LOGGY("EOI reached before SOS!?\n");
|
||||
break;
|
||||
case SOI_MARKER:
|
||||
//LOGGY("SOI found\n");
|
||||
break;
|
||||
case DRI_MARKER:
|
||||
LOGGY("DRI found\n");
|
||||
if (readDRI(data, size, &offset) == 0) {
|
||||
driFound = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((dqtFound == 0) || (sofFound == 0)) {
|
||||
goto unsupported_jpeg;
|
||||
}
|
||||
|
||||
if (_width == 0 || _height == 0) {
|
||||
goto no_dimension;
|
||||
}
|
||||
|
||||
_scandata = data + jpeg_header_size;
|
||||
_scandataLength = size - jpeg_header_size;
|
||||
|
||||
if (driFound == 1) {
|
||||
_type += 64;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
/* ERRORS */
|
||||
unsupported_jpeg:
|
||||
return -1;
|
||||
|
||||
no_dimension:
|
||||
return -1;
|
||||
|
||||
invalid_format:
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (C) Peter Gaal
|
||||
* this code is derived from work of W.L. Chuang <ponponli2000 at gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef _JPEG_FRAME_PARSER_HH_INCLUDED
|
||||
#define _JPEG_FRAME_PARSER_HH_INCLUDED
|
||||
|
||||
|
||||
class JpegFrameParser
|
||||
{
|
||||
public:
|
||||
JpegFrameParser();
|
||||
virtual ~JpegFrameParser();
|
||||
|
||||
unsigned char width() { return _width; }
|
||||
unsigned char height() { return _height; }
|
||||
unsigned char type() { return _type; }
|
||||
unsigned char precision() { return _precision; }
|
||||
unsigned char qFactor() { return _qFactor; }
|
||||
|
||||
unsigned short restartInterval() { return _restartInterval; }
|
||||
|
||||
unsigned char const* quantizationTables(unsigned short& length)
|
||||
{
|
||||
length = _qTablesLength;
|
||||
return _qTables;
|
||||
}
|
||||
|
||||
int parse(unsigned char* data, unsigned int size);
|
||||
|
||||
unsigned char const* scandata(unsigned int& length)
|
||||
{
|
||||
length = _scandataLength;
|
||||
|
||||
return _scandata;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int scanJpegMarker(const unsigned char* data,
|
||||
unsigned int size,
|
||||
unsigned int* offset);
|
||||
int readSOF(const unsigned char* data,
|
||||
unsigned int size, unsigned int* offset);
|
||||
unsigned int readDQT(const unsigned char* data,
|
||||
unsigned int size, unsigned int offset);
|
||||
int readDRI(const unsigned char* data,
|
||||
unsigned int size, unsigned int* offset);
|
||||
|
||||
private:
|
||||
unsigned char _width;
|
||||
unsigned char _height;
|
||||
unsigned char _type;
|
||||
unsigned char _precision;
|
||||
unsigned char _qFactor;
|
||||
|
||||
unsigned char* _qTables;
|
||||
unsigned short _qTablesLength;
|
||||
|
||||
unsigned short _restartInterval;
|
||||
|
||||
unsigned char* _scandata;
|
||||
unsigned int _scandataLength;
|
||||
};
|
||||
|
||||
|
||||
#endif /* _JPEG_FRAME_PARSER_HH_INCLUDED */
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* @file LiveServerMediaSubsession.cpp
|
||||
* @author 吴晨
|
||||
* @brief 适配 live555
|
||||
*
|
||||
* @copyright Copyright (c) 2022-2024 OurEDA
|
||||
*/
|
||||
|
||||
#include "LiveServerMediaSubsession.hh"
|
||||
|
||||
#include "liveMedia.hh"
|
||||
|
||||
#include "BaseDeviceSource.hh"
|
||||
#include "H26XDeviceSource.hh"
|
||||
#include "MJPEGDeviceSource.hh"
|
||||
|
||||
LiveServerMediaSubsession *LiveServerMediaSubsession::createNew(UsageEnvironment &env, codec_type_t codecType) {
|
||||
return new LiveServerMediaSubsession(env, codecType);
|
||||
}
|
||||
|
||||
LiveServerMediaSubsession::LiveServerMediaSubsession(UsageEnvironment &env, codec_type_t codecType)
|
||||
: OnDemandServerMediaSubsession(env, True), fCodecType(codecType), fDeviceSource(NULL) {}
|
||||
|
||||
LiveServerMediaSubsession::~LiveServerMediaSubsession() {}
|
||||
|
||||
FramedSource *LiveServerMediaSubsession::createNewStreamSource(unsigned clientSessionId, unsigned &estBitrate) {
|
||||
if (fCodecType == ROV_H264) {
|
||||
estBitrate = 3000; // kbps, estimate
|
||||
H26XDeviceSource *streamSource = H26XDeviceSource::createNew(envir(), False);
|
||||
fDeviceSource = streamSource;
|
||||
return H264VideoStreamDiscreteFramer::createNew(envir(), streamSource, True);
|
||||
} else if (fCodecType == ROV_H265) {
|
||||
estBitrate = 2500;
|
||||
H26XDeviceSource *streamSource = H26XDeviceSource::createNew(envir(), True);
|
||||
fDeviceSource = streamSource;
|
||||
return H265VideoStreamDiscreteFramer::createNew(envir(), streamSource, True);
|
||||
} else if (fCodecType == ROV_JPEG) {
|
||||
estBitrate = 8000;
|
||||
MJPEGDeviceSource *streamSource = MJPEGDeviceSource::createNew(envir());
|
||||
fDeviceSource = streamSource;
|
||||
return streamSource;
|
||||
} else if (fCodecType == ROV_AAC) {
|
||||
estBitrate = 64;
|
||||
return NULL;
|
||||
} else if (fCodecType == ROV_G711A || fCodecType == ROV_G711U) {
|
||||
estBitrate = 128;
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RTPSink *LiveServerMediaSubsession::createNewRTPSink(Groupsock *rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource *inputSource) {
|
||||
if (fCodecType == ROV_H264) {
|
||||
OutPacketBuffer::increaseMaxSizeTo(512 * 1024);
|
||||
return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
|
||||
} else if (fCodecType == ROV_H265) {
|
||||
OutPacketBuffer::increaseMaxSizeTo(512 * 1024);
|
||||
return H265VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
|
||||
} else if (fCodecType == ROV_JPEG) {
|
||||
OutPacketBuffer::increaseMaxSizeTo(2 * 512 * 1024);
|
||||
return JPEGVideoRTPSink::createNew(envir(), rtpGroupsock);
|
||||
} else if (fCodecType == ROV_AAC) {
|
||||
return SimpleRTPSink::createNew(envir(), rtpGroupsock, 0, 8000, "audio", "AAC", 1, False);
|
||||
} else if (fCodecType == ROV_G711A) {
|
||||
return SimpleRTPSink::createNew(envir(), rtpGroupsock, 0, 8000, "audio", "PCMA", 1, False);
|
||||
} else if (fCodecType == ROV_G711U) {
|
||||
return SimpleRTPSink::createNew(envir(), rtpGroupsock, 0, 8000, "audio", "PCMU", 1, False);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void LiveServerMediaSubsession::closeStreamSource(FramedSource *inputSource) {
|
||||
fDeviceSource = NULL;
|
||||
OnDemandServerMediaSubsession::closeStreamSource(inputSource);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @file LiveServerMediaSubsession.hh
|
||||
* @author 吴晨
|
||||
* @brief 适配 live555
|
||||
*
|
||||
* @copyright Copyright (c) 2022-2024 OurEDA
|
||||
*/
|
||||
|
||||
#ifndef _LIVE_SERVER_MEDIA_SUBSESSION_HH
|
||||
#define _LIVE_SERVER_MEDIA_SUBSESSION_HH
|
||||
|
||||
#include "OnDemandServerMediaSubsession.hh"
|
||||
|
||||
#include "BaseDeviceSource.hh"
|
||||
|
||||
class LiveServerMediaSubsession : public OnDemandServerMediaSubsession {
|
||||
public:
|
||||
static LiveServerMediaSubsession *createNew(UsageEnvironment &env, codec_type_t codecType);
|
||||
BaseDeviceSource *deviceSource() { return fDeviceSource; }
|
||||
|
||||
protected:
|
||||
LiveServerMediaSubsession(UsageEnvironment &env, codec_type_t codecType);
|
||||
virtual ~LiveServerMediaSubsession();
|
||||
virtual FramedSource *createNewStreamSource(unsigned clientSessionId, unsigned &estBitrate);
|
||||
virtual RTPSink *createNewRTPSink(Groupsock *rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource *inputSource);
|
||||
virtual void closeStreamSource(FramedSource* inputSource);
|
||||
|
||||
private:
|
||||
codec_type_t fCodecType;
|
||||
BaseDeviceSource *fDeviceSource;
|
||||
};
|
||||
#endif
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* @file MJPEGDeviceSource.cpp
|
||||
* @author 吴晨
|
||||
* @brief 适配 live555
|
||||
*
|
||||
* @copyright Copyright (c) 2022-2024 OurEDA
|
||||
*/
|
||||
|
||||
#include "MJPEGDeviceSource.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include "GroupsockHelper.hh"
|
||||
|
||||
unsigned MJPEGDeviceSource::referenceCount = 0;
|
||||
EventTriggerId MJPEGDeviceSource::eventTriggerId = 0;
|
||||
|
||||
MJPEGDeviceSource *MJPEGDeviceSource::createNew(UsageEnvironment &env) {
|
||||
return new MJPEGDeviceSource(env);
|
||||
}
|
||||
|
||||
MJPEGDeviceSource::MJPEGDeviceSource(UsageEnvironment &env)
|
||||
: JPEGVideoSource(env), parser(), frameDataQueue(3) {
|
||||
if (eventTriggerId == 0) {
|
||||
eventTriggerId = envir().taskScheduler().createEventTrigger([](void *clientData) {
|
||||
((MJPEGDeviceSource *) clientData)->deliverFrame();
|
||||
});
|
||||
}
|
||||
++referenceCount;
|
||||
}
|
||||
|
||||
MJPEGDeviceSource::~MJPEGDeviceSource() {
|
||||
--referenceCount;
|
||||
if (referenceCount == 0) {
|
||||
envir().taskScheduler().deleteEventTrigger(eventTriggerId);
|
||||
eventTriggerId = 0;
|
||||
}
|
||||
FrameData frameData;
|
||||
while (frameDataQueue.try_dequeue(frameData)) {
|
||||
delete[] frameData.data;
|
||||
}
|
||||
}
|
||||
|
||||
codec_type_t MJPEGDeviceSource::getCodecType() {
|
||||
return ROV_JPEG;
|
||||
}
|
||||
|
||||
void MJPEGDeviceSource::signalNewFrame(const FrameData &frameData) {
|
||||
if (!frameDataQueue.try_enqueue(frameData)) {
|
||||
envir() << "Stream data queue full!\n";
|
||||
delete[] frameData.data;
|
||||
}
|
||||
envir().taskScheduler().triggerEvent(eventTriggerId, this);
|
||||
}
|
||||
|
||||
unsigned MJPEGDeviceSource::maxFrameSize() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MJPEGDeviceSource::doGetNextFrame() {
|
||||
if (frameDataQueue.size_approx() > 0) {
|
||||
deliverFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void MJPEGDeviceSource::deliverFrame() {
|
||||
if (!isCurrentlyAwaitingData()) return;
|
||||
|
||||
FrameData buffer;
|
||||
if (!frameDataQueue.try_dequeue(buffer)) return;
|
||||
|
||||
if (parser.parse(buffer.data, buffer.length) != 0) {
|
||||
delete[] buffer.data;
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer.length > fMaxSize) {
|
||||
fFrameSize = fMaxSize;
|
||||
fNumTruncatedBytes = buffer.length - fMaxSize;
|
||||
} else {
|
||||
fFrameSize = buffer.length;
|
||||
}
|
||||
|
||||
if (buffer.timestamp == 0) {
|
||||
gettimeofday(&fPresentationTime, NULL);
|
||||
} else {
|
||||
fPresentationTime.tv_sec = buffer.timestamp / 1000000;
|
||||
fPresentationTime.tv_usec = buffer.timestamp % 1000000;
|
||||
}
|
||||
|
||||
memcpy(fTo, buffer.data, fFrameSize);
|
||||
delete[] buffer.data;
|
||||
|
||||
afterGetting(this);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* @file MJPEGDeviceSource.hh
|
||||
* @author 吴晨
|
||||
* @brief 适配 live555
|
||||
*
|
||||
* @copyright Copyright (c) 2022-2024 OurEDA
|
||||
*/
|
||||
|
||||
#ifndef _MJPEG_DEVICE_SOURCE_HH
|
||||
#define _MJPEG_DEVICE_SOURCE_HH
|
||||
|
||||
#include "JPEGVideoSource.hh"
|
||||
#include "readerwritercircularbuffer.h"
|
||||
|
||||
#include "BaseDeviceSource.hh"
|
||||
#include "JpegFrameParser.hh"
|
||||
|
||||
class MJPEGDeviceSource : public JPEGVideoSource, public BaseDeviceSource {
|
||||
public:
|
||||
static MJPEGDeviceSource *createNew(UsageEnvironment &env);
|
||||
|
||||
public:
|
||||
virtual u_int8_t type() { return parser.type(); }
|
||||
virtual u_int8_t qFactor() { return parser.qFactor(); }
|
||||
virtual u_int8_t width() { return parser.width(); }
|
||||
virtual u_int8_t height() { return parser.height(); }
|
||||
virtual u_int8_t const *quantizationTables(u_int8_t &precision, u_int16_t &length) {
|
||||
precision = parser.precision();
|
||||
return parser.quantizationTables(length);
|
||||
}
|
||||
virtual u_int16_t restartInterval() { return parser.restartInterval(); }
|
||||
virtual codec_type_t getCodecType();
|
||||
virtual void signalNewFrame(const FrameData &frameData);
|
||||
|
||||
protected:
|
||||
MJPEGDeviceSource(UsageEnvironment &env);
|
||||
virtual ~MJPEGDeviceSource();
|
||||
virtual unsigned maxFrameSize() const;
|
||||
virtual void doGetNextFrame();
|
||||
|
||||
private:
|
||||
void deliverFrame();
|
||||
|
||||
private:
|
||||
JpegFrameParser parser;
|
||||
moodycamel::BlockingReaderWriterCircularBuffer<FrameData> frameDataQueue;
|
||||
static unsigned referenceCount;
|
||||
static EventTriggerId eventTriggerId;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# rtsp/build.mk
|
||||
# rtsp 模块构建脚本
|
||||
#
|
||||
# Copyright (c) 2022-2024 OurEDA
|
||||
#
|
||||
|
||||
TARGET := rtsp
|
||||
CUR_INCS := -I$(CUR_SRC_DIR) \
|
||||
-I$(ROV_HOME)/configs \
|
||||
-I$(ROV_HOME)/common \
|
||||
-I$(ROV_HOME)/modules \
|
||||
-I$(ROV_HOME)/modules/tcp/generated \
|
||||
-I$(ROV_OUT_DIR)/include \
|
||||
-I$(ROV_OUT_DIR)/include/groupsock \
|
||||
-I$(ROV_OUT_DIR)/include/BasicUsageEnvironment \
|
||||
-I$(ROV_OUT_DIR)/include/UsageEnvironment \
|
||||
-I$(ROV_OUT_DIR)/include/liveMedia
|
||||
CUR_SRCS := $(wildcard $(CUR_SRC_DIR)/*.c) \
|
||||
$(wildcard $(CUR_SRC_DIR)/*.cpp)
|
||||
CUR_STATIC_LIBS := -lliveMedia -lUsageEnvironment -lBasicUsageEnvironment -lgroupsock
|
||||
CUR_DEPS := live555 readerwriterqueue mpp
|
||||
|
||||
$(call build_module,$(TARGET))
|
|
@ -0,0 +1,132 @@
|
|||
/**
|
||||
* @file rtsp_server.cpp
|
||||
* @author 吴晨
|
||||
* @brief 基于 live555 的 RTSP 服务器
|
||||
*
|
||||
* @copyright Copyright (c) 2022-2024 OurEDA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include "BasicUsageEnvironment.hh"
|
||||
#include "GroupsockHelper.hh"
|
||||
#include "liveMedia.hh"
|
||||
|
||||
#include "module_init.h"
|
||||
#include "platform/log.h"
|
||||
#include "msgbus/ipc.h"
|
||||
#include "LiveServerMediaSubsession.hh"
|
||||
|
||||
static const char *stream_name = "main";
|
||||
static const char *description = "Session streamed by the main camera of rouring ROV";
|
||||
|
||||
static volatile char stop_running;
|
||||
static pthread_t rtsp_thread;
|
||||
static TaskScheduler *scheduler;
|
||||
static UsageEnvironment *env;
|
||||
static RTSPServer *rtspServer;
|
||||
static LiveServerMediaSubsession *videoSubsession;
|
||||
static LiveServerMediaSubsession *audioSubsession;
|
||||
|
||||
static void announceURL(RTSPServer *rtspServer, ServerMediaSession *sms) {
|
||||
if (rtspServer == NULL || sms == NULL) return; // sanuty check
|
||||
UsageEnvironment &env = rtspServer->envir();
|
||||
env << "Play this stream using the URL ";
|
||||
if (weHaveAnIPv4Address(env)) {
|
||||
char *url = rtspServer->ipv4rtspURL(sms);
|
||||
env << "\"" << url << "\"";
|
||||
delete[] url;
|
||||
if (weHaveAnIPv6Address(env)) env << " or ";
|
||||
}
|
||||
if (weHaveAnIPv6Address(env)) {
|
||||
char *url = rtspServer->ipv6rtspURL(sms);
|
||||
env << "\"" << url << "\"";
|
||||
delete[] url;
|
||||
}
|
||||
env << "\n";
|
||||
}
|
||||
|
||||
static void *rtsp_loop(void *arg) {
|
||||
UNUSED(arg);
|
||||
env->taskScheduler().doEventLoop(&stop_running);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void on_msg_received(ipc_request_t *req) {
|
||||
if (req->id == IPC_MSG_ID_RTSP_STREAM_VIDEO) {
|
||||
const ipc_msg_rtsp_stream_t *packet = (const ipc_msg_rtsp_stream_t *)req->msg;
|
||||
if (videoSubsession == nullptr || videoSubsession->deviceSource() == nullptr) {
|
||||
delete[] packet->data;
|
||||
ipc_reply(req, nullptr);
|
||||
return;
|
||||
}
|
||||
videoSubsession->deviceSource()->signalNewFrame(*packet);
|
||||
ipc_reply(req, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static bool rtsp_start() {
|
||||
LOG_DEBUG("Starting RTSP server...\n");
|
||||
ipc_register_callback(MOD_RTSP, on_msg_received);
|
||||
|
||||
scheduler = BasicTaskScheduler::createNew();
|
||||
env = BasicUsageEnvironment::createNew(*scheduler);
|
||||
rtspServer = RTSPServer::createNew(*env, 554, nullptr, 10U);
|
||||
if (rtspServer == nullptr) {
|
||||
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
ipc_msg_mpp_stream_info_req stream_info_req;
|
||||
stream_info_req.num = 0;
|
||||
ipc_request_t req = IPC_REQUEST_INIT(MOD_MPP);
|
||||
req.id = IPC_MSG_ID_MPP_STREAM_INFO;
|
||||
req.msg = (const ipc_msg_t *)&stream_info_req;
|
||||
req.length = sizeof(ipc_msg_mpp_stream_info_req);
|
||||
int result = ipc_send(&req);
|
||||
if (result != IPC_OK) {
|
||||
*env << "Failed to get live stream params\n";
|
||||
return false;
|
||||
}
|
||||
ipc_msg_mpp_stream_info_res *stream_info_res = &req.res->msg.mpp_stream_info_res;
|
||||
if (stream_info_res->video_codec < ROV_AUDIO) {
|
||||
videoSubsession = LiveServerMediaSubsession::createNew(*env, stream_info_res->video_codec);
|
||||
}
|
||||
if (stream_info_res->audio_codec > ROV_AUDIO) {
|
||||
audioSubsession = LiveServerMediaSubsession::createNew(*env, stream_info_res->audio_codec);
|
||||
}
|
||||
free(req.res);
|
||||
|
||||
ServerMediaSession *sms = ServerMediaSession::createNew(*env, stream_name, stream_name, description);
|
||||
if (videoSubsession != nullptr) {
|
||||
sms->addSubsession(videoSubsession);
|
||||
}
|
||||
if (audioSubsession != nullptr) {
|
||||
sms->addSubsession(audioSubsession);
|
||||
}
|
||||
rtspServer->addServerMediaSession(sms);
|
||||
announceURL(rtspServer, sms);
|
||||
|
||||
stop_running = 0;
|
||||
result = pthread_create(&rtsp_thread, NULL, rtsp_loop, NULL);
|
||||
if (result != 0) {
|
||||
*env << "Create RTSP server thread failed, result: " << result << "\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rtsp_stop() {
|
||||
stop_running = 1;
|
||||
pthread_join(rtsp_thread, NULL);
|
||||
videoSubsession = audioSubsession = nullptr;
|
||||
// 若销毁RTSP服务端时客户端未断开连接则不会关闭连接, 所以在销毁之前关闭一下
|
||||
rtspServer->closeAllClientSessionsForServerMediaSession(stream_name);
|
||||
Medium::close(rtspServer);
|
||||
if (env && !env->reclaim()) {
|
||||
fprintf(stderr, "!!! UsageEnvironment release failed !!!\n");
|
||||
}
|
||||
delete scheduler;
|
||||
}
|
||||
|
||||
MODULE_RUN(rtsp_start, rtsp_stop, 09);
|
59
xmake.lua
59
xmake.lua
|
@ -19,32 +19,43 @@ add_requires("live555 2024.11.28")
|
|||
add_requires("zlog 1.2.17", { system = false })
|
||||
|
||||
target("ISP")
|
||||
set_kind("static")
|
||||
add_files("src/isp/*.c")
|
||||
add_includedirs("src")
|
||||
add_deps("sample_common")
|
||||
add_packages("zlog")
|
||||
set_kind("static")
|
||||
add_files("src/modules/isp/*.c")
|
||||
add_includedirs("src")
|
||||
add_deps("sample_common")
|
||||
add_packages("zlog")
|
||||
target_end()
|
||||
|
||||
target("NNIE")
|
||||
set_kind("static")
|
||||
add_files("src/nnie/*.c")
|
||||
add_includedirs("src")
|
||||
add_deps("sample_common", "sample_svp")
|
||||
add_packages("zlog")
|
||||
set_kind("static")
|
||||
add_files("src/modules/nnie/*.c")
|
||||
add_includedirs("src")
|
||||
add_deps("sample_common", "sample_svp")
|
||||
add_packages("zlog")
|
||||
target_end()
|
||||
|
||||
target("RTSP")
|
||||
set_kind("static")
|
||||
add_files("src/modules/rtsp/*.cpp")
|
||||
add_includedirs("src")
|
||||
add_packages("live555", "zlog")
|
||||
add_links("pthread")
|
||||
target_end()
|
||||
|
||||
target("CatFeeder")
|
||||
set_kind("binary")
|
||||
add_includedirs("src")
|
||||
add_files("src/*.cpp")
|
||||
add_deps("hi_library", "ISP", "NNIE")
|
||||
add_packages("zlog")
|
||||
add_links("stdc++fs")
|
||||
after_build(function(target)
|
||||
os.cp("src/log.conf", "$(buildir)/cross/arm/release")
|
||||
end)
|
||||
set_kind("binary")
|
||||
add_includedirs("src")
|
||||
add_files("src/*.cpp")
|
||||
add_deps("hi_library", "ISP", "NNIE", "RTSP")
|
||||
add_packages("zlog")
|
||||
add_links("stdc++fs")
|
||||
after_build(function(target)
|
||||
os.cp("src/log.conf", "$(buildir)/cross/arm/release")
|
||||
end)
|
||||
|
||||
if is_mode("debug") then
|
||||
add_defines("DEBUG")
|
||||
set_symbols("debug")
|
||||
set_optimize("none")
|
||||
end
|
||||
if is_mode("debug") then
|
||||
add_defines("DEBUG")
|
||||
set_symbols("debug")
|
||||
set_optimize("none")
|
||||
end
|
||||
target_end()
|
||||
|
|
Loading…
Reference in New Issue