diff --git a/lib/micro-jpg/include/jpg.h b/lib/micro-jpg/include/jpg.h index 0e4c6b2..1862bd4 100644 --- a/lib/micro-jpg/include/jpg.h +++ b/lib/micro-jpg/include/jpg.h @@ -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; diff --git a/lib/micro-jpg/include/jpg_section.h b/lib/micro-jpg/include/jpg_section.h index 380e9a7..506cbfa 100644 --- a/lib/micro-jpg/include/jpg_section.h +++ b/lib/micro-jpg/include/jpg_section.h @@ -1,7 +1,11 @@ #pragma once +#include #include +// 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; \ No newline at end of file diff --git a/lib/micro-jpg/src/jpg.cpp b/lib/micro-jpg/src/jpg.cpp index ef0bef7..502c1f5 100644 --- a/lib/micro-jpg/src/jpg.cpp +++ b/lib/micro-jpg/src/jpg.cpp @@ -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(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(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; } diff --git a/lib/micro-rtsp-server/include/micro_rtp_structs.h b/lib/micro-rtsp-server/include/micro_rtp_structs.h index 9c8b215..2d90b8d 100644 --- a/lib/micro-rtsp-server/include/micro_rtp_structs.h +++ b/lib/micro-rtsp-server/include/micro_rtp_structs.h @@ -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; diff --git a/lib/micro-rtsp-server/include/micro_rtsp_streamer.h b/lib/micro-rtsp-server/include/micro_rtsp_streamer.h index 16d3f64..328f489 100644 --- a/lib/micro-rtsp-server/include/micro_rtsp_streamer.h +++ b/lib/micro-rtsp-server/include/micro_rtsp_streamer.h @@ -1,14 +1,12 @@ #pragma once +#include #include // 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; diff --git a/lib/micro-rtsp-server/src/micro_rtsp_streamer.cpp b/lib/micro-rtsp-server/src/micro_rtsp_streamer.cpp index 93a748e..5272f9a 100644 --- a/lib/micro-rtsp-server/src/micro_rtsp_streamer.cpp +++ b/lib/micro-rtsp-server/src/micro_rtsp_streamer.cpp @@ -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(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(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); } diff --git a/test/test_main.cpp b/test/test_main.cpp index 5fd8164..289fd2b 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -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(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(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(); }