forked from external-repos/esp32cam-rtsp
Work in progress
This commit is contained in:
18
lib/micro-jpg/include/jpg.h
Normal file
18
lib/micro-jpg/include/jpg.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include "jpg_section.h"
|
||||
|
||||
class jpg
|
||||
{
|
||||
public:
|
||||
bool decode(const uint8_t *jpg, size_t size);
|
||||
|
||||
const struct jpg_section *quantization_table_0_;
|
||||
const struct jpg_section *quantization_table_1_;
|
||||
|
||||
const uint8_t *jpeg_data_start;
|
||||
const uint8_t *jpeg_data_end;
|
||||
|
||||
private:
|
||||
static const jpg_section *find_jpg_section(const uint8_t **ptr, const uint8_t *end, jpg_section::jpg_section_flag flag);
|
||||
};
|
||||
103
lib/micro-jpg/include/jpg_section.h
Normal file
103
lib/micro-jpg/include/jpg_section.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct __attribute__((packed)) jpg_section
|
||||
{
|
||||
enum jpg_section_flag : uint8_t
|
||||
{
|
||||
DATA = 0x00,
|
||||
SOF0 = 0xc0,
|
||||
SOF1 = 0xc1,
|
||||
SOF2 = 0xc2,
|
||||
SOF3 = 0xc3,
|
||||
DHT = 0xc4,
|
||||
SOF5 = 0xc5,
|
||||
SOF6 = 0xc6,
|
||||
SOF7 = 0xc7,
|
||||
JPG = 0xc8,
|
||||
SOF9 = 0xc9,
|
||||
SOF10 = 0xca,
|
||||
SOF11 = 0xcb,
|
||||
DAC = 0xcc,
|
||||
SOF13 = 0xcd,
|
||||
SOF14 = 0xce,
|
||||
SOF15 = 0xcf,
|
||||
RST0 = 0xd0,
|
||||
RST1 = 0xd1,
|
||||
RST2 = 0xd2,
|
||||
RST3 = 0xd3,
|
||||
RST4 = 0xd4,
|
||||
RST5 = 0xd5,
|
||||
RST6 = 0xd6,
|
||||
RST7 = 0xd7,
|
||||
SOI = 0xd8,
|
||||
EOI = 0xd9,
|
||||
SOS = 0xda,
|
||||
DQT = 0xdb,
|
||||
DNL = 0xdc,
|
||||
DRI = 0xdd,
|
||||
DHP = 0xde,
|
||||
EXP = 0xdf,
|
||||
APP0 = 0xe0,
|
||||
APP1 = 0xe1,
|
||||
APP2 = 0xe2,
|
||||
APP3 = 0xe3,
|
||||
APP4 = 0xe4,
|
||||
APP5 = 0xe5,
|
||||
APP6 = 0xe6,
|
||||
APP7 = 0xe7,
|
||||
APP8 = 0xe8,
|
||||
APP9 = 0xe9,
|
||||
APP10 = 0xea,
|
||||
APP11 = 0xeb,
|
||||
APP12 = 0xec,
|
||||
APP13 = 0xed,
|
||||
APP14 = 0xee,
|
||||
APP15 = 0xef,
|
||||
JPG0 = 0xf0,
|
||||
JPG1 = 0xf1,
|
||||
JPG2 = 0xf2,
|
||||
JPG3 = 0xf3,
|
||||
JPG4 = 0xf4,
|
||||
JPG5 = 0xf5,
|
||||
JPG6 = 0xf6,
|
||||
JPG7 = 0xf7,
|
||||
JPG8 = 0xf8,
|
||||
JPG9 = 0xf9,
|
||||
COM = 0xfe,
|
||||
JPG10 = 0xfa,
|
||||
JPG11 = 0xfb,
|
||||
JPG12 = 0xfc,
|
||||
JPG13 = 0xfd
|
||||
};
|
||||
|
||||
const uint8_t framing; // 0xff
|
||||
const jpg_section_flag flag;
|
||||
const uint8_t length_msb;
|
||||
const uint8_t length_lsb;
|
||||
const uint8_t data[];
|
||||
|
||||
static bool is_valid_flag(const jpg_section_flag flag);
|
||||
static const char *flag_name(const jpg_section_flag flag);
|
||||
uint16_t data_length() const;
|
||||
uint16_t section_length() const;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) jpg_section_app0 // 0xffe0
|
||||
{
|
||||
char identifier[5] = {'J', 'F', 'I', 'F', 0}; // JFIF identifier, zero-terminated
|
||||
uint8_t version_major = 1;
|
||||
uint8_t version_minor = 1; // JFIF version 1.1
|
||||
uint8_t density_units = 0; // no density units specified
|
||||
uint16_t density_hor = 1;
|
||||
uint16_t density_ver = 1; // density: 1 pixel "per pixel" horizontally and vertically
|
||||
uint8_t thumbnail_hor = 0;
|
||||
uint8_t thumbnail_ver = 0; // no thumbnail (size 0 x 0)
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) jpg_section_dqt // 0xffdb
|
||||
{
|
||||
uint8_t id; // 0= quantLuminance, 1= quantChrominance
|
||||
uint8_t data[64];
|
||||
};
|
||||
14
lib/micro-jpg/library.json
Normal file
14
lib/micro-jpg/library.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "micro-jpg",
|
||||
"version": "1.0.0",
|
||||
"description": "JPEG library",
|
||||
"keywords": "",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rzeldent/"
|
||||
},
|
||||
"build": {
|
||||
"srcDir": "src/",
|
||||
"includeDir": "include/"
|
||||
}
|
||||
}
|
||||
90
lib/micro-jpg/src/jpg.cpp
Normal file
90
lib/micro-jpg/src/jpg.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <esp32-hal-log.h>
|
||||
#include "jpg.h"
|
||||
|
||||
const jpg_section *jpg::find_jpg_section(const uint8_t **ptr, const uint8_t *end, jpg_section::jpg_section_flag flag)
|
||||
{
|
||||
log_d("find_jpeg_section 0x%02x (%s)", flag, jpg_section::flag_name(flag));
|
||||
while (*ptr < end)
|
||||
{
|
||||
// flag, len MSB, len LSB
|
||||
auto section = reinterpret_cast<const jpg_section *>((*ptr));
|
||||
if (section->framing != 0xff)
|
||||
{
|
||||
log_e("Expected framing 0xff but found: 0x%02x", section->framing);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!jpg_section::is_valid_flag(section->flag))
|
||||
{
|
||||
log_d("Unknown section 0x%02x", flag);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Advance pointer section has a length, so not SOI (0xd8) and EOI (0xd9)
|
||||
*ptr += section->section_length();
|
||||
if (section->flag == flag)
|
||||
{
|
||||
log_d("Found section 0x%02x (%s), %d bytes", flag, jpg_section::flag_name(section->flag), section->section_length());
|
||||
return section;
|
||||
}
|
||||
|
||||
log_d("Skipping section: 0x%02x (%s), %d bytes", section->flag, jpg_section::flag_name(section->flag), section->section_length());
|
||||
}
|
||||
|
||||
// Not found
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// See https://create.stephan-brumme.com/toojpeg/
|
||||
bool jpg::decode(const uint8_t *data, size_t size)
|
||||
{
|
||||
log_d("decode_jpeg");
|
||||
// Look for start jpeg file (0xd8)
|
||||
auto ptr = data;
|
||||
auto end = ptr + size;
|
||||
|
||||
// Check for SOI (start of image) 0xff, 0xd8
|
||||
if (!find_jpg_section(&ptr, end, jpg_section::jpg_section_flag::SOI))
|
||||
{
|
||||
log_e("No valid start of image marker found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// First quantization table (Luminance - black & white images)
|
||||
if (!(quantization_table_0_ = find_jpg_section(&ptr, end, jpg_section::jpg_section_flag::DQT)))
|
||||
{
|
||||
log_e("No quantization table 0 section found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Second quantization table (Chrominance - color images)
|
||||
if (!(quantization_table_1_ = find_jpg_section(&ptr, end, jpg_section::jpg_section_flag::DQT)))
|
||||
log_w("No quantization table 1 section found");
|
||||
|
||||
// Start of scan
|
||||
if (!find_jpg_section(&ptr, end, jpg_section::jpg_section_flag::SOS))
|
||||
{
|
||||
log_e("No start of scan section found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start of the data sections
|
||||
jpeg_data_start = ptr;
|
||||
|
||||
log_d("Skipping over data sections");
|
||||
// Scan over all the sections. 0xff followed by not zero, is a new section
|
||||
while (ptr < end - 1 && (ptr[0] != 0xff || ptr[1] == 0))
|
||||
ptr++;
|
||||
|
||||
// Check if marker is an end of image marker
|
||||
if (!find_jpg_section(&ptr, end, jpg_section::jpg_section_flag::EOI))
|
||||
{
|
||||
log_e("No end of image marker found");
|
||||
return false;
|
||||
}
|
||||
|
||||
jpeg_data_size = ptr;
|
||||
log_d("Total jpeg data = %d bytes", jpeg_data_end - jpeg_data_start);
|
||||
|
||||
return true;
|
||||
}
|
||||
154
lib/micro-jpg/src/jpg_section.cpp
Normal file
154
lib/micro-jpg/src/jpg_section.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include "jpg_section.h"
|
||||
|
||||
uint16_t jpg_section::data_length() const
|
||||
{
|
||||
return (length_msb << 8) + length_lsb - sizeof(jpg_section::length_msb)- sizeof(jpg_section::length_lsb);
|
||||
}
|
||||
|
||||
uint16_t jpg_section::section_length() const
|
||||
{
|
||||
return flag == SOI || flag == EOI ? sizeof(jpg_section::framing) + sizeof(jpg_section::flag) : sizeof(jpg_section::framing) + sizeof(jpg_section::flag) + (length_msb << 8) + length_lsb;
|
||||
}
|
||||
|
||||
bool jpg_section::is_valid_flag(const jpg_section_flag flag)
|
||||
{
|
||||
return flag >= SOF0 && flag <= COM;
|
||||
}
|
||||
|
||||
// from: https://www.disktuna.com/list-of-jpeg-markers/
|
||||
const char *jpg_section::flag_name(const jpg_section_flag flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case DATA:
|
||||
return "DATA"; // DATA
|
||||
case SOF0:
|
||||
return "SOF0"; // Start of Frame 0 Baseline DCT
|
||||
case SOF1:
|
||||
return "SOF1"; // Start of Frame 1 Extended Sequential DCT
|
||||
case SOF2:
|
||||
return "SOF2"; // Start of Frame 2 Progressive DCT
|
||||
case SOF3:
|
||||
return "SOF3"; // Start of Frame 3 Lossless (sequential)
|
||||
case DHT:
|
||||
return "DHT"; // Define Huffman Table
|
||||
case SOF5:
|
||||
return "SOF5"; // Start of Frame 5 Differential sequential DCT
|
||||
case SOF6:
|
||||
return "SOF6"; // Start of Frame 6 Differential progressive DCT
|
||||
case SOF7:
|
||||
return "SOF7"; // Start of Frame 7 Differential lossless (sequential)
|
||||
case JPG:
|
||||
return "JPG"; // JPEG Extensions
|
||||
case SOF9:
|
||||
return "SOF9"; // Start of Frame 9 Extended sequential DCT, Arithmetic coding
|
||||
case SOF10:
|
||||
return "SOF10"; // Start of Frame 10 Progressive DCT, Arithmetic coding
|
||||
case SOF11:
|
||||
return "SOF11"; // Start of Frame 11 Lossless (sequential), Arithmetic coding
|
||||
case DAC:
|
||||
return "DAC"; // Define Arithmetic Coding
|
||||
case SOF13:
|
||||
return "SOF13"; // Start of Frame 13 Differential sequential DCT, Arithmetic coding
|
||||
case SOF14:
|
||||
return "SOF14"; // Start of Frame 14 Differential progressive DCT, Arithmetic coding
|
||||
case SOF15:
|
||||
return "SOF15"; // Start of Frame 15 Differential lossless (sequential), Arithmetic coding
|
||||
case RST0:
|
||||
return "RST0"; // Restart Marker 0
|
||||
case RST1:
|
||||
return "RST1"; // Restart Marker 1
|
||||
case RST2:
|
||||
return "RST2"; // Restart Marker 2
|
||||
case RST3:
|
||||
return "RST3"; // Restart Marker 3
|
||||
case RST4:
|
||||
return "RST4"; // Restart Marker 4
|
||||
case RST5:
|
||||
return "RST5"; // Restart Marker 5
|
||||
case RST6:
|
||||
return "RST6"; // Restart Marker 6
|
||||
case RST7:
|
||||
return "RST7"; // Restart Marker 7
|
||||
case SOI:
|
||||
return "SOI"; // Start of Image
|
||||
case EOI:
|
||||
return "EOI"; // End of Image
|
||||
case SOS:
|
||||
return "SOS"; // Start of Scan
|
||||
case DQT:
|
||||
return "DQT"; // Define Quantization Table
|
||||
case DNL:
|
||||
return "DNL"; // Define Number of Lines (Not common)
|
||||
case DRI:
|
||||
return "DRI"; // Define Restart Interval
|
||||
case DHP:
|
||||
return "DHP"; // Define Hierarchical Progression (Not common)
|
||||
case EXP:
|
||||
return "EXP"; // Expand Reference Component (Not common)
|
||||
case APP0:
|
||||
return "APP0"; // Application Segment 0 JFIF – JFIF JPEG image, AVI1 – Motion JPEG (MJPG)
|
||||
case APP1:
|
||||
return "APP1"; // Application Segment 1 EXIF Metadata, TIFF IFD format, JPEG Thumbnail (160×120) Adobe XMP
|
||||
case APP2:
|
||||
return "APP2"; // Application Segment 2 ICC color profile, FlashPix
|
||||
case APP3:
|
||||
return "APP3"; // Application Segment 3 (Not common) JPS Tag for Stereoscopic JPEG images
|
||||
case APP4:
|
||||
return "APP4"; // Application Segment 4 (Not common)
|
||||
case APP5:
|
||||
return "APP5"; // Application Segment 5 (Not common)
|
||||
case APP6:
|
||||
return "APP6"; // Application Segment 6 (Not common) NITF Lossles profile
|
||||
case APP7:
|
||||
return "APP7"; // Application Segment 7 (Not common)
|
||||
case APP8:
|
||||
return "APP8"; // Application Segment 8 (Not common)
|
||||
case APP9:
|
||||
return "APP9"; // Application Segment 9 (Not common)
|
||||
case APP10:
|
||||
return "APP10"; // Application Segment 10 PhoTags (Not common) ActiveObject (multimedia messages / captions)
|
||||
case APP11:
|
||||
return "APP11"; // Application Segment 11 (Not common) HELIOS JPEG Resources (OPI Postscript)
|
||||
case APP12:
|
||||
return "APP12"; // Application Segment 12 Picture Info (older digicams), Photoshop Save for Web: Ducky
|
||||
case APP13:
|
||||
return "APP13"; // Application Segment 13 Photoshop Save As: IRB, 8BIM, IPTC
|
||||
case APP14:
|
||||
return "APP14"; // Application Segment 14 (Not common)
|
||||
case APP15:
|
||||
return "APP15"; // Application Segment 15 (Not common)
|
||||
case JPG0:
|
||||
return "JPG0"; // JPEG Extension 0
|
||||
case JPG1:
|
||||
return "JPG1"; // JPEG Extension 1
|
||||
case JPG2:
|
||||
return "JPG2"; // JPEG Extension 2
|
||||
case JPG3:
|
||||
return "JPG3"; // JPEG Extension 3
|
||||
case JPG4:
|
||||
return "JPG4"; // JPEG Extension 4
|
||||
case JPG5:
|
||||
return "JPG5"; // JPEG Extension 5
|
||||
case JPG6:
|
||||
return "JPG6"; // JPEG Extension 6
|
||||
case JPG7:
|
||||
return "JPG7"; // SOF48 JPEG Extension 7 JPEG-LS Lossless JPEG
|
||||
case JPG8:
|
||||
return "JPG8"; // LSE JPEG Extension 8 JPEG-LS Extension Lossless JPEG Extension Parameters
|
||||
case JPG9:
|
||||
return "JPG9"; // JPEG Extension 9 (Not common)
|
||||
case JPG10:
|
||||
return "JPG10"; // JPEG Extension 10 (Not common)
|
||||
case JPG11:
|
||||
return "JPG11"; // JPEG Extension 11 (Not common)
|
||||
case JPG12:
|
||||
return "JPG12"; // JPEG Extension 12 (Not common)
|
||||
case JPG13:
|
||||
return "JPG13"; // JPEG Extension 13 (Not common)
|
||||
case COM:
|
||||
return "COM"; // Comment
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
class micro_rtsp_jpeg
|
||||
{
|
||||
public:
|
||||
bool decode_jpeg(const uint8_t *jpg, size_t size);
|
||||
|
||||
class __attribute__ ((packed)) jpg_section
|
||||
{
|
||||
public:
|
||||
uint8_t flag() const { return section_flag; }
|
||||
const char *flag_name() const;
|
||||
uint16_t section_length() const { return section_flag == 0xd8 || section_flag == 0xd9 ? 0 : (section_length_msb << 8) + section_length_lsb; }
|
||||
const uint8_t *data() const { return reinterpret_cast<const uint8_t *>(§ion_data[1]); }
|
||||
uint16_t data_length() const { return section_length() - 3; }
|
||||
|
||||
private:
|
||||
const uint8_t section_flag;
|
||||
const uint8_t section_length_msb;
|
||||
const uint8_t section_length_lsb;
|
||||
const uint8_t section_data[];
|
||||
};
|
||||
|
||||
const jpg_section *quantization_table_0_;
|
||||
const jpg_section *quantization_table_1_;
|
||||
|
||||
const uint8_t *jpeg_data_start;
|
||||
const uint8_t *jpeg_data_end;
|
||||
|
||||
private:
|
||||
static const jpg_section *find_jpeg_section(const uint8_t **ptr, const uint8_t *end, uint8_t flag);
|
||||
};
|
||||
15
lib/micro-rtsp-server/include/micro_rtsp_streamer.h
Normal file
15
lib/micro-rtsp-server/include/micro_rtsp_streamer.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <jpg.h>
|
||||
|
||||
|
||||
class micro_rtsp_streamer
|
||||
{
|
||||
public:
|
||||
micro_rtsp_streamer();
|
||||
void create_jpg_packet(const jpg jpg, uint32_t timestamp);
|
||||
private :
|
||||
|
||||
uint32_t ssrc_;
|
||||
uint16_t sequenceNumber_;
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
class micro_rtsp_streamer
|
||||
{
|
||||
public:
|
||||
};
|
||||
@@ -14,6 +14,7 @@
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"micro-jpg": "^1.0.0",
|
||||
"espressif/esp32-camera": "^2.0.4"
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
#include <esp32-hal-log.h>
|
||||
|
||||
#include "micro_rtsp_jpeg.h"
|
||||
|
||||
const char *micro_rtsp_jpeg::jpg_section::flag_name() const
|
||||
{
|
||||
switch (section_flag)
|
||||
{
|
||||
case 0xd8:
|
||||
return "SOI"; // start of image
|
||||
case 0xd9:
|
||||
return "EOI"; // end of image
|
||||
case 0xe0:
|
||||
return "APP0";
|
||||
case 0xdb:
|
||||
return "DQT"; // define quantization table
|
||||
case 0xc4:
|
||||
return "DHT"; // define Huffman table
|
||||
case 0xc0:
|
||||
return "SOF"; // start of frame
|
||||
case 0xda:
|
||||
return "SOS"; // start of scan
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const micro_rtsp_jpeg::jpg_section *micro_rtsp_jpeg::find_jpeg_section(const uint8_t **ptr, const uint8_t *end, uint8_t flag)
|
||||
{
|
||||
log_d("find_jpeg_section 0x%02x", flag);
|
||||
while (*ptr < end)
|
||||
{
|
||||
auto framing = *((*ptr)++);
|
||||
if (framing != 0xff)
|
||||
{
|
||||
log_e("Expected framing 0xff but found: 0x%02x", framing);
|
||||
break;
|
||||
}
|
||||
|
||||
// framing = 0xff, flag, len MSB, len LSB
|
||||
auto section = reinterpret_cast<const jpg_section *>((*ptr)++);
|
||||
// Advance pointer section has a length, so not SOI (0xd8) and EOI (0xd9)
|
||||
*ptr += section->section_length();
|
||||
if (section->flag() == flag)
|
||||
{
|
||||
log_d("Found section 0x%02x (%s), %d bytes", flag, section->flag_name(), section->section_length());
|
||||
return section;
|
||||
}
|
||||
|
||||
log_d("Skipping section: 0x%02x (%s), %d bytes", section->flag(), section->flag_name(), section->section_length());
|
||||
}
|
||||
|
||||
// Not found
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// See https://create.stephan-brumme.com/toojpeg/
|
||||
bool micro_rtsp_jpeg::decode_jpeg(const uint8_t *data, size_t size)
|
||||
{
|
||||
log_d("decode_jpeg");
|
||||
// Look for start jpeg file (0xd8)
|
||||
auto ptr = data;
|
||||
auto end = ptr + size;
|
||||
|
||||
// Check for SOI (start of image) 0xff, 0xd8
|
||||
if (!find_jpeg_section(&ptr, end, 0xd8))
|
||||
{
|
||||
log_e("No valid start of image marker found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// First quantization table
|
||||
if (!(quantization_table_0_ = find_jpeg_section(&ptr, end, 0xdb)))
|
||||
log_e("No quantization table 0 section found");
|
||||
|
||||
// Second quantization table (for color images)
|
||||
if (!(quantization_table_1_ = find_jpeg_section(&ptr, end, 0xdb)))
|
||||
log_w("No quantization table 1 section found");
|
||||
|
||||
// Start of scan
|
||||
if (!find_jpeg_section(&ptr, end, 0xda))
|
||||
log_e("No start of scan section found");
|
||||
|
||||
// Start of the data sections
|
||||
jpeg_data_start = ptr;
|
||||
|
||||
// Scan over all the sections. 0xff followed by not zero, is a new section
|
||||
while (ptr < end - 1 && (ptr[0] != 0xff || ptr[1] == 0))
|
||||
ptr++;
|
||||
|
||||
// Check if marker is an end of image marker
|
||||
if (!find_jpeg_section(&ptr, end, 0xd9))
|
||||
{
|
||||
log_e("No end of image marker found");
|
||||
return false;
|
||||
}
|
||||
|
||||
jpeg_data_end = ptr;
|
||||
log_d("Total jpeg data = %d bytes", jpeg_data_end - jpeg_data_start);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <regex>
|
||||
#include "micro_rtsp_requests.h"
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc2326
|
||||
|
||||
micro_rtsp_requests::rtsp_command micro_rtsp_requests::parse_command(const std::string &request)
|
||||
{
|
||||
log_i("parse_command");
|
||||
|
||||
157
lib/micro-rtsp-server/src/micro_rtsp_streamer.cpp
Normal file
157
lib/micro-rtsp-server/src/micro_rtsp_streamer.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "micro_rtsp_streamer.h"
|
||||
|
||||
#include "rtp_payloads.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "esp_random.h"
|
||||
|
||||
// https://github.com/txgcwm/Linux-C-Examples/blob/master/h264/h264dec/rtcp.h
|
||||
|
||||
// RTP data header (http://www.ietf.org/rfc/rfc3550.txt)
|
||||
struct rtp_hdr
|
||||
{
|
||||
uint version : 2; // protocol version
|
||||
uint p : 1; // padding flag
|
||||
uint x : 1; // header extension flag
|
||||
uint cc : 4; // CSRC count
|
||||
uint m : 1; // marker bit
|
||||
uint pt : 7; // payload type
|
||||
uint seq : 16; // sequence number
|
||||
uint32_t ts; // timestamp
|
||||
uint32_t ssrc; // synchronization source
|
||||
uint32_t csrc[]; // optional CSRC list
|
||||
} rtp_hdr;
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc2435
|
||||
|
||||
// The following definition is from RFC1890
|
||||
#define RTP_PT_JPEG 26
|
||||
|
||||
struct jpeghdr
|
||||
{
|
||||
uint322_t tspec : 8; // type-specific field
|
||||
uint322_t off : 24; // fragment byte offset
|
||||
uint8_t type; // id of jpeg decoder params
|
||||
uint8_t q; // quantization factor (or table id)
|
||||
uint8_t width; // frame width in 8 pixel blocks
|
||||
uint8_t height; // frame height in 8 pixel blocks
|
||||
};
|
||||
|
||||
struct jpeghdr_rst
|
||||
{
|
||||
uint16_t dri;
|
||||
uint16_t f : 1;
|
||||
uint16_t l : 1;
|
||||
uint16_t count : 14;
|
||||
};
|
||||
|
||||
struct jpeghdr_qtable
|
||||
{
|
||||
uint8_t mbz;
|
||||
uint8_t precision;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
#define RTP_JPEG_RESTART 0x40
|
||||
|
||||
micro_rtsp_streamer::micro_rtsp_streamer()
|
||||
{
|
||||
// Random number
|
||||
ssrc_ = esp_random();
|
||||
sequenceNumber_ = 0;
|
||||
}
|
||||
|
||||
#define MAX_ESP32_MTU 1440
|
||||
|
||||
size_t micro_rtsp_streamer::create_jpg_packet(const uint8_t *jpg, const uint8_t *jpg_end, uint32_t timestamp)
|
||||
{
|
||||
int fragmentLen = MAX_FRAGMENT_SIZE;
|
||||
if (fragmentLen + fragmentOffset > jpegLen) // Shrink last fragment if needed
|
||||
fragmentLen = jpegLen - fragmentOffset;
|
||||
|
||||
bool isLastFragment = (fragmentOffset + fragmentLen) == jpegLen;
|
||||
|
||||
struct rtp_header header = {
|
||||
.version = 2,
|
||||
.seq = sequenceNumber_,
|
||||
.marker = 1, // TODO = 1 if last fragfment
|
||||
.pt = rtp_payload.JPEG,
|
||||
.ts = timestamp,
|
||||
.ssrc = ssrc_};
|
||||
|
||||
struct jpeghdr jpghdr = {
|
||||
.tspec = 0, // type-specific field
|
||||
.off = offset, // fragment byte offset
|
||||
.type = 0, // id of jpeg decoder params
|
||||
.q = 0x5e, // quantization factor (or table id)
|
||||
.width = width >> 3, // frame width in 8 pixel blocks
|
||||
.height = height >> 3 // frame height in 8 pixel blocks
|
||||
};
|
||||
|
||||
memset(RtpBuf, 0x00, sizeof(RtpBuf));
|
||||
// Prepare the first 4 byte of the packet. This is the Rtp over Rtsp header in case of TCP based transport
|
||||
RtpBuf[0] = '$'; // magic number
|
||||
RtpBuf[1] = 0; // number of multiplexed subchannel on RTPS connection - here the RTP channel
|
||||
RtpBuf[2] = (RtpPacketSize & 0x0000FF00) >> 8;
|
||||
RtpBuf[3] = (RtpPacketSize & 0x000000FF);
|
||||
// Prepare the 12 byte RTP header
|
||||
RtpBuf[4] = 0x80; // RTP version
|
||||
RtpBuf[5] = 0x1a | (isLastFragment ? 0x80 : 0x00); // JPEG payload (26) and marker bit
|
||||
RtpBuf[7] = m_SequenceNumber & 0x0FF; // each packet is counted with a sequence counter
|
||||
RtpBuf[6] = m_SequenceNumber >> 8;
|
||||
RtpBuf[8] = (m_Timestamp & 0xFF000000) >> 24; // each image gets a timestamp
|
||||
RtpBuf[9] = (m_Timestamp & 0x00FF0000) >> 16;
|
||||
RtpBuf[10] = (m_Timestamp & 0x0000FF00) >> 8;
|
||||
RtpBuf[11] = (m_Timestamp & 0x000000FF);
|
||||
RtpBuf[12] = 0x13; // 4 byte SSRC (sychronization source identifier)
|
||||
RtpBuf[13] = 0xf9; // we just an arbitrary number here to keep it simple
|
||||
RtpBuf[14] = 0x7e;
|
||||
RtpBuf[15] = 0x67;
|
||||
|
||||
// Prepare the 8 byte payload JPEG header
|
||||
RtpBuf[16] = 0x00; // type specific
|
||||
RtpBuf[17] = (fragmentOffset & 0x00FF0000) >> 16; // 3 byte fragmentation offset for fragmented images
|
||||
RtpBuf[18] = (fragmentOffset & 0x0000FF00) >> 8;
|
||||
RtpBuf[19] = (fragmentOffset & 0x000000FF);
|
||||
|
||||
/* These sampling factors indicate that the chrominance components of
|
||||
type 0 video is downsampled horizontally by 2 (often called 4:2:2)
|
||||
while the chrominance components of type 1 video are downsampled both
|
||||
horizontally and vertically by 2 (often called 4:2:0). */
|
||||
RtpBuf[20] = 0x00; // type (fixme might be wrong for camera data) https://tools.ietf.org/html/rfc2435
|
||||
RtpBuf[21] = q; // quality scale factor was 0x5e
|
||||
RtpBuf[22] = m_width / 8; // width / 8
|
||||
RtpBuf[23] = m_height / 8; // height / 8
|
||||
|
||||
int headerLen = 24; // Inlcuding jpeg header but not qant table header
|
||||
if (includeQuantTbl)
|
||||
{ // we need a quant header - but only in first packet of the frame
|
||||
// printf("inserting quanttbl\n");
|
||||
RtpBuf[24] = 0; // MBZ
|
||||
RtpBuf[25] = 0; // 8 bit precision
|
||||
RtpBuf[26] = 0; // MSB of lentgh
|
||||
|
||||
int numQantBytes = 64; // Two 64 byte tables
|
||||
RtpBuf[27] = 2 * numQantBytes; // LSB of length
|
||||
|
||||
headerLen += 4;
|
||||
|
||||
memcpy(RtpBuf + headerLen, quant0tbl, numQantBytes);
|
||||
headerLen += numQantBytes;
|
||||
|
||||
memcpy(RtpBuf + headerLen, quant1tbl, numQantBytes);
|
||||
headerLen += numQantBytes;
|
||||
}
|
||||
// printf("Sending timestamp %d, seq %d, fragoff %d, fraglen %d, jpegLen %d\n", m_Timestamp, m_SequenceNumber, fragmentOffset, fragmentLen, jpegLen);
|
||||
|
||||
// append the JPEG scan data to the RTP buffer
|
||||
memcpy(RtpBuf + headerLen, jpeg + fragmentOffset, fragmentLen);
|
||||
fragmentOffset += fragmentLen;
|
||||
|
||||
m_SequenceNumber++; // prepare the packet counter for the next packet
|
||||
|
||||
IPADDRESS otherip;
|
||||
IPPORT otherport;
|
||||
socketpeeraddr(m_Client, &otherip, &otherport);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
#include "micro_rtssp_streamer.h"
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user