forked from external-repos/esp32cam-rtsp
WIP
This commit is contained in:
@@ -7,8 +7,8 @@ class jpg
|
||||
public:
|
||||
bool decode(const uint8_t *jpg, size_t size);
|
||||
|
||||
const jpg_section_t *quantization_table_luminance_;
|
||||
const jpg_section_t *quantization_table_chrominance_;
|
||||
const jpg_section_dqt_t *quantization_table_luminance_;
|
||||
const jpg_section_dqt_t *quantization_table_chrominance_;
|
||||
|
||||
const uint8_t *jpeg_data_start;
|
||||
const uint8_t *jpeg_data_end;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// http://www.ietf.org/rfc/rfc2345.txt Each table is an array of 64 values given in zig-zag order, identical to the format used in a JFIF DQT marker segment.
|
||||
constexpr size_t jpeg_quantization_table_length = 64;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
enum jpg_section_flag : uint8_t
|
||||
@@ -99,5 +103,5 @@ typedef struct __attribute__((packed)) // 0xffe0
|
||||
typedef struct __attribute__((packed)) // 0xffdb
|
||||
{
|
||||
uint8_t id; // 0= quantLuminance, 1= quantChrominance
|
||||
uint8_t data[64];
|
||||
uint8_t data[jpeg_quantization_table_length];
|
||||
} jpg_section_dqt_t;
|
||||
@@ -51,15 +51,35 @@ bool jpg::decode(const uint8_t *data, size_t size)
|
||||
}
|
||||
|
||||
// First quantization table (Luminance - black & white images)
|
||||
if (!(quantization_table_luminance_ = find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::DQT)))
|
||||
const jpg_section_t *quantization_table_section;
|
||||
if (!(quantization_table_section = find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::DQT)))
|
||||
{
|
||||
log_e("No quantization_table_luminance section found");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (quantization_table_section->data_length() != sizeof(jpg_section_dqt_t))
|
||||
{
|
||||
log_w("Invalid length of quantization_table_luminance section. Expected %d but read %d", sizeof(jpg_section_dqt_t), quantization_table_section->data_length());
|
||||
return false;
|
||||
}
|
||||
|
||||
quantization_table_luminance_ = reinterpret_cast<const jpg_section_dqt_t *>(quantization_table_section->data);
|
||||
|
||||
// Second quantization table (Chrominance - color images)
|
||||
if (!(quantization_table_chrominance_ = find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::DQT)))
|
||||
if (!(quantization_table_section = find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::DQT)))
|
||||
{
|
||||
log_w("No quantization_table_chrominance section found");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (quantization_table_section->data_length() != sizeof(jpg_section_dqt_t))
|
||||
{
|
||||
log_w("Invalid length of quantization_table_chrominance section. Expected %d but read %d", sizeof(jpg_section_dqt_t), quantization_table_section->data_length());
|
||||
return false;
|
||||
}
|
||||
|
||||
quantization_table_chrominance_ = reinterpret_cast<const jpg_section_dqt_t *>(quantization_table_section->data);
|
||||
|
||||
// Start of scan
|
||||
if (!find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::SOS))
|
||||
@@ -85,7 +105,7 @@ bool jpg::decode(const uint8_t *data, size_t size)
|
||||
|
||||
jpeg_data_end = ptr;
|
||||
|
||||
log_d("Total jpeg data= %d bytes", jpeg_data_end - jpeg_data_start);
|
||||
log_d("Total jpeg data: %d bytes", jpeg_data_end - jpeg_data_start);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// https://www.ietf.org/rfc/rfc2326#section-10.12
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char magic; // Magic encapsulation ASCII dollar sign (24 hexadecimal)
|
||||
char magic='$'; // Magic encapsulation ASCII dollar sign (24 hexadecimal)
|
||||
uint8_t channel; // Channel identifier
|
||||
uint16_t length; // Network order
|
||||
} rtp_over_tcp_hdr_t;
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <jpg_section.h>
|
||||
#include <micro_rtp_structs.h>
|
||||
|
||||
// https://en.wikipedia.org/wiki/Maximum_transmission_unit
|
||||
constexpr size_t max_wifi_mtu = 2304;
|
||||
// Payload JPG - https://www.ietf.org/rfc/rfc1890.txt
|
||||
constexpr uint8_t RTP_PAYLOAD_JPG = 26;
|
||||
// http://www.ietf.org/rfc/rfc2345.txt Each table is an array of 64 values given in zig-zag order, identical to the format used in a JFIF DQT marker segment.
|
||||
constexpr size_t jpeg_luminance_table_length = 64;
|
||||
constexpr size_t jpeg_chrominance_table_length = 64;
|
||||
|
||||
// One of the types below will be returned, the jpeg_packet_with_quantization_t for the first packet, then the jpeg_packet_t
|
||||
|
||||
@@ -18,8 +16,8 @@ typedef struct __attribute__((packed))
|
||||
rtp_hdr_t rtp_hdr;
|
||||
jpeg_hdr_t jpeg_hdr;
|
||||
jpeg_hdr_qtable_t jpeg_hdr_qtable;
|
||||
uint8_t quantization_table_luminance[jpeg_luminance_table_length];
|
||||
uint8_t quantization_table_chrominance[jpeg_chrominance_table_length];
|
||||
uint8_t quantization_table_luminance[jpeg_quantization_table_length];
|
||||
uint8_t quantization_table_chrominance[jpeg_quantization_table_length];
|
||||
uint8_t jpeg_data[];
|
||||
} jpeg_packet_with_quantization_t;
|
||||
|
||||
|
||||
@@ -31,11 +31,10 @@ rtp_over_tcp_hdr_t *micro_rtsp_streamer::create_jpg_packet(const uint8_t *jpg_sc
|
||||
const auto jpg_bytes = isLastFragment ? jpg_bytes_left : payload_size;
|
||||
const uint16_t packet_size = headers_size + jpg_bytes;
|
||||
|
||||
const auto packet = (jpeg_packet_t *)calloc(1, packet_size);
|
||||
const auto packet = static_cast<jpeg_packet_t *>(calloc(1, packet_size));
|
||||
|
||||
// 4 bytes RTP over TCP header
|
||||
packet->rtp_over_tcp_hdr.magic = '$'; // encapsulation
|
||||
packet->rtp_over_tcp_hdr.channel = 0; // number of multiplexed sub-channel on RTPS connection - here the RTP channel
|
||||
packet->rtp_over_tcp_hdr.channel = 0;
|
||||
packet->rtp_over_tcp_hdr.length = packet_size;
|
||||
log_v("rtp_over_tcp_hdr_t={.magic=%c,.channel=%u,.length=%u}", packet->rtp_over_tcp_hdr.magic, packet->rtp_over_tcp_hdr.channel, packet->rtp_over_tcp_hdr.length);
|
||||
|
||||
@@ -57,16 +56,16 @@ rtp_over_tcp_hdr_t *micro_rtsp_streamer::create_jpg_packet(const uint8_t *jpg_sc
|
||||
packet->jpeg_hdr.height = (uint8_t)(height_ >> 3); // frame height in 8 pixel blocks
|
||||
log_v("jpeg_hdr={.tspec:%u,.off:0x%6x,.type:0x2%x,.q:%u,.width:%u.height:%u}", packet->jpeg_hdr.tspec, packet->jpeg_hdr.off, packet->jpeg_hdr.type, packet->jpeg_hdr.q, packet->jpeg_hdr.width, packet->jpeg_hdr.height);
|
||||
|
||||
// Only in first packet of the frame
|
||||
if (include_quantization_tables)
|
||||
{
|
||||
const auto packet_with_quantization = (jpeg_packet_with_quantization_t *)packet;
|
||||
// Only in first packet of the frame
|
||||
auto packet_with_quantization = reinterpret_cast<jpeg_packet_with_quantization_t *>(packet);
|
||||
packet_with_quantization->jpeg_hdr_qtable.mbz = 0;
|
||||
packet_with_quantization->jpeg_hdr_qtable.precision = 0; // 8 bit precision
|
||||
packet_with_quantization->jpeg_hdr_qtable.length = jpeg_luminance_table_length + jpeg_chrominance_table_length;
|
||||
packet_with_quantization->jpeg_hdr_qtable.length = jpeg_quantization_table_length + jpeg_quantization_table_length;
|
||||
log_v("jpeg_hdr_qtable={.mbz:%u,.precision:%u,.length:%u}", packet_with_quantization->jpeg_hdr_qtable.mbz, packet_with_quantization->jpeg_hdr_qtable.precision, packet_with_quantization->jpeg_hdr_qtable.length);
|
||||
memcpy(packet_with_quantization->quantization_table_luminance, quantization_table_luminance, jpeg_luminance_table_length);
|
||||
memcpy(packet_with_quantization->quantization_table_chrominance, quantization_table_chrominance, jpeg_chrominance_table_length);
|
||||
memcpy(packet_with_quantization->quantization_table_luminance, quantization_table_luminance, jpeg_quantization_table_length);
|
||||
memcpy(packet_with_quantization->quantization_table_chrominance, quantization_table_chrominance, jpeg_quantization_table_length);
|
||||
// Copy JPG data
|
||||
memcpy(packet_with_quantization->jpeg_data, *jpg_offset, jpg_bytes);
|
||||
}
|
||||
|
||||
@@ -73,13 +73,9 @@ void test_jpg_decode()
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(jpeg_data, jpg.jpeg_data_start, sizeof(jpeg_data));
|
||||
|
||||
// Id is not stored
|
||||
TEST_ASSERT_EQUAL_INT32(sizeof(jpg_section_dqt_t::id) + sizeof(jpeg_qtable0), jpg.quantization_table_luminance_->data_length());
|
||||
auto jpg_section_dqt_luminance = reinterpret_cast<const jpg_section_dqt_t *>(jpg.quantization_table_luminance_->data);
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(jpeg_qtable0, jpg_section_dqt_luminance->data, sizeof(jpeg_qtable0));
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(jpeg_qtable0, jpg.quantization_table_luminance_->data, sizeof(jpeg_qtable0));
|
||||
// Id is not stored
|
||||
TEST_ASSERT_EQUAL_INT32(sizeof(jpg_section_dqt_t::id) + sizeof(jpeg_qtable1), jpg.quantization_table_chrominance_->data_length());
|
||||
auto jpg_section_dqt_chrominance = reinterpret_cast<const jpg_section_dqt_t *>(jpg.quantization_table_chrominance_->data);
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(jpeg_qtable1, jpg_section_dqt_chrominance->data, sizeof(jpeg_qtable1));
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(jpeg_qtable1, jpg.quantization_table_chrominance_->data, sizeof(jpeg_qtable1));
|
||||
}
|
||||
|
||||
void test_struct_sizes()
|
||||
@@ -91,6 +87,25 @@ void test_struct_sizes()
|
||||
TEST_ASSERT_EQUAL(24, sizeof(jpeg_packet_t));
|
||||
}
|
||||
|
||||
void test_bitfield()
|
||||
{
|
||||
jpeg_hdr_t jpeg_hdr;
|
||||
jpeg_hdr.tspec = 0x55;
|
||||
jpeg_hdr.off = 0xAAAAAA;
|
||||
TEST_ASSERT_EQUAL(0x55, jpeg_hdr.tspec);
|
||||
TEST_ASSERT_EQUAL(0xAAAAAA, jpeg_hdr.off);
|
||||
jpeg_hdr.tspec = 0xAA;
|
||||
jpeg_hdr.off = 0x555555;
|
||||
TEST_ASSERT_EQUAL(0xAA, jpeg_hdr.tspec);
|
||||
TEST_ASSERT_EQUAL(0x555555, jpeg_hdr.off);
|
||||
}
|
||||
|
||||
void test_default()
|
||||
{
|
||||
rtp_over_tcp_hdr_t rtp_over_tcp_hdr;
|
||||
TEST_ASSERT_EQUAL('$', rtp_over_tcp_hdr.magic);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Disable brownout
|
||||
@@ -102,6 +117,8 @@ void setup()
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_jpg_decode);
|
||||
RUN_TEST(test_struct_sizes);
|
||||
RUN_TEST(test_bitfield);
|
||||
RUN_TEST(test_default);
|
||||
UNITY_END();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user