fix live555 reference not found and use new project construction
This commit is contained in:
		
							
								
								
									
										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)
 | 
			
		||||
							
								
								
									
										22
									
								
								src/modules/rtsp/BaseDeviceSource.hh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										22
									
								
								src/modules/rtsp/BaseDeviceSource.hh
									
									
									
									
									
										Executable file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										369
									
								
								src/modules/rtsp/JpegFrameParser.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										369
									
								
								src/modules/rtsp/JpegFrameParser.cpp
									
									
									
									
									
										Executable file
									
								
							@@ -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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										81
									
								
								src/modules/rtsp/JpegFrameParser.hh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										81
									
								
								src/modules/rtsp/JpegFrameParser.hh
									
									
									
									
									
										Executable file
									
								
							@@ -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 */
 | 
			
		||||
							
								
								
									
										75
									
								
								src/modules/rtsp/LiveServerMediaSubsession.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										75
									
								
								src/modules/rtsp/LiveServerMediaSubsession.cpp
									
									
									
									
									
										Executable file
									
								
							@@ -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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								src/modules/rtsp/LiveServerMediaSubsession.hh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								src/modules/rtsp/LiveServerMediaSubsession.hh
									
									
									
									
									
										Executable file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										95
									
								
								src/modules/rtsp/MJPEGDeviceSource.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										95
									
								
								src/modules/rtsp/MJPEGDeviceSource.cpp
									
									
									
									
									
										Executable file
									
								
							@@ -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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								src/modules/rtsp/MJPEGDeviceSource.hh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										51
									
								
								src/modules/rtsp/MJPEGDeviceSource.hh
									
									
									
									
									
										Executable file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										24
									
								
								src/modules/rtsp/build.mk
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										24
									
								
								src/modules/rtsp/build.mk
									
									
									
									
									
										Executable file
									
								
							@@ -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))
 | 
			
		||||
							
								
								
									
										132
									
								
								src/modules/rtsp/rtsp_server.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										132
									
								
								src/modules/rtsp/rtsp_server.cpp
									
									
									
									
									
										Executable file
									
								
							@@ -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()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user