forked from external-repos/esp32cam-rtsp
WIP
This commit is contained in:
@@ -53,13 +53,13 @@ bool jpg::decode(const uint8_t *data, size_t size)
|
|||||||
// First quantization table (Luminance - black & white images)
|
// First quantization table (Luminance - black & white images)
|
||||||
if (!(quantization_table_luminance_ = find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::DQT)))
|
if (!(quantization_table_luminance_ = find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::DQT)))
|
||||||
{
|
{
|
||||||
log_e("No quantization table 0 section found");
|
log_e("No quantization_table_luminance section found");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second quantization table (Chrominance - color images)
|
// 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_chrominance_ = find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::DQT)))
|
||||||
log_w("No quantization table 1 section found");
|
log_w("No quantization_table_chrominance section found");
|
||||||
|
|
||||||
// Start of scan
|
// Start of scan
|
||||||
if (!find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::SOS))
|
if (!find_jpg_section(&ptr, end, jpg_section_t::jpg_section_flag::SOS))
|
||||||
@@ -83,6 +83,8 @@ bool jpg::decode(const uint8_t *data, size_t size)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return true;
|
||||||
|
|||||||
@@ -10,16 +10,15 @@ public:
|
|||||||
|
|
||||||
esp_err_t initialize(camera_config_t *camera_config);
|
esp_err_t initialize(camera_config_t *camera_config);
|
||||||
esp_err_t deinitialize();
|
esp_err_t deinitialize();
|
||||||
// sensor_t* esp_camera_sensor_get();
|
|
||||||
|
|
||||||
void update_frame();
|
void update_frame();
|
||||||
|
|
||||||
uint8_t *data() const { return fb->buf; }
|
uint8_t *data() const { return fb_->buf; }
|
||||||
size_t width() const { return fb->width; }
|
size_t width() const { return fb_->width; }
|
||||||
size_t height() const { return fb->height; }
|
size_t height() const { return fb_->height; }
|
||||||
size_t size() const { return fb->len; }
|
size_t size() const { return fb_->len; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
esp_err_t init_result;
|
esp_err_t init_result_;
|
||||||
camera_fb_t *fb;
|
camera_fb_t *fb_;
|
||||||
};
|
};
|
||||||
@@ -18,7 +18,7 @@ private:
|
|||||||
rtsp_command_teardown // TEARDOWN
|
rtsp_command_teardown // TEARDOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string available_stream_name_ = "mjpeg/1";
|
const char* available_stream_name_ = "mjpeg/1";
|
||||||
|
|
||||||
rtsp_command parse_command(const std::string &request);
|
rtsp_command parse_command(const std::string &request);
|
||||||
bool parse_client_port(const std::string &request);
|
bool parse_client_port(const std::string &request);
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ constexpr uint8_t RTP_PAYLOAD_JPG = 26;
|
|||||||
constexpr size_t jpeg_luminance_table_length = 64;
|
constexpr size_t jpeg_luminance_table_length = 64;
|
||||||
constexpr size_t jpeg_chrominance_table_length = 64;
|
constexpr size_t jpeg_chrominance_table_length = 64;
|
||||||
|
|
||||||
|
class micro_rtsp_streamer
|
||||||
|
{
|
||||||
|
public:
|
||||||
// https://www.ietf.org/rfc/rfc2326#section-10.12
|
// https://www.ietf.org/rfc/rfc2326#section-10.12
|
||||||
typedef struct __attribute__((packed))
|
typedef struct __attribute__((packed))
|
||||||
{
|
{
|
||||||
@@ -16,9 +19,6 @@ typedef struct __attribute__((packed))
|
|||||||
uint16_t length; // Network order
|
uint16_t length; // Network order
|
||||||
} rtp_over_tcp_hdr_t;
|
} rtp_over_tcp_hdr_t;
|
||||||
|
|
||||||
class micro_rtsp_streamer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
micro_rtsp_streamer(const uint16_t width, const uint16_t height);
|
micro_rtsp_streamer(const uint16_t width, const uint16_t height);
|
||||||
rtp_over_tcp_hdr_t *create_jpg_packet(const uint8_t *jpg_scan, const uint8_t *jpg_scan_end, uint8_t **jpg_offset, const uint32_t timestamp, const uint8_t *quantization_table_luminance, const uint8_t *quantization_table_chrominance);
|
rtp_over_tcp_hdr_t *create_jpg_packet(const uint8_t *jpg_scan, const uint8_t *jpg_scan_end, uint8_t **jpg_offset, const uint32_t timestamp, const uint8_t *quantization_table_luminance, const uint8_t *quantization_table_chrominance);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
micro_rtsp_camera::micro_rtsp_camera()
|
micro_rtsp_camera::micro_rtsp_camera()
|
||||||
{
|
{
|
||||||
init_result == ESP_FAIL;
|
init_result_ == ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
micro_rtsp_camera::~micro_rtsp_camera()
|
micro_rtsp_camera::~micro_rtsp_camera()
|
||||||
@@ -14,24 +14,26 @@ micro_rtsp_camera::~micro_rtsp_camera()
|
|||||||
|
|
||||||
esp_err_t micro_rtsp_camera::initialize(camera_config_t *camera_config)
|
esp_err_t micro_rtsp_camera::initialize(camera_config_t *camera_config)
|
||||||
{
|
{
|
||||||
init_result = esp_camera_init(camera_config);
|
log_v("camera_config={.pin_pwdn:%u,.pin_reset:%u,.pin_xclk:%u,.pin_sccb_sda:%u,.pin_sccb_scl:%u,.pin_d7:%u,.pin_d6:%u,.pin_d5:%u,.pin_d4:%u,.pin_d3:%u,.pin_d2:%u,.pin_d1:%u,.pin_d0:%u,.pin_vsync:%u,.pin_href:%u,.pin_pclk:%u,.xclk_freq_hz:%d,.ledc_timer:%u,ledc_channel:%u,.pixel_format:%d,.frame_size:%d,.jpeg_quality:%d,.fb_count:%d,.fb_location%d,.grab_mode:%d,sccb_i2c_port:%d}", camera_config->pin_pwdn, camera_config->pin_reset, camera_config->pin_xclk, camera_config->pin_sccb_sda, camera_config->pin_sccb_scl, camera_config->pin_d7, camera_config->pin_d6, camera_config->pin_d5, camera_config->pin_d4, camera_config->pin_d3, camera_config->pin_d2, camera_config->pin_d1, camera_config->pin_d0, camera_config->pin_vsync, camera_config->pin_href, camera_config->pin_pclk, camera_config->xclk_freq_hz, camera_config->ledc_timer, camera_config->ledc_channel, camera_config->pixel_format, camera_config->frame_size, camera_config->jpeg_quality, camera_config->fb_count, camera_config->fb_location, camera_config->grab_mode, camera_config->sccb_i2c_port);
|
||||||
if (init_result == ESP_OK)
|
|
||||||
|
init_result_ = esp_camera_init(camera_config);
|
||||||
|
if (init_result_ == ESP_OK)
|
||||||
update_frame();
|
update_frame();
|
||||||
else
|
else
|
||||||
log_e("Camera initialization failed: 0x%02x", init_result);
|
log_e("Camera initialization failed: 0x%02x", init_result_);
|
||||||
|
|
||||||
return init_result;
|
return init_result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t micro_rtsp_camera::deinitialize()
|
esp_err_t micro_rtsp_camera::deinitialize()
|
||||||
{
|
{
|
||||||
return init_result == ESP_OK ? esp_camera_deinit() : ESP_OK;
|
return init_result_ == ESP_OK ? esp_camera_deinit() : ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void micro_rtsp_camera::update_frame()
|
void micro_rtsp_camera::update_frame()
|
||||||
{
|
{
|
||||||
if (fb)
|
if (fb_)
|
||||||
esp_camera_fb_return(fb);
|
esp_camera_fb_return(fb_);
|
||||||
|
|
||||||
fb = esp_camera_fb_get();
|
fb_ = esp_camera_fb_get();
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
micro_rtsp_requests::rtsp_command micro_rtsp_requests::parse_command(const std::string &request)
|
micro_rtsp_requests::rtsp_command micro_rtsp_requests::parse_command(const std::string &request)
|
||||||
{
|
{
|
||||||
log_i("parse_command");
|
log_v("request: %s", request.c_str());
|
||||||
// Check for RTSP commands: Option, Describe, Setup, Play, Teardown
|
// Check for RTSP commands: Option, Describe, Setup, Play, Teardown
|
||||||
|
|
||||||
if (std::regex_match(request, std::regex("^OPTION ", std::regex_constants::icase)))
|
if (std::regex_match(request, std::regex("^OPTION ", std::regex_constants::icase)))
|
||||||
@@ -31,7 +31,8 @@ micro_rtsp_requests::rtsp_command micro_rtsp_requests::parse_command(const std::
|
|||||||
|
|
||||||
bool micro_rtsp_requests::parse_client_port(const std::string &request)
|
bool micro_rtsp_requests::parse_client_port(const std::string &request)
|
||||||
{
|
{
|
||||||
log_i("parse_client_port");
|
log_v("request: %s", request.c_str());
|
||||||
|
|
||||||
std::regex regex("client_port=([0-9]+)", std::regex_constants::icase);
|
std::regex regex("client_port=([0-9]+)", std::regex_constants::icase);
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
if (!std::regex_match(request, match, regex))
|
if (!std::regex_match(request, match, regex))
|
||||||
@@ -46,7 +47,8 @@ bool micro_rtsp_requests::parse_client_port(const std::string &request)
|
|||||||
|
|
||||||
bool micro_rtsp_requests::parse_cseq(const std::string &request)
|
bool micro_rtsp_requests::parse_cseq(const std::string &request)
|
||||||
{
|
{
|
||||||
log_i("parse_cseq");
|
log_v("request: %s", request.c_str());
|
||||||
|
|
||||||
// CSeq: 2
|
// CSeq: 2
|
||||||
std::regex regex("CSeq: (\\d+)", std::regex_constants::icase);
|
std::regex regex("CSeq: (\\d+)", std::regex_constants::icase);
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
@@ -62,7 +64,8 @@ bool micro_rtsp_requests::parse_cseq(const std::string &request)
|
|||||||
|
|
||||||
bool micro_rtsp_requests::parse_stream_url(const std::string &request)
|
bool micro_rtsp_requests::parse_stream_url(const std::string &request)
|
||||||
{
|
{
|
||||||
log_i("parse_host_url");
|
log_v("request: %s", request.c_str());
|
||||||
|
|
||||||
// SETUP rtsp://192.168.10.93:1234/mjpeg/1 RTSP/1.0
|
// SETUP rtsp://192.168.10.93:1234/mjpeg/1 RTSP/1.0
|
||||||
std::regex regex("rtsp:\\/\\/([\\w.]+):(\\d+)\\/([\\/\\w]+)\\s+RTSP/1.0", std::regex_constants::icase);
|
std::regex regex("rtsp:\\/\\/([\\w.]+):(\\d+)\\/([\\/\\w]+)\\s+RTSP/1.0", std::regex_constants::icase);
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
@@ -88,6 +91,8 @@ std::string micro_rtsp_requests::date_header()
|
|||||||
|
|
||||||
std::string micro_rtsp_requests::rtsp_error(unsigned short code, const std::string &message)
|
std::string micro_rtsp_requests::rtsp_error(unsigned short code, const std::string &message)
|
||||||
{
|
{
|
||||||
|
log_v("code: %d, message: %s", code, message.c_str());
|
||||||
|
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "RTSP/1.0 " << code << " " << message << "\r\n"
|
oss << "RTSP/1.0 " << code << " " << message << "\r\n"
|
||||||
<< "CSeq: " << cseq_ << "\r\n"
|
<< "CSeq: " << cseq_ << "\r\n"
|
||||||
@@ -98,7 +103,8 @@ std::string micro_rtsp_requests::rtsp_error(unsigned short code, const std::stri
|
|||||||
|
|
||||||
std::string micro_rtsp_requests::handle_option(const std::string &request)
|
std::string micro_rtsp_requests::handle_option(const std::string &request)
|
||||||
{
|
{
|
||||||
log_i("handle_option");
|
log_v("request: %s", request.c_str());
|
||||||
|
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "RTSP/1.0 200 OK\r\n"
|
oss << "RTSP/1.0 200 OK\r\n"
|
||||||
<< "CSeq: " << cseq_ << "\r\n"
|
<< "CSeq: " << cseq_ << "\r\n"
|
||||||
@@ -111,7 +117,8 @@ std::string micro_rtsp_requests::handle_option(const std::string &request)
|
|||||||
|
|
||||||
std::string micro_rtsp_requests::handle_describe(const std::string &request)
|
std::string micro_rtsp_requests::handle_describe(const std::string &request)
|
||||||
{
|
{
|
||||||
log_i("handle_describe");
|
log_v("request: %s", request.c_str());
|
||||||
|
|
||||||
if (!parse_stream_url(request))
|
if (!parse_stream_url(request))
|
||||||
return rtsp_error(400, "Invalid Stream Name");
|
return rtsp_error(400, "Invalid Stream Name");
|
||||||
|
|
||||||
@@ -141,6 +148,8 @@ std::string micro_rtsp_requests::handle_describe(const std::string &request)
|
|||||||
|
|
||||||
std::string micro_rtsp_requests::handle_setup(const std::string &request)
|
std::string micro_rtsp_requests::handle_setup(const std::string &request)
|
||||||
{
|
{
|
||||||
|
log_v("request: %s", request.c_str());
|
||||||
|
|
||||||
tcp_transport_ = request.find("rtp/avp/tcp") != std::string::npos;
|
tcp_transport_ = request.find("rtp/avp/tcp") != std::string::npos;
|
||||||
|
|
||||||
if (!parse_client_port(request))
|
if (!parse_client_port(request))
|
||||||
@@ -164,6 +173,8 @@ std::string micro_rtsp_requests::handle_setup(const std::string &request)
|
|||||||
|
|
||||||
std::string micro_rtsp_requests::handle_play(const std::string &request)
|
std::string micro_rtsp_requests::handle_play(const std::string &request)
|
||||||
{
|
{
|
||||||
|
log_v("request: %s", request.c_str());
|
||||||
|
|
||||||
stream_active_ = true;
|
stream_active_ = true;
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "RTSP/1.0 200 OK\r\n"
|
oss << "RTSP/1.0 200 OK\r\n"
|
||||||
@@ -178,6 +189,8 @@ std::string micro_rtsp_requests::handle_play(const std::string &request)
|
|||||||
|
|
||||||
std::string micro_rtsp_requests::handle_teardown(const std::string &request)
|
std::string micro_rtsp_requests::handle_teardown(const std::string &request)
|
||||||
{
|
{
|
||||||
|
log_v("request: %s", request.c_str());
|
||||||
|
|
||||||
stream_stopped_ = true;
|
stream_stopped_ = true;
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "RTSP/1.0 200 OK\r\n"
|
oss << "RTSP/1.0 200 OK\r\n"
|
||||||
@@ -189,7 +202,8 @@ std::string micro_rtsp_requests::handle_teardown(const std::string &request)
|
|||||||
|
|
||||||
std::string micro_rtsp_requests::process_request(const std::string &request)
|
std::string micro_rtsp_requests::process_request(const std::string &request)
|
||||||
{
|
{
|
||||||
log_i("handle_request");
|
log_v("request: %s", request.c_str());
|
||||||
|
|
||||||
auto command = parse_command(request);
|
auto command = parse_command(request);
|
||||||
if (command != rtsp_command_unknown)
|
if (command != rtsp_command_unknown)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
micro_rtsp_server::micro_rtsp_server(micro_rtsp_camera &source, unsigned frame_interval /*= 100*/)
|
micro_rtsp_server::micro_rtsp_server(micro_rtsp_camera &source, unsigned frame_interval /*= 100*/)
|
||||||
: source_(source), streamer_(source.width(), source.height())
|
: source_(source), streamer_(source.width(), source.height())
|
||||||
{
|
{
|
||||||
log_i("starting RTSP server");
|
log_v("frame_interval:%d", frame_interval);
|
||||||
frame_interval_ = frame_interval;
|
frame_interval_ = frame_interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ void micro_rtsp_server::loop()
|
|||||||
|
|
||||||
if (next_check_client_ < now)
|
if (next_check_client_ < now)
|
||||||
{
|
{
|
||||||
|
log_v("Check for new client");
|
||||||
next_check_client_ = now + CHECK_CLIENT_INTERVAL;
|
next_check_client_ = now + CHECK_CLIENT_INTERVAL;
|
||||||
|
|
||||||
// Check if a client wants to connect
|
// Check if a client wants to connect
|
||||||
@@ -51,6 +52,7 @@ void micro_rtsp_server::loop()
|
|||||||
|
|
||||||
if (next_frame_update_ < now)
|
if (next_frame_update_ < now)
|
||||||
{
|
{
|
||||||
|
log_v("Stream frame");
|
||||||
next_frame_update_ = now + frame_interval_;
|
next_frame_update_ = now + frame_interval_;
|
||||||
|
|
||||||
auto ts = time(nullptr);
|
auto ts = time(nullptr);
|
||||||
@@ -68,8 +70,10 @@ void micro_rtsp_server::loop()
|
|||||||
auto packet = streamer_.create_jpg_packet(jpg.jpeg_data_start, jpg.jpeg_data_end, &jpg_scan_current, ts, jpg.quantization_table_luminance_->data, jpg.quantization_table_chrominance_->data);
|
auto packet = streamer_.create_jpg_packet(jpg.jpeg_data_start, jpg.jpeg_data_end, &jpg_scan_current, ts, jpg.quantization_table_luminance_->data, jpg.quantization_table_chrominance_->data);
|
||||||
for (auto client : clients_)
|
for (auto client : clients_)
|
||||||
{
|
{
|
||||||
;
|
log_v("Stream frame to client: 0x%08x", client);
|
||||||
// client->session->broadcastCurrentFrame(now);
|
// RTP over TCP encapsulates in a $
|
||||||
|
client.write((const uint8_t *)packet, packet->length + sizeof(micro_rtsp_streamer::rtp_over_tcp_hdr_t));
|
||||||
|
// TODO: UDP
|
||||||
}
|
}
|
||||||
free(packet);
|
free(packet);
|
||||||
}
|
}
|
||||||
@@ -79,18 +83,15 @@ void micro_rtsp_server::loop()
|
|||||||
micro_rtsp_server::rtsp_client::rtsp_client(const WiFiClient &wifi_client)
|
micro_rtsp_server::rtsp_client::rtsp_client(const WiFiClient &wifi_client)
|
||||||
: WiFiClient(wifi_client)
|
: WiFiClient(wifi_client)
|
||||||
{
|
{
|
||||||
log_i("rtsp_client");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
micro_rtsp_server::rtsp_client::~rtsp_client()
|
micro_rtsp_server::rtsp_client::~rtsp_client()
|
||||||
{
|
{
|
||||||
log_i("~rtsp_client");
|
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void micro_rtsp_server::rtsp_client::handle_request()
|
void micro_rtsp_server::rtsp_client::handle_request()
|
||||||
{
|
{
|
||||||
log_i("handle_request");
|
|
||||||
// Read if data available
|
// Read if data available
|
||||||
auto bytes_available = available();
|
auto bytes_available = available();
|
||||||
if (bytes_available > 0)
|
if (bytes_available > 0)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
micro_rtsp_streamer::micro_rtsp_streamer(const uint16_t width, const uint16_t height)
|
micro_rtsp_streamer::micro_rtsp_streamer(const uint16_t width, const uint16_t height)
|
||||||
{
|
{
|
||||||
|
log_v("width:%d, height:%d", width, height);
|
||||||
width_ = width;
|
width_ = width;
|
||||||
height_ = height;
|
height_ = height;
|
||||||
// Random number
|
// Random number
|
||||||
@@ -14,8 +15,10 @@ micro_rtsp_streamer::micro_rtsp_streamer(const uint16_t width, const uint16_t he
|
|||||||
sequence_number_ = 0;
|
sequence_number_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
rtp_over_tcp_hdr_t *micro_rtsp_streamer::create_jpg_packet(const uint8_t *jpg_scan, const uint8_t *jpg_scan_end, uint8_t **jpg_offset, const uint32_t timestamp, const uint8_t* quantization_table_luminance, const uint8_t* quantization_table_chrominance)
|
micro_rtsp_streamer::rtp_over_tcp_hdr_t *micro_rtsp_streamer::create_jpg_packet(const uint8_t *jpg_scan, const uint8_t *jpg_scan_end, uint8_t **jpg_offset, const uint32_t timestamp, const uint8_t *quantization_table_luminance, const uint8_t *quantization_table_chrominance)
|
||||||
{
|
{
|
||||||
|
log_v("jpg_scan:0x%08x, jpg_scan_end:0x%08x, jpg_offset:0x%08x, timestamp:%d, quantization_table_luminance:0x%08x, quantization_table_chrominance:0x%08x", jpg_scan, jpg_scan_end, jpg_offset, timestamp, quantization_table_luminance, quantization_table_chrominance);
|
||||||
|
|
||||||
// The MTU of wireless networks is 2,312 bytes. This size includes the packet headers.
|
// The MTU of wireless networks is 2,312 bytes. This size includes the packet headers.
|
||||||
const auto isFirstFragment = jpg_scan == *jpg_offset;
|
const auto isFirstFragment = jpg_scan == *jpg_offset;
|
||||||
const auto include_quantization_tables = isFirstFragment && quantization_table_luminance != nullptr && quantization_table_chrominance != nullptr;
|
const auto include_quantization_tables = isFirstFragment && quantization_table_luminance != nullptr && quantization_table_chrominance != nullptr;
|
||||||
@@ -31,44 +34,37 @@ rtp_over_tcp_hdr_t *micro_rtsp_streamer::create_jpg_packet(const uint8_t *jpg_sc
|
|||||||
const auto packet = (jpeg_packet_t *)calloc(1, packet_size);
|
const auto packet = (jpeg_packet_t *)calloc(1, packet_size);
|
||||||
|
|
||||||
// 4 bytes RTP over TCP header
|
// 4 bytes RTP over TCP header
|
||||||
assert(4 == sizeof(rtp_over_tcp_hdr_t));
|
|
||||||
packet->rtp_over_tcp_hdr.magic = '$'; // encapsulation
|
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; // number of multiplexed sub-channel on RTPS connection - here the RTP channel
|
||||||
packet->rtp_over_tcp_hdr.length = packet_size;
|
packet->rtp_over_tcp_hdr.length = packet_size;
|
||||||
log_v("rtp_over_tcp_hdr_t={.magic=%c,.channel=%i,.length=%i}", packet->rtp_over_tcp_hdr.magic, packet->rtp_over_tcp_hdr.channel, packet->rtp_over_tcp_hdr.length);
|
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);
|
||||||
|
|
||||||
// 12 bytes RTP header
|
// 12 bytes RTP header
|
||||||
assert(12 == sizeof(rtp_hdr_t));
|
|
||||||
packet->rtp_hdr.version = 2;
|
packet->rtp_hdr.version = 2;
|
||||||
packet->rtp_hdr.marker = isLastFragment;
|
packet->rtp_hdr.marker = isLastFragment;
|
||||||
packet->rtp_hdr.pt = RTP_PAYLOAD_JPG;
|
packet->rtp_hdr.pt = RTP_PAYLOAD_JPG;
|
||||||
packet->rtp_hdr.seq = sequence_number_;
|
packet->rtp_hdr.seq = sequence_number_;
|
||||||
packet->rtp_hdr.ts = timestamp;
|
packet->rtp_hdr.ts = timestamp;
|
||||||
packet->rtp_hdr.ssrc = ssrc_;
|
packet->rtp_hdr.ssrc = ssrc_;
|
||||||
log_v("rtp_hdr={.version:%i,.padding:%i,.extension:%i,.cc:%i,.marker:%i,.pt:%i,.seq:%i,.ts:%u,.ssrc:%u}", packet->rtp_hdr.version, packet->rtp_hdr.padding, packet->rtp_hdr.extension, packet->rtp_hdr.cc, packet->rtp_hdr.marker, packet->rtp_hdr.pt, packet->rtp_hdr.seq, packet->rtp_hdr.ts, packet->rtp_hdr.ssrc);
|
log_v("rtp_hdr={.version:%u,.padding:%u,.extension:%u,.cc:%u,.marker:%u,.pt:%u,.seq:%u,.ts:%u,.ssrc:%u}", packet->rtp_hdr.version, packet->rtp_hdr.padding, packet->rtp_hdr.extension, packet->rtp_hdr.cc, packet->rtp_hdr.marker, packet->rtp_hdr.pt, packet->rtp_hdr.seq, packet->rtp_hdr.ts, packet->rtp_hdr.ssrc);
|
||||||
|
|
||||||
// 8 bytes JPEG payload header
|
// 8 bytes JPEG payload header
|
||||||
assert(8 == sizeof(jpeg_hdr_t));
|
|
||||||
packet->jpeg_hdr.tspec = 0; // type-specific field
|
packet->jpeg_hdr.tspec = 0; // type-specific field
|
||||||
packet->jpeg_hdr.off = (uint32_t)(*jpg_offset - jpg_scan); // fragment byte offset (24 bits in jpg)
|
packet->jpeg_hdr.off = (uint32_t)(*jpg_offset - jpg_scan); // fragment byte offset (24 bits in jpg)
|
||||||
packet->jpeg_hdr.type = 0; // id of jpeg decoder params
|
packet->jpeg_hdr.type = 0; // id of jpeg decoder params
|
||||||
packet->jpeg_hdr.q = (uint8_t)(include_quantization_tables ? 0x80 : 0x5e); // quantization factor (or table id) 5eh=94d
|
packet->jpeg_hdr.q = (uint8_t)(include_quantization_tables ? 0x80 : 0x5e); // quantization factor (or table id) 5eh=94d
|
||||||
packet->jpeg_hdr.width = (uint8_t)(width_ >> 3); // frame width in 8 pixel blocks
|
packet->jpeg_hdr.width = (uint8_t)(width_ >> 3); // frame width in 8 pixel blocks
|
||||||
packet->jpeg_hdr.height = (uint8_t)(height_ >> 3); // frame height in 8 pixel blocks
|
packet->jpeg_hdr.height = (uint8_t)(height_ >> 3); // frame height in 8 pixel blocks
|
||||||
log_v("jpeg_hdr={.tspec:%i,.off:0x%6x,.type:0x2%x,.q:%i,.width:%i.height:%i}", packet->jpeg_hdr.tspec, packet->jpeg_hdr.off, packet->jpeg_hdr.type, packet->jpeg_hdr.q, packet->jpeg_hdr.width, packet->jpeg_hdr.height);
|
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);
|
||||||
|
|
||||||
// length so far should be 24 bytes
|
|
||||||
assert(24 == sizeof(jpeg_packet_t));
|
|
||||||
|
|
||||||
if (include_quantization_tables)
|
if (include_quantization_tables)
|
||||||
{
|
{
|
||||||
const auto packet_with_quantization = (jpeg_packet_with_quantization_t *)packet;
|
const auto packet_with_quantization = (jpeg_packet_with_quantization_t *)packet;
|
||||||
assert(4 == sizeof(jpeg_hdr_qtable_t));
|
|
||||||
// Only in first packet of the frame
|
// Only in first packet of the frame
|
||||||
packet_with_quantization->jpeg_hdr_qtable.mbz = 0;
|
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.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_luminance_table_length + jpeg_chrominance_table_length;
|
||||||
log_v("jpeg_hdr_qtable={.mbz:%i,.precision:%i,.length:%d}", packet_with_quantization->jpeg_hdr_qtable.mbz, packet_with_quantization->jpeg_hdr_qtable.precision, packet_with_quantization->jpeg_hdr_qtable.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_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_chrominance, quantization_table_chrominance, jpeg_chrominance_table_length);
|
||||||
// Copy JPG data
|
// Copy JPG data
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
#include <IotWebConf.h>
|
#include <IotWebConf.h>
|
||||||
|
|
||||||
#include <unity.h>
|
#include <unity.h>
|
||||||
|
|
||||||
#include <jpg.h>
|
#include <jpg.h>
|
||||||
|
#include <micro_rtsp_streamer.h>
|
||||||
|
|
||||||
static unsigned char jpeg[8468] = {
|
static unsigned char jpeg[8468] = {
|
||||||
// SOI
|
// SOI
|
||||||
@@ -65,21 +67,30 @@ void tearDown(void)
|
|||||||
|
|
||||||
void test_jpg_decode()
|
void test_jpg_decode()
|
||||||
{
|
{
|
||||||
jpg mr_jpeg;
|
jpg jpg;
|
||||||
TEST_ASSERT_TRUE(mr_jpeg.decode(reinterpret_cast<const uint8_t *>(jpeg), sizeof(jpeg)));
|
TEST_ASSERT_TRUE(jpg.decode(reinterpret_cast<const uint8_t *>(jpeg), sizeof(jpeg)));
|
||||||
TEST_ASSERT_EQUAL_INT32(sizeof(jpeg_data), mr_jpeg.jpeg_data_end - mr_jpeg.jpeg_data_start);
|
TEST_ASSERT_EQUAL_INT32(sizeof(jpeg_data), jpg.jpeg_data_end - jpg.jpeg_data_start);
|
||||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(jpeg_data, mr_jpeg.jpeg_data_start, sizeof(jpeg_data));
|
TEST_ASSERT_EQUAL_UINT8_ARRAY(jpeg_data, jpg.jpeg_data_start, sizeof(jpeg_data));
|
||||||
|
|
||||||
// Id is not stored
|
// Id is not stored
|
||||||
TEST_ASSERT_EQUAL_INT32(sizeof(jpg_section_dqt_t::id) + sizeof(jpeg_qtable0), mr_jpeg.quantization_table_luminance_->data_length());
|
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 *>(mr_jpeg.quantization_table_luminance_->data);
|
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_section_dqt_luminance->data, sizeof(jpeg_qtable0));
|
||||||
// Id is not stored
|
// Id is not stored
|
||||||
TEST_ASSERT_EQUAL_INT32(sizeof(jpg_section_dqt_t::id) + sizeof(jpeg_qtable1), mr_jpeg.quantization_table_chrominance_->data_length());
|
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 *>(mr_jpeg.quantization_table_chrominance_->data);
|
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_section_dqt_chrominance->data, sizeof(jpeg_qtable1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_struct_sizes()
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL(4, sizeof(micro_rtsp_streamer::rtp_over_tcp_hdr_t));
|
||||||
|
// TEST_ASSERT_EQUAL(12, sizeof(micro_rtsp_streamer::rtp_hdr_t));
|
||||||
|
// TEST_ASSERT_EQUAL(8, sizeof(micro_rtsp_streamer::jpeg_hdr_t));
|
||||||
|
// TEST_ASSERT_EQUAL(4, sizeof(micro_rtsp_streamer::jpeg_hdr_qtable_t));
|
||||||
|
// TEST_ASSERT_EQUAL(24, sizeof(micro_rtsp_streamer::jpeg_packet_t));
|
||||||
|
}
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
// Disable brownout
|
// Disable brownout
|
||||||
@@ -90,6 +101,7 @@ void setup()
|
|||||||
|
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
RUN_TEST(test_jpg_decode);
|
RUN_TEST(test_jpg_decode);
|
||||||
|
RUN_TEST(test_struct_sizes);
|
||||||
UNITY_END();
|
UNITY_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user