Work in progress

This commit is contained in:
Rene Zeldenthuis
2024-02-18 12:47:17 +01:00
parent 6119020e7d
commit 2eb7a58e1c
14 changed files with 585 additions and 158 deletions

View File

@@ -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 *>(&section_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);
};

View 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_;
};

View File

@@ -1,6 +0,0 @@
#pragma once
class micro_rtsp_streamer
{
public:
};

View File

@@ -14,6 +14,7 @@
},
"dependencies": {
"micro-jpg": "^1.0.0",
"espressif/esp32-camera": "^2.0.4"
}
}

View File

@@ -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;
}

View File

@@ -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");

View 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);
}

View File

@@ -1 +0,0 @@
#include "micro_rtssp_streamer.h"