Use TouchGFX

This commit is contained in:
2023-03-06 21:21:00 +01:00
parent 2cadbff590
commit 8a2bdc347c
1214 changed files with 358250 additions and 87 deletions

View File

@ -0,0 +1,9 @@
/*********************************************************************************/
/********** THIS FILE IS GENERATED BY TOUCHGFX DESIGNER, DO NOT MODIFY ***********/
/*********************************************************************************/
#include <touchgfx/hal/HAL.hpp>
#include <touchgfx/lcd/LCD.hpp>
void setupSimulator(int argc, char** argv, touchgfx::HAL& hal);
touchgfx::LCD& setupLCD();

View File

@ -0,0 +1,354 @@
/*********************************************************************************/
/********** THIS FILE IS GENERATED BY TOUCHGFX DESIGNER, DO NOT MODIFY ***********/
/*********************************************************************************/
#ifndef TOUCHGFX_DIRECTFRAMEBUFFERVIDEOCONTROLLER_HPP
#define TOUCHGFX_DIRECTFRAMEBUFFERVIDEOCONTROLLER_HPP
#include <touchgfx/widgets/VideoWidget.hpp>
#include <simulator/video/MJPEGDecoder.hpp>
#include <string.h>
/**
* Strategy:
* Decode directly into the framebuffer in draw.
* Tick will decide if we are going to a new frame.
*/
template <uint32_t no_streams, touchgfx::Bitmap::BitmapFormat output_format>
class DirectFrameBufferVideoController : public touchgfx::VideoController
{
public:
DirectFrameBufferVideoController()
: VideoController(), allowSkipFrames(true)
{
assert((no_streams > 0) && "Video: Number of streams zero!");
// Clear arrays
memset(mjpegDecoders, 0, sizeof(mjpegDecoders));
}
Handle registerVideoWidget(touchgfx::VideoWidget& widget)
{
// Find stream handle for Widget
Handle handle = getFreeHandle();
streams[handle].isActive = true;
//Set Widget buffer format and address
widget.setVideoBufferFormat(output_format, 0, 0);
widget.setVideoBuffer((uint8_t*)0);
return handle;
}
void unregisterVideoWidget(const Handle handle)
{
streams[handle].isActive = false;
}
void setFrameRate(const Handle handle, uint32_t ui_frames, uint32_t video_frames)
{
assert(handle < no_streams);
Stream& stream = streams[handle];
// Reset counters
stream.frameCount = 0;
stream.tickCount = 0;
// Save requested frame rate ratio
stream.frame_rate_ticks = ui_frames;
stream.frame_rate_video = video_frames;
}
void setVideoData(const Handle handle, const uint8_t* movie, const uint32_t length)
{
assert(handle < no_streams);
// Reset decoder to first frame
mjpegDecoders[handle]->setVideoData(movie, length);
// Lower flag to show the first frame
Stream& stream = streams[handle];
stream.frameNumber = mjpegDecoders[handle]->getCurrentFrameNumber();
stream.doDecodeNextFrame = false;
// Stop playing
setCommand(handle, PAUSE, 0);
}
void setVideoData(const Handle handle, touchgfx::VideoDataReader& reader)
{
assert(handle < no_streams);
// Reset decoder to first frame
mjpegDecoders[handle]->setVideoData(reader);
// Lower flag to show the first frame
Stream& stream = streams[handle];
stream.frameNumber = mjpegDecoders[handle]->getCurrentFrameNumber();
stream.doDecodeNextFrame = false;
// Stop playing
setCommand(handle, PAUSE, 0);
}
void setCommand(const Handle handle, Command cmd, uint32_t param)
{
assert(handle < no_streams);
Stream& stream = streams[handle];
switch (cmd)
{
case PLAY:
// Cannot Play without movie
if (mjpegDecoders[handle]->hasVideo())
{
stream.isPlaying = true;
stream.isShowingOneFrame = false;
// Reset counters
stream.frameCount = 0;
stream.tickCount = 0;
// If non-repeating video stopped at the end, kick to next frame
if (!stream.repeat)
{
MJPEGDecoder* const decoder = mjpegDecoders[handle];
if (decoder->getCurrentFrameNumber() == decoder->getNumberOfFrames())
{
decoder->gotoNextFrame();
}
}
}
break;
case PAUSE:
stream.isPlaying = false;
stream.isShowingOneFrame = false;
break;
case SEEK:
stream.seek_to_frame = param;
// Reset counters
stream.frameCount = 0;
stream.tickCount = 0;
break;
case SHOW:
stream.seek_to_frame = param;
stream.isShowingOneFrame = true;
stream.doDecodeNextFrame = true;
// Reset counters
stream.frameCount = 0;
stream.tickCount = 0;
break;
case STOP:
stream.isPlaying = false;
stream.isShowingOneFrame = false;
stream.seek_to_frame = 1;
// Reset counters
stream.frameCount = 0;
stream.tickCount = 0;
break;
case SET_REPEAT:
stream.repeat = (param > 0);
break;
}
}
bool updateFrame(const Handle handle, touchgfx::VideoWidget& widget)
{
assert(handle < no_streams);
Stream& stream = streams[handle];
bool hasMoreFrames = true;
if (stream.isPlaying || stream.isShowingOneFrame)
{
// Increase tickCount
stream.tickCount+=HAL::getInstance()->getLCDRefreshCount();
// Lower flag
stream.isShowingOneFrame = false;
if (stream.doDecodeNextFrame)
{
MJPEGDecoder* const decoder = mjpegDecoders[handle];
// Invalidate to get widget redrawn
widget.invalidate();
// Seek or increment video frame
if (stream.seek_to_frame > 0)
{
decoder->gotoFrame(stream.seek_to_frame);
hasMoreFrames = (stream.seek_to_frame < decoder->getNumberOfFrames());
stream.seek_to_frame = 0;
}
else
{
if (stream.skip_frames > 0)
{
decoder->gotoFrame(decoder->getCurrentFrameNumber() + stream.skip_frames);
stream.frameCount += stream.skip_frames;
stream.skip_frames = 0;
}
if (stream.repeat)
{
hasMoreFrames = decoder->gotoNextFrame();
}
else
{
if (decoder->getCurrentFrameNumber() < decoder->getNumberOfFrames())
{
hasMoreFrames = decoder->gotoNextFrame();
}
else
{
stream.isPlaying = false;
hasMoreFrames = false;
}
}
}
stream.frameNumber = decoder->getCurrentFrameNumber();
stream.frameCount++;
}
// Save decode status for next frame
stream.doDecodeNextFrame = decodeForNextTick(stream);
}
return hasMoreFrames;
}
void draw(const Handle handle, const touchgfx::Rect& invalidatedArea, const touchgfx::VideoWidget& widget)
{
assert(handle < no_streams);
if (output_format != Bitmap::RGB565 && output_format != Bitmap::RGB888 && output_format != Bitmap::ARGB8888)
{
return;
}
if (mjpegDecoders[handle]->hasVideo())
{
uint8_t* wbuf = (uint8_t*)touchgfx::HAL::getInstance()->lockFrameBuffer();
const touchgfx::Rect& absolute = widget.getAbsoluteRect();
// Get frame buffer pointer to upper left of widget in framebuffer coordinates
switch(output_format)
{
case Bitmap::RGB565:
wbuf += (absolute.x + absolute.y * touchgfx::HAL::FRAME_BUFFER_WIDTH) * 2;
break;
case Bitmap::RGB888:
wbuf += (absolute.x + absolute.y * touchgfx::HAL::FRAME_BUFFER_WIDTH) * 3;
break;
case Bitmap::ARGB8888:
wbuf += (absolute.x + absolute.y * touchgfx::HAL::FRAME_BUFFER_WIDTH) * 4;
break;
default:
break;
}
// Decode relevant part of the frame to the framebuffer
mjpegDecoders[handle]->decodeFrame(invalidatedArea, wbuf, touchgfx::HAL::FRAME_BUFFER_WIDTH);
// Release frame buffer
touchgfx::HAL::getInstance()->unlockFrameBuffer();
}
}
void addDecoder(MJPEGDecoder& decoder, uint32_t index)
{
assert(index < no_streams);
mjpegDecoders[index] = &decoder;
}
uint32_t getCurrentFrameNumber(const Handle handle)
{
assert(handle < no_streams);
Stream& stream = streams[handle];
return stream.frameNumber;
}
void getVideoInformation(const Handle handle, touchgfx::VideoInformation* data)
{
assert(handle < no_streams);
mjpegDecoders[handle]->getVideoInfo(data);
}
bool getIsPlaying(const Handle handle)
{
assert(handle < no_streams);
Stream& stream = streams[handle];
return stream.isPlaying;
}
void setFrameRateCompensation(const bool allow)
{
allowSkipFrames = allow;
}
private:
class Stream
{
public:
Stream()
: frameCount(0), frameNumber(0), tickCount(0),
frame_rate_video(0), frame_rate_ticks(0),
seek_to_frame(0),
isActive(false), isPlaying(false), isShowingOneFrame(false), repeat(true),
doDecodeNextFrame(false)
{
}
uint32_t frameCount; // Video frames decoded since play
uint32_t frameNumber; // Video frame showed number
uint32_t tickCount; // UI frames since play
uint32_t frame_rate_video; // Ratio of frames wanted counter
uint32_t frame_rate_ticks; // Ratio of frames wanted divider
uint32_t seek_to_frame; // Requested next frame number
uint32_t skip_frames; // Number of frames to skip to keep frame rate
bool isActive;
bool isPlaying;
bool isShowingOneFrame;
bool repeat;
bool doDecodeNextFrame; // High if we should go to next frame in next tick
};
MJPEGDecoder* mjpegDecoders[no_streams];
Stream streams[no_streams];
bool allowSkipFrames;
/**
* Return true, if new video frame should be decoded for the next tick (keep video decode framerate low)
*/
bool decodeForNextTick(Stream& stream)
{
// Running in UI thread
// Compare tickCount/frameNumber to frame_rate_ticks/frame_rate_video
if ((stream.tickCount * stream.frame_rate_video) > (stream.frame_rate_ticks * stream.frameCount))
{
if (allowSkipFrames)
{
stream.skip_frames = (stream.tickCount * stream.frame_rate_video - stream.frame_rate_ticks * stream.frameCount) / stream.frame_rate_ticks;
if (stream.skip_frames > 0)
{
stream.skip_frames--;
}
}
return true;
}
return false;
}
Handle getFreeHandle()
{
for (uint32_t i = 0; i < no_streams; i++)
{
if (streams[i].isActive == false)
{
return static_cast<VideoController::Handle>(i);
}
}
assert(0 && "Unable to find free video stream handle!");
return static_cast<VideoController::Handle>(0);
}
};
#endif // TOUCHGFX_DIRECTFRAMEBUFFERVIDEOCONTROLLER_HPP

View File

@ -0,0 +1,75 @@
/*********************************************************************************/
/********** THIS FILE IS GENERATED BY TOUCHGFX DESIGNER, DO NOT MODIFY ***********/
/*********************************************************************************/
#ifndef TOUCHGFX_MJPEGDECODER_HPP
#define TOUCHGFX_MJPEGDECODER_HPP
#include <touchgfx/hal/Types.hpp>
#include <touchgfx/hal/VideoController.hpp>
class MJPEGDecoder
{
public:
virtual ~MJPEGDecoder()
{
}
//Set video data for the decoder
virtual void setVideoData(const uint8_t* movie, const uint32_t length) = 0;
//Set video data for the decoder
virtual void setVideoData(touchgfx::VideoDataReader& reader) = 0;
/**
* Check if MJPEGDecoder has a video.
*
* @return Returns true if the MJPEGDecoder has a video.
*/
virtual bool hasVideo() = 0;
//Increment position to next frame and decode and convert to RGB
virtual bool decodeNextFrame(uint8_t* buffer, uint16_t width, uint16_t height, uint32_t stride) = 0;
//Increment position to next frame and decode. Used with decodeFrame.
virtual bool gotoNextFrame() = 0;
//Decode part of the current frame, framebuffer is locked, area is drawn relative to frameBuffer
virtual bool decodeFrame(const touchgfx::Rect& area, uint8_t* frameBuffer, uint32_t framebufferStride) = 0;
//Decode thumbnail, assumes buffer stride is width
virtual bool decodeThumbnail(uint32_t frameno, uint8_t* buffer, uint16_t width, uint16_t height) = 0;
//Set current frame number
virtual void gotoFrame(uint32_t frameno) = 0;
//Get current frame number
virtual uint32_t getCurrentFrameNumber() const = 0;
//Get number of frames in video
virtual uint32_t getNumberOfFrames() = 0;
//Read video information
virtual void getVideoInfo(touchgfx::VideoInformation* data) = 0;
enum AVIErrors
{
AVI_NO_ERROR,
AVI_NO_BUFFERS,
AVI_NO_FILE,
AVI_ERROR_NOT_RIFF,
AVI_ERROR_AVI_HEADER_NOT_FOUND,
AVI_ERROR_AVI_LIST_NOT_FOUND,
AVI_ERROR_AVI_HDRL_NOT_FOUND,
AVI_ERROR_AVI_AVIH_NOT_FOUND,
AVI_ERROR_AVI_HEADER_TO_SHORT, //not full header provided
AVI_ERROR_FILE_BUFFER_TO_SMALL,
AVI_ERROR_MOVI_NOT_FOUND,
AVI_ERROR_IDX1_NOT_FOUND,
AVI_ERROR_FRAMENO_TO_HIGH,
AVI_ERROR_EOF_REACHED
};
AVIErrors virtual getLastError() = 0;
};
#endif // TOUCHGFX_MJPEGDECODER_HPP

View File

@ -0,0 +1,73 @@
/*********************************************************************************/
/********** THIS FILE IS GENERATED BY TOUCHGFX DESIGNER, DO NOT MODIFY ***********/
/*********************************************************************************/
#ifndef TOUCHGFX_SOFTWAREMJPEGDECODER_HPP
#define TOUCHGFX_SOFTWAREMJPEGDECODER_HPP
#include <simulator/video/MJPEGDecoder.hpp>
class SoftwareMJPEGDecoder : public MJPEGDecoder
{
public:
SoftwareMJPEGDecoder(uint8_t* linebuffer);
virtual void setVideoData(const uint8_t* movie, const uint32_t length);
virtual void setVideoData(touchgfx::VideoDataReader& reader);
virtual bool hasVideo();
virtual bool decodeNextFrame(uint8_t* frameBuffer, uint16_t width, uint16_t height, uint32_t framebuffer_width);
virtual bool gotoNextFrame();
virtual bool decodeFrame(const touchgfx::Rect& area, uint8_t* frameBuffer, uint32_t framebuffer_width);
virtual bool decodeThumbnail(uint32_t frameno, uint8_t* buffer, uint16_t width, uint16_t height);
virtual void gotoFrame(uint32_t frameno);
virtual uint32_t getCurrentFrameNumber() const
{
return frameNumber;
}
virtual uint32_t getNumberOfFrames();
virtual void getVideoInfo(touchgfx::VideoInformation* data);
void setAVIFileBuffer(uint8_t* buffer, uint32_t size)
{
aviBuffer = buffer, aviBufferLength = size;
}
AVIErrors getLastError()
{
return lastError;
}
private:
void readVideoHeader();
void decodeMJPEGFrame(const uint8_t* const mjpgdata, const uint32_t length, uint8_t* buffer, uint16_t width, uint16_t height, uint32_t stride);
int compare(const uint32_t offset, const char* str, uint32_t num);
uint32_t getU32(const uint32_t offset);
uint32_t getU16(const uint32_t offset);
const uint8_t* readData(uint32_t offset, uint32_t length);
touchgfx::VideoInformation videoInfo;
uint32_t frameNumber;
uint32_t currentMovieOffset;
uint32_t indexOffset;
uint32_t firstFrameOffset;
uint32_t lastFrameEnd;
uint32_t movieLength;
const uint8_t* movieData;
touchgfx::VideoDataReader* reader;
uint8_t* lineBuffer;
uint8_t* aviBuffer;
uint32_t aviBufferLength;
uint32_t aviBufferStartOffset;
AVIErrors lastError;
};
#endif // TOUCHGFX_SOFTWAREMJPEGDECODER_HPP