210 lines
4.7 KiB
C
210 lines
4.7 KiB
C
|
|
/*******************************************************************************
|
||
|
|
* JPEGDEC Wrapper Class
|
||
|
|
*
|
||
|
|
* Dependent libraries:
|
||
|
|
* JPEGDEC: https://github.com/bitbank2/JPEGDEC.git
|
||
|
|
******************************************************************************/
|
||
|
|
#ifndef _MJPEGCLASS_H_
|
||
|
|
#define _MJPEGCLASS_H_
|
||
|
|
|
||
|
|
#define READ_BUFFER_SIZE 1024
|
||
|
|
#define MAXOUTPUTSIZE (MAX_BUFFERED_PIXELS / 16 / 16)
|
||
|
|
|
||
|
|
/* Wio Terminal */
|
||
|
|
#if defined(ARDUINO_ARCH_SAMD) && defined(SEEED_GROVE_UI_WIRELESS)
|
||
|
|
#include <Seeed_FS.h>
|
||
|
|
#elif defined(ESP32) || defined(ESP8266)
|
||
|
|
#include <FS.h>
|
||
|
|
#else
|
||
|
|
#include <SD.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include <JPEGDEC.h>
|
||
|
|
|
||
|
|
class MjpegClass
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
bool setup(
|
||
|
|
Stream *input, uint8_t *mjpeg_buf, JPEG_DRAW_CALLBACK *pfnDraw, bool useBigEndian,
|
||
|
|
int x, int y, int widthLimit, int heightLimit)
|
||
|
|
{
|
||
|
|
_input = input;
|
||
|
|
_mjpeg_buf = mjpeg_buf;
|
||
|
|
_pfnDraw = pfnDraw;
|
||
|
|
_useBigEndian = useBigEndian;
|
||
|
|
_x = x;
|
||
|
|
_y = y;
|
||
|
|
_widthLimit = widthLimit;
|
||
|
|
_heightLimit = heightLimit;
|
||
|
|
_inputindex = 0;
|
||
|
|
|
||
|
|
if (!_read_buf)
|
||
|
|
{
|
||
|
|
_read_buf = (uint8_t *)malloc(READ_BUFFER_SIZE);
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool readMjpegBuf()
|
||
|
|
{
|
||
|
|
if (_inputindex == 0)
|
||
|
|
{
|
||
|
|
_buf_read = _input->readBytes(_read_buf, READ_BUFFER_SIZE);
|
||
|
|
_inputindex += _buf_read;
|
||
|
|
}
|
||
|
|
_mjpeg_buf_offset = 0;
|
||
|
|
int i = 0;
|
||
|
|
bool found_FFD8 = false;
|
||
|
|
while ((_buf_read > 0) && (!found_FFD8))
|
||
|
|
{
|
||
|
|
i = 0;
|
||
|
|
while ((i < _buf_read) && (!found_FFD8))
|
||
|
|
{
|
||
|
|
if ((_read_buf[i] == 0xFF) && (_read_buf[i + 1] == 0xD8)) // JPEG header
|
||
|
|
{
|
||
|
|
// Serial.printf("Found FFD8 at: %d.\n", i);
|
||
|
|
found_FFD8 = true;
|
||
|
|
}
|
||
|
|
++i;
|
||
|
|
}
|
||
|
|
if (found_FFD8)
|
||
|
|
{
|
||
|
|
--i;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_buf_read = _input->readBytes(_read_buf, READ_BUFFER_SIZE);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
uint8_t *_p = _read_buf + i;
|
||
|
|
_buf_read -= i;
|
||
|
|
bool found_FFD9 = false;
|
||
|
|
if (_buf_read > 0)
|
||
|
|
{
|
||
|
|
i = 3;
|
||
|
|
while ((_buf_read > 0) && (!found_FFD9))
|
||
|
|
{
|
||
|
|
if ((_mjpeg_buf_offset > 0) && (_mjpeg_buf[_mjpeg_buf_offset - 1] == 0xFF) && (_p[0] == 0xD9)) // JPEG trailer
|
||
|
|
{
|
||
|
|
// Serial.printf("Found FFD9 at: %d.\n", i);
|
||
|
|
found_FFD9 = true;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
while ((i < _buf_read) && (!found_FFD9))
|
||
|
|
{
|
||
|
|
if ((_p[i] == 0xFF) && (_p[i + 1] == 0xD9)) // JPEG trailer
|
||
|
|
{
|
||
|
|
found_FFD9 = true;
|
||
|
|
++i;
|
||
|
|
}
|
||
|
|
++i;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Serial.printf("i: %d\n", i);
|
||
|
|
memcpy(_mjpeg_buf + _mjpeg_buf_offset, _p, i);
|
||
|
|
_mjpeg_buf_offset += i;
|
||
|
|
size_t o = _buf_read - i;
|
||
|
|
if (o > 0)
|
||
|
|
{
|
||
|
|
// Serial.printf("o: %d\n", o);
|
||
|
|
memcpy(_read_buf, _p + i, o);
|
||
|
|
_buf_read = _input->readBytes(_read_buf + o, READ_BUFFER_SIZE - o);
|
||
|
|
_p = _read_buf;
|
||
|
|
_inputindex += _buf_read;
|
||
|
|
_buf_read += o;
|
||
|
|
// Serial.printf("_buf_read: %d\n", _buf_read);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_buf_read = _input->readBytes(_read_buf, READ_BUFFER_SIZE);
|
||
|
|
_p = _read_buf;
|
||
|
|
_inputindex += _buf_read;
|
||
|
|
}
|
||
|
|
i = 0;
|
||
|
|
}
|
||
|
|
if (found_FFD9)
|
||
|
|
{
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool drawJpg()
|
||
|
|
{
|
||
|
|
_remain = _mjpeg_buf_offset;
|
||
|
|
_jpeg.openRAM(_mjpeg_buf, _remain, _pfnDraw);
|
||
|
|
if (_scale == -1)
|
||
|
|
{
|
||
|
|
// scale to fit height
|
||
|
|
int iMaxMCUs;
|
||
|
|
int w = _jpeg.getWidth();
|
||
|
|
int h = _jpeg.getHeight();
|
||
|
|
float ratio = (float)h / _heightLimit;
|
||
|
|
if (ratio <= 1)
|
||
|
|
{
|
||
|
|
_scale = 0;
|
||
|
|
iMaxMCUs = _widthLimit / 16;
|
||
|
|
}
|
||
|
|
else if (ratio <= 2)
|
||
|
|
{
|
||
|
|
_scale = JPEG_SCALE_HALF;
|
||
|
|
iMaxMCUs = _widthLimit / 8;
|
||
|
|
w /= 2;
|
||
|
|
h /= 2;
|
||
|
|
}
|
||
|
|
else if (ratio <= 4)
|
||
|
|
{
|
||
|
|
_scale = JPEG_SCALE_QUARTER;
|
||
|
|
iMaxMCUs = _widthLimit / 4;
|
||
|
|
w /= 4;
|
||
|
|
h /= 4;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_scale = JPEG_SCALE_EIGHTH;
|
||
|
|
iMaxMCUs = _widthLimit / 2;
|
||
|
|
w /= 8;
|
||
|
|
h /= 8;
|
||
|
|
}
|
||
|
|
_jpeg.setMaxOutputSize(iMaxMCUs);
|
||
|
|
_x = (w > _widthLimit) ? 0 : ((_widthLimit - w) / 2);
|
||
|
|
_y = (_heightLimit - h) / 2;
|
||
|
|
}
|
||
|
|
if (_useBigEndian)
|
||
|
|
{
|
||
|
|
_jpeg.setPixelType(RGB565_BIG_ENDIAN);
|
||
|
|
}
|
||
|
|
_jpeg.decode(_x, _y, _scale);
|
||
|
|
_jpeg.close();
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
Stream *_input;
|
||
|
|
uint8_t *_mjpeg_buf;
|
||
|
|
JPEG_DRAW_CALLBACK *_pfnDraw;
|
||
|
|
bool _useBigEndian;
|
||
|
|
int _x;
|
||
|
|
int _y;
|
||
|
|
int _widthLimit;
|
||
|
|
int _heightLimit;
|
||
|
|
|
||
|
|
uint8_t *_read_buf;
|
||
|
|
int32_t _mjpeg_buf_offset = 0;
|
||
|
|
|
||
|
|
JPEGDEC _jpeg;
|
||
|
|
int _scale = -1;
|
||
|
|
|
||
|
|
int32_t _inputindex = 0;
|
||
|
|
int32_t _buf_read;
|
||
|
|
int32_t _remain = 0;
|
||
|
|
};
|
||
|
|
|
||
|
|
#endif // _MJPEGCLASS_H_
|